Перенести все админские эндпоинты на порт 8445 и добавить отдельную авторизацию для админов. Часть 2. Final #3
This commit is contained in:
@@ -46,7 +46,7 @@ test_get_report() ->
|
||||
target_type = <<"event">>,
|
||||
target_id = <<"e1">>,
|
||||
reason = <<"spam">>,
|
||||
status = <<"new">>,
|
||||
status = pending,
|
||||
created_at = {{2026,4,26},{12,0,0}},
|
||||
resolved_at = undefined
|
||||
},
|
||||
@@ -55,7 +55,7 @@ test_get_report() ->
|
||||
{ok, _, _} = admin_handler_report_by_id:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
#{<<"id">> := <<"r1">>, <<"status">> := <<"new">>} = jsx:decode(RespBody, [return_maps]).
|
||||
#{<<"id">> := <<"r1">>, <<"status">> := <<"pending">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% GET – не найдено
|
||||
test_get_report_not_found() ->
|
||||
@@ -94,9 +94,9 @@ test_update_report() ->
|
||||
fun(id, _) -> <<"r1">> end),
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"reviewed">>}), Req} end),
|
||||
Updated = #report{id = <<"r1">>, status = <<"reviewed">>},
|
||||
Updated = #report{id = <<"r1">>, status = reviewed},
|
||||
ok = meck:expect(core_report, update_status,
|
||||
fun(<<"r1">>, <<"reviewed">>) -> {ok, Updated} end),
|
||||
fun(<<"r1">>, reviewed, <<"adm1">>) -> {ok, Updated} end),
|
||||
{ok, _, _} = admin_handler_report_by_id:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
@@ -115,7 +115,7 @@ test_update_report_not_found() ->
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"reviewed">>}), Req} end),
|
||||
ok = meck:expect(core_report, update_status,
|
||||
fun(_, _) -> {error, not_found} end),
|
||||
fun(<<"r99">>, reviewed, <<"adm1">>) -> {error, not_found} end),
|
||||
{ok, _, _} = admin_handler_report_by_id:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(404, Status).
|
||||
|
||||
@@ -29,6 +29,7 @@ admin_reports_test_() ->
|
||||
{"POST /admin/reports – method not allowed", fun test_wrong_method/0}
|
||||
]}.
|
||||
|
||||
%% GET – успех
|
||||
test_list_reports() ->
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end),
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
@@ -42,17 +43,18 @@ test_list_reports() ->
|
||||
target_type = <<"event">>,
|
||||
target_id = <<"e1">>,
|
||||
reason = <<"spam">>,
|
||||
status = <<"new">>,
|
||||
status = pending,
|
||||
created_at = {{2026,4,26},{12,0,0}},
|
||||
resolved_at = undefined
|
||||
},
|
||||
ok = meck:expect(core_report, list_reports, fun() -> [Report] end),
|
||||
% list_all возвращает {ok, List}
|
||||
ok = meck:expect(core_report, list_all, fun() -> {ok, [Report]} end),
|
||||
{ok, _, _} = admin_handler_reports:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
[#{<<"id">> := <<"r1">>, <<"target_type">> := <<"event">>, <<"status">> := <<"new">>}]
|
||||
= jsx:decode(RespBody, [return_maps]).
|
||||
[#{<<"id">> := <<"r1">>, <<"status">> := <<"pending">>}] = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% GET – запрещён
|
||||
test_list_reports_forbidden() ->
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end),
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
@@ -62,6 +64,7 @@ test_list_reports_forbidden() ->
|
||||
?assertEqual(403, Status),
|
||||
#{<<"error">> := <<"Admin access required">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% PUT – успех
|
||||
test_update_report() ->
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end),
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
@@ -73,14 +76,16 @@ test_update_report() ->
|
||||
fun(id, _) -> <<"r1">> end),
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"reviewed">>}), Req} end),
|
||||
Updated = #report{id = <<"r1">>, status = <<"reviewed">>},
|
||||
Updated = #report{id = <<"r1">>, status = reviewed},
|
||||
% обработчик передаёт бинарный статус, поэтому мок ожидает строку
|
||||
ok = meck:expect(core_report, update_status,
|
||||
fun(<<"r1">>, <<"reviewed">>) -> {ok, Updated} end),
|
||||
fun(<<"r1">>, <<"reviewed">>, <<"adm1">>) -> {ok, Updated} end),
|
||||
{ok, _, _} = admin_handler_reports:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
#{<<"status">> := <<"reviewed">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% PUT – невалидный JSON
|
||||
test_update_report_bad_json() ->
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end),
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
@@ -93,9 +98,10 @@ test_update_report_bad_json() ->
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, <<"bad json">>, Req} end),
|
||||
{ok, _, _} = admin_handler_reports:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply), %% исправлено: четыре элемента
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(400, Status).
|
||||
|
||||
%% PUT – не найдено
|
||||
test_update_report_not_found() ->
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end),
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
@@ -108,11 +114,12 @@ test_update_report_not_found() ->
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"reviewed">>}), Req} end),
|
||||
ok = meck:expect(core_report, update_status,
|
||||
fun(_, _) -> {error, not_found} end),
|
||||
fun(<<"r99">>, <<"reviewed">>, <<"adm1">>) -> {error, not_found} end),
|
||||
{ok, _, _} = admin_handler_reports:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply), %% исправлено: четыре элемента
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(404, Status).
|
||||
|
||||
%% Неправильный метод
|
||||
test_wrong_method() ->
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"POST">> end),
|
||||
{ok, _, _} = admin_handler_reports:init(req, []),
|
||||
|
||||
@@ -21,182 +21,210 @@ cleanup(_) ->
|
||||
|
||||
admin_tickets_test_() ->
|
||||
{setup, fun setup/0, fun cleanup/1, [
|
||||
{"GET /admin/tickets – success", fun test_list/0},
|
||||
{"GET /admin/tickets – forbidden", fun test_list_forbidden/0},
|
||||
{"POST /admin/tickets – success", fun test_create/0},
|
||||
{"POST /admin/tickets – missing error_message", fun test_create_missing/0},
|
||||
{"GET /admin/tickets/:id – success", fun test_get/0},
|
||||
{"GET /admin/tickets/:id – not found", fun test_get_not_found/0},
|
||||
{"PUT /admin/tickets/:id – success", fun test_update/0},
|
||||
{"PUT /admin/tickets/:id – not found", fun test_update_not_found/0},
|
||||
{"DELETE /admin/tickets/:id – success", fun test_delete/0},
|
||||
{"DELETE /admin/tickets/:id – not found", fun test_delete_not_found/0},
|
||||
{"PATCH /admin/tickets – method not allowed", fun test_wrong_method/0}
|
||||
{"GET /admin/tickets – success", fun test_list/0},
|
||||
{"GET /admin/tickets – forbidden", fun test_list_forbidden/0},
|
||||
{"POST /admin/tickets – success", fun test_create/0},
|
||||
{"POST /admin/tickets – missing error_message", fun test_create_missing/0},
|
||||
{"GET /admin/tickets/:id – success", fun test_get/0},
|
||||
{"GET /admin/tickets/:id – not found", fun test_get_not_found/0},
|
||||
{"PUT /admin/tickets/:id – success", fun test_update/0},
|
||||
{"PUT /admin/tickets/:id – not found", fun test_update_not_found/0},
|
||||
{"DELETE /admin/tickets/:id – success", fun test_delete/0},
|
||||
{"DELETE /admin/tickets/:id – not found", fun test_delete_not_found/0},
|
||||
{"PATCH /admin/tickets – method not allowed", fun test_wrong_method/0}
|
||||
]}.
|
||||
|
||||
%% GET – список тикетов (успех)
|
||||
test_list() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> undefined end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> undefined end), % для маршрута без id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
Ticket = #ticket{
|
||||
id = <<"t1">>,
|
||||
error_hash = <<"abc123">>,
|
||||
error_message = <<"Ooops">>,
|
||||
stacktrace = <<"trace">>,
|
||||
error_hash = <<"hash1">>,
|
||||
error_message = <<"Error message">>,
|
||||
stacktrace = <<"stack">>,
|
||||
context = <<"ctx">>,
|
||||
count = 3,
|
||||
first_seen = {{2026,4,27},{12,0,0}},
|
||||
last_seen = {{2026,4,27},{13,0,0}},
|
||||
count = 1,
|
||||
first_seen = {{2026,4,28},{12,0,0}},
|
||||
last_seen = {{2026,4,28},{12,0,0}},
|
||||
status = open,
|
||||
assigned_to = <<"adm2">>,
|
||||
assigned_to = undefined,
|
||||
resolution_note = undefined
|
||||
},
|
||||
ok = meck:expect(core_ticket, list_tickets, fun() -> [Ticket] end),
|
||||
ok = meck:expect(core_ticket, list_all, fun() -> [Ticket] end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
[#{<<"id">> := <<"t1">>, <<"error_message">> := <<"Ooops">>, <<"status">> := <<"open">>}] =
|
||||
[#{<<"id">> := <<"t1">>, <<"error_message">> := <<"Error message">>}] =
|
||||
jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% GET – запрещён
|
||||
test_list_forbidden() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> undefined end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {error, 403, <<"Admin access required">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> undefined end), % для маршрута без id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {error, 403, <<"Admin access required">>, Req} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(403, Status).
|
||||
|
||||
%% POST – создание тикета
|
||||
test_create() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> undefined end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"POST">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> undefined end), % для маршрута без id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
BodyMap = #{<<"error_message">> => <<"New bug">>, <<"stacktrace">> => <<"trace">>},
|
||||
ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(BodyMap), Req} end),
|
||||
Created = #ticket{
|
||||
id = <<"t_new">>,
|
||||
error_hash = <<"hash">>,
|
||||
error_message = <<"New bug">>,
|
||||
stacktrace = <<"trace">>,
|
||||
context = <<>>,
|
||||
count = 1,
|
||||
first_seen = {{2026,4,27},{14,0,0}},
|
||||
last_seen = {{2026,4,27},{14,0,0}},
|
||||
status = open,
|
||||
assigned_to = undefined,
|
||||
resolution_note = undefined
|
||||
},
|
||||
ok = meck:expect(core_ticket, create_ticket, fun(Data) ->
|
||||
true = maps:is_key(<<"error_message">>, Data),
|
||||
{ok, Created}
|
||||
end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
BodyMap = #{<<"error_message">> => <<"Bug">>, <<"stacktrace">> => <<"trace">>},
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(BodyMap), Req} end),
|
||||
Created = #ticket{id = <<"t_new">>, error_message = <<"Bug">>, status = open},
|
||||
ok = meck:expect(core_ticket, create_ticket, fun(_) -> {ok, Created} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(201, Status),
|
||||
#{<<"error_message">> := <<"New bug">>, <<"status">> := <<"open">>} = jsx:decode(RespBody, [return_maps]).
|
||||
#{<<"error_message">> := <<"Bug">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% POST – отсутствует поле error_message
|
||||
test_create_missing() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> undefined end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"POST">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> undefined end), % для маршрута без id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(#{<<"desc">> => <<"no msg">>}), Req} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"title">> => <<"No msg">>}), Req} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(400, Status).
|
||||
|
||||
%% GET – один тикет по ID
|
||||
test_get() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"t1">> end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> <<"t1">> end), % для маршрута с id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
Ticket = #ticket{
|
||||
id = <<"t1">>,
|
||||
error_hash = <<"abc">>,
|
||||
error_message = <<"msg">>,
|
||||
stacktrace = <<>>,
|
||||
context = <<>>,
|
||||
count = 1,
|
||||
first_seen = {{2026,4,27},{12,0,0}},
|
||||
last_seen = {{2026,4,27},{12,0,0}},
|
||||
status = open,
|
||||
assigned_to = undefined,
|
||||
resolution_note = undefined
|
||||
},
|
||||
ok = meck:expect(core_ticket, get_by_id, fun(<<"t1">>) -> {ok, Ticket} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
Ticket = #ticket{id = <<"t1">>, error_message = <<"Test">>, status = open},
|
||||
ok = meck:expect(core_ticket, get_by_id,
|
||||
fun(<<"t1">>) -> {ok, Ticket} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
#{<<"id">> := <<"t1">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% GET – тикет не найден
|
||||
test_get_not_found() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"t99">> end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> <<"t99">> end), % для маршрута с id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_ticket, get_by_id, fun(_) -> {error, not_found} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_ticket, get_by_id,
|
||||
fun(_) -> {error, not_found} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(404, Status).
|
||||
|
||||
%% PUT – обновление тикета
|
||||
test_update() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"t1">> end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> <<"t1">> end), % для маршрута с id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"closed">>}), Req} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"closed">>}), Req} end),
|
||||
Updated = #ticket{id = <<"t1">>, status = closed},
|
||||
ok = meck:expect(core_ticket, update_ticket, fun(<<"t1">>, _) -> {ok, Updated} end),
|
||||
ok = meck:expect(core_ticket, update_ticket,
|
||||
fun(<<"t1">>, _) -> {ok, Updated} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
#{<<"status">> := <<"closed">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% PUT – тикет не найден
|
||||
test_update_not_found() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"t99">> end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> <<"t99">> end), % для маршрута с id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"closed">>}), Req} end),
|
||||
ok = meck:expect(core_ticket, update_ticket, fun(_, _) -> {error, not_found} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(cowboy_req, read_body,
|
||||
fun(Req) -> {ok, jsx:encode(#{<<"status">> => <<"closed">>}), Req} end),
|
||||
ok = meck:expect(core_ticket, update_ticket,
|
||||
fun(_, _) -> {error, not_found} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(404, Status).
|
||||
|
||||
%% DELETE – удаление тикета
|
||||
test_delete() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"t1">> end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"DELETE">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> <<"t1">> end), % для маршрута с id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_ticket, delete_ticket, fun(<<"t1">>) -> {ok, deleted} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_ticket, delete_ticket,
|
||||
fun(<<"t1">>) -> {ok, deleted} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(200, Status),
|
||||
#{<<"status">> := <<"deleted">>} = jsx:decode(RespBody, [return_maps]).
|
||||
|
||||
%% DELETE – тикет не найден
|
||||
test_delete_not_found() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"t99">> end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"DELETE">> end),
|
||||
ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> <<"t99">> end), % для маршрута с id
|
||||
ok = meck:expect(handler_auth, authenticate,
|
||||
fun(Req) -> {ok, <<"adm1">>, Req} end),
|
||||
AdminUser = #user{id = <<"adm1">>, role = admin},
|
||||
ok = meck:expect(core_user, get_by_id, fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_ticket, delete_ticket, fun(_) -> {error, not_found} end),
|
||||
ok = meck:expect(core_user, get_by_id,
|
||||
fun(<<"adm1">>) -> {ok, AdminUser} end),
|
||||
ok = meck:expect(core_ticket, delete_ticket,
|
||||
fun(_) -> {error, not_found} end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, _, _} = erase(test_reply),
|
||||
?assertEqual(404, Status).
|
||||
|
||||
%% Неправильный метод
|
||||
test_wrong_method() ->
|
||||
ok = meck:expect(cowboy_req, binding, fun(id, _) -> undefined end),
|
||||
ok = meck:expect(cowboy_req, method, fun(_) -> <<"PATCH">> end),
|
||||
ok = meck:expect(cowboy_req, binding,
|
||||
fun(id, _) -> undefined end), % для маршрута без id
|
||||
ok = meck:expect(cowboy_req, reply,
|
||||
fun(Code, Headers, Body, Req) ->
|
||||
put(test_reply, {Code, Headers, Body, Req})
|
||||
end),
|
||||
{ok, _, _} = admin_handler_tickets:init(req, []),
|
||||
{Status, _, RespBody, _} = erase(test_reply),
|
||||
?assertEqual(405, Status),
|
||||
|
||||
@@ -27,21 +27,21 @@ generate_user_token_test_() ->
|
||||
{setup, fun setup/0, fun cleanup/1, [
|
||||
{"Generate user token returns a binary",
|
||||
fun() ->
|
||||
Token = auth:generate_user_token(<<"user123">>, <<"user">>),
|
||||
Token = eventhub_auth:generate_user_token(<<"user123">>, <<"user">>),
|
||||
?assert(is_binary(Token)),
|
||||
?assert(size(Token) > 0)
|
||||
end},
|
||||
{"Generated user token can be verified",
|
||||
fun() ->
|
||||
Token = auth:generate_user_token(<<"user123">>, <<"user">>),
|
||||
{ok, UserId, Role} = auth:verify_user_token(Token),
|
||||
Token = eventhub_auth:generate_user_token(<<"user123">>, <<"user">>),
|
||||
{ok, UserId, Role} = eventhub_auth:verify_user_token(Token),
|
||||
?assertEqual(<<"user123">>, UserId),
|
||||
?assertEqual(<<"user">>, Role)
|
||||
end},
|
||||
{"Generate admin token with superadmin role",
|
||||
fun() ->
|
||||
Token = auth:generate_admin_token(<<"admin1">>, <<"superadmin">>),
|
||||
{ok, UserId, Role} = auth:verify_admin_token(Token),
|
||||
Token = eventhub_auth:generate_admin_token(<<"admin1">>, <<"superadmin">>),
|
||||
{ok, UserId, Role} = eventhub_auth:verify_admin_token(Token),
|
||||
?assertEqual(<<"admin1">>, UserId),
|
||||
?assertEqual(<<"superadmin">>, Role)
|
||||
end}
|
||||
@@ -55,19 +55,19 @@ verify_token_errors_test_() ->
|
||||
{"Invalid token signature returns error",
|
||||
fun() ->
|
||||
FakeToken = <<"not.a.valid.token">>,
|
||||
?assertEqual({error, invalid_token}, auth:verify_user_token(FakeToken)),
|
||||
?assertEqual({error, invalid_token}, auth:verify_admin_token(FakeToken))
|
||||
?assertEqual({error, invalid_token}, eventhub_auth:verify_user_token(FakeToken)),
|
||||
?assertEqual({error, invalid_token}, eventhub_auth:verify_admin_token(FakeToken))
|
||||
end},
|
||||
{"User token rejected by admin verifier (different secret)",
|
||||
fun() ->
|
||||
Token = auth:generate_user_token(<<"x">>, <<"user">>),
|
||||
Token = eventhub_auth:generate_user_token(<<"x">>, <<"user">>),
|
||||
% Разные секреты → подпись недействительна для admin JWK
|
||||
?assertEqual({error, invalid_signature}, auth:verify_admin_token(Token))
|
||||
?assertEqual({error, invalid_signature}, eventhub_auth:verify_admin_token(Token))
|
||||
end},
|
||||
{"Admin token rejected by user verifier (different secret)",
|
||||
fun() ->
|
||||
Token = auth:generate_admin_token(<<"x">>, <<"admin">>),
|
||||
?assertEqual({error, invalid_signature}, auth:verify_user_token(Token))
|
||||
Token = eventhub_auth:generate_admin_token(<<"x">>, <<"admin">>),
|
||||
?assertEqual({error, invalid_signature}, eventhub_auth:verify_user_token(Token))
|
||||
end}
|
||||
]}.
|
||||
|
||||
@@ -81,10 +81,10 @@ authenticate_user_request_test_() ->
|
||||
UserMap = #{id => <<"user1">>, email => <<"u@test.com">>, role => <<"user">>},
|
||||
ok = meck:expect(logic_auth, authenticate_user, fun(_Email, _Password) -> {ok, UserMap} end),
|
||||
Req = undefined,
|
||||
{ok, Token, ReturnedUser} = auth:authenticate_user_request(Req, <<"u@test.com">>, <<"pass">>),
|
||||
{ok, Token, ReturnedUser} = eventhub_auth:authenticate_user_request(Req, <<"u@test.com">>, <<"pass">>),
|
||||
?assert(is_binary(Token)),
|
||||
?assertEqual(UserMap, ReturnedUser),
|
||||
{ok, UserId, Role} = auth:verify_user_token(Token),
|
||||
{ok, UserId, Role} = eventhub_auth:verify_user_token(Token),
|
||||
?assertEqual(<<"user1">>, UserId),
|
||||
?assertEqual(<<"user">>, Role)
|
||||
end},
|
||||
@@ -92,7 +92,7 @@ authenticate_user_request_test_() ->
|
||||
fun() ->
|
||||
ok = meck:expect(logic_auth, authenticate_user, fun(_Email, _Password) -> {error, bad_credentials} end),
|
||||
Req = undefined,
|
||||
?assertEqual({error, bad_credentials}, auth:authenticate_user_request(Req, <<"bad">>, <<"pwd">>))
|
||||
?assertEqual({error, bad_credentials}, eventhub_auth:authenticate_user_request(Req, <<"bad">>, <<"pwd">>))
|
||||
end}
|
||||
]}.
|
||||
|
||||
@@ -106,10 +106,10 @@ authenticate_admin_request_test_() ->
|
||||
AdminMap = #{id => <<"adm1">>, email => <<"admin@test.com">>, role => <<"superadmin">>},
|
||||
ok = meck:expect(logic_auth, authenticate_user, fun(_Email, _Password) -> {ok, AdminMap} end),
|
||||
Req = undefined,
|
||||
{ok, Token, ReturnedUser} = auth:authenticate_admin_request(Req, <<"admin@test.com">>, <<"pass">>),
|
||||
{ok, Token, ReturnedUser} = eventhub_auth:authenticate_admin_request(Req, <<"admin@test.com">>, <<"pass">>),
|
||||
?assert(is_binary(Token)),
|
||||
?assertEqual(AdminMap, ReturnedUser),
|
||||
{ok, UserId, Role} = auth:verify_admin_token(Token),
|
||||
{ok, UserId, Role} = eventhub_auth:verify_admin_token(Token),
|
||||
?assertEqual(<<"adm1">>, UserId),
|
||||
?assertEqual(<<"superadmin">>, Role)
|
||||
end},
|
||||
@@ -119,15 +119,15 @@ authenticate_admin_request_test_() ->
|
||||
ok = meck:expect(logic_auth, authenticate_user, fun(_Email, _Password) -> {ok, UserMap} end),
|
||||
Req = undefined,
|
||||
?assertEqual({error, insufficient_permissions},
|
||||
auth:authenticate_admin_request(Req, <<"u@test.com">>, <<"pwd">>))
|
||||
eventhub_auth:authenticate_admin_request(Req, <<"u@test.com">>, <<"pwd">>))
|
||||
end},
|
||||
{"Moderator role is accepted as admin",
|
||||
fun() ->
|
||||
ModMap = #{id => <<"moder1">>, email => <<"mod@test.com">>, role => <<"moderator">>},
|
||||
ok = meck:expect(logic_auth, authenticate_user, fun(_Email, _Password) -> {ok, ModMap} end),
|
||||
Req = undefined,
|
||||
{ok, Token, _} = auth:authenticate_admin_request(Req, <<"mod@test.com">>, <<"pwd">>),
|
||||
{ok, _, Role} = auth:verify_admin_token(Token),
|
||||
{ok, Token, _} = eventhub_auth:authenticate_admin_request(Req, <<"mod@test.com">>, <<"pwd">>),
|
||||
{ok, _, Role} = eventhub_auth:verify_admin_token(Token),
|
||||
?assertEqual(<<"moderator">>, Role)
|
||||
end}
|
||||
]}.
|
||||
@@ -136,4 +136,4 @@ authenticate_admin_request_test_() ->
|
||||
%% Тест generate_refresh_token/1
|
||||
%% ------------------------------------------------------------------
|
||||
generate_refresh_token_test() ->
|
||||
{_, _} = auth:generate_refresh_token(<<"anyuser">>).
|
||||
{_, _} = eventhub_auth:generate_refresh_token(<<"anyuser">>).
|
||||
@@ -1,63 +0,0 @@
|
||||
-module(core_banned_word_tests).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
setup() ->
|
||||
{atomic, ok} = mnesia:start(), % правильное значение
|
||||
ok = mnesia:create_table(banned_word, [
|
||||
{attributes, record_info(fields, banned_word)},
|
||||
{disc_copies, []},
|
||||
{ram_copies, [node()]}
|
||||
]).
|
||||
|
||||
cleanup(_) ->
|
||||
mnesia:delete_table(banned_word),
|
||||
mnesia:stop().
|
||||
|
||||
core_banned_word_test_() ->
|
||||
{setup, fun setup/0, fun cleanup/1, [
|
||||
{"Add banned word – success", fun test_add_success/0},
|
||||
{"Add banned word – already exists", fun test_add_already_exists/0},
|
||||
{"Remove banned word – success", fun test_remove_success/0},
|
||||
{"Remove banned word – not found", fun test_remove_not_found/0},
|
||||
{"Update banned word – success", fun test_update_success/0},
|
||||
{"Update banned word – not found", fun test_update_not_found/0},
|
||||
{"List banned words – returns all records", fun test_list/0}
|
||||
]}.
|
||||
|
||||
test_add_success() ->
|
||||
{ok, BW} = core_banned_words:add_banned_word(<<"badword">>, <<"admin1">>),
|
||||
?assertEqual(<<"badword">>, BW#banned_word.word),
|
||||
?assertEqual(<<"admin1">>, BW#banned_word.added_by),
|
||||
?assert(is_binary(BW#banned_word.id)),
|
||||
?assert(size(BW#banned_word.id) > 0),
|
||||
?assertEqual(1, length(core_banned_words:list_banned_words())).
|
||||
|
||||
test_add_already_exists() ->
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"spam">>, <<"admin1">>),
|
||||
{error, already_exists} = core_banned_words:add_banned_word(<<"spam">>, <<"admin2">>).
|
||||
|
||||
test_remove_success() ->
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"badword">>, <<"admin1">>),
|
||||
{ok, deleted} = core_banned_words:remove_banned_word(<<"badword">>),
|
||||
?assertEqual([], core_banned_words:list_banned_words()).
|
||||
|
||||
test_remove_not_found() ->
|
||||
?assertEqual({error, not_found}, core_banned_words:remove_banned_word(<<"unknown">>)).
|
||||
|
||||
test_update_success() ->
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"oldword">>, <<"admin1">>),
|
||||
{ok, BW} = core_banned_words:update_banned_word(<<"oldword">>, <<"newword">>),
|
||||
?assertEqual(<<"newword">>, BW#banned_word.word),
|
||||
?assertEqual([<<"newword">>], [W#banned_word.word || W <- core_banned_words:list_banned_words()]).
|
||||
|
||||
test_update_not_found() ->
|
||||
?assertEqual({error, not_found}, core_banned_words:update_banned_word(<<"unknown">>, <<"newword">>)).
|
||||
|
||||
test_list() ->
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"word1">>, <<"adm1">>),
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"word2">>, <<"adm2">>),
|
||||
List = core_banned_words:list_banned_words(),
|
||||
?assertEqual(2, length(List)),
|
||||
?assert(lists:any(fun(W) -> W#banned_word.word == <<"word1">> end, List)),
|
||||
?assert(lists:any(fun(W) -> W#banned_word.word == <<"word2">> end, List)).
|
||||
87
test/unit/core_banned_words_tests.erl
Normal file
87
test/unit/core_banned_words_tests.erl
Normal file
@@ -0,0 +1,87 @@
|
||||
-module(core_banned_words_tests).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Фикстуры
|
||||
%% ----------------------------------------------------------------
|
||||
setup() ->
|
||||
% Гарантированно останавливаем Mnesia (если уже запущена)
|
||||
catch mnesia:stop(),
|
||||
% Запускаем Mnesia (первый раз вернёт {atomic, ok}, потом ok)
|
||||
case mnesia:start() of
|
||||
{atomic, ok} -> ok;
|
||||
ok -> ok
|
||||
end,
|
||||
% Создаём таблицу (всегда возвращает {atomic, ok})
|
||||
{atomic, ok} = mnesia:create_table(banned_word, [
|
||||
{attributes, record_info(fields, banned_word)},
|
||||
{ram_copies, [node()]}
|
||||
]),
|
||||
ok.
|
||||
|
||||
cleanup(_) ->
|
||||
mnesia:delete_table(banned_word),
|
||||
mnesia:stop().
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Тесты
|
||||
%% ----------------------------------------------------------------
|
||||
core_banned_words_test_() ->
|
||||
{foreach, fun setup/0, fun cleanup/1, [
|
||||
{"Add banned word (success)", fun test_add_banned_word/0},
|
||||
{"Add banned word (duplicate)", fun test_add_duplicate/0},
|
||||
{"Remove banned word (success)", fun test_remove_banned_word/0},
|
||||
{"Remove banned word (not found)", fun test_remove_not_found/0},
|
||||
{"Update banned word (success)", fun test_update_banned_word/0},
|
||||
{"Update banned word (not found)", fun test_update_not_found/0},
|
||||
{"List banned words", fun test_list_banned_words/0}
|
||||
]}.
|
||||
|
||||
%% ── Добавление ───────────────────────────────────────────
|
||||
test_add_banned_word() ->
|
||||
Word = <<"badword">>,
|
||||
AddedBy = <<"admin1">>,
|
||||
{ok, BW} = core_banned_words:add_banned_word(Word, AddedBy),
|
||||
?assertEqual(Word, BW#banned_word.word),
|
||||
?assertEqual(AddedBy, BW#banned_word.added_by),
|
||||
?assert(is_binary(BW#banned_word.id)),
|
||||
?assert(size(BW#banned_word.id) > 0).
|
||||
|
||||
test_add_duplicate() ->
|
||||
Word = <<"duplicate">>,
|
||||
{ok, _} = core_banned_words:add_banned_word(Word, <<"admin1">>),
|
||||
?assertEqual({error, already_exists}, core_banned_words:add_banned_word(Word, <<"admin2">>)).
|
||||
|
||||
%% ── Удаление ─────────────────────────────────────────────
|
||||
test_remove_banned_word() ->
|
||||
Word = <<"to_remove">>,
|
||||
{ok, _} = core_banned_words:add_banned_word(Word, <<"admin1">>),
|
||||
{ok, deleted} = core_banned_words:remove_banned_word(Word),
|
||||
?assertEqual([], core_banned_words:list_banned_words()).
|
||||
|
||||
test_remove_not_found() ->
|
||||
?assertEqual({error, not_found}, core_banned_words:remove_banned_word(<<"nonexistent">>)).
|
||||
|
||||
%% ── Обновление ───────────────────────────────────────────
|
||||
test_update_banned_word() ->
|
||||
OldWord = <<"old_word">>,
|
||||
NewWord = <<"new_word">>,
|
||||
{ok, _} = core_banned_words:add_banned_word(OldWord, <<"admin1">>),
|
||||
{ok, Updated} = core_banned_words:update_banned_word(OldWord, NewWord),
|
||||
?assertEqual(NewWord, Updated#banned_word.word),
|
||||
?assertEqual([NewWord], [W#banned_word.word || W <- core_banned_words:list_banned_words()]).
|
||||
|
||||
test_update_not_found() ->
|
||||
?assertEqual({error, not_found}, core_banned_words:update_banned_word(<<"unknown">>, <<"new">>)).
|
||||
|
||||
%% ── Список ───────────────────────────────────────────────
|
||||
test_list_banned_words() ->
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"word1">>, <<"adm1">>),
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"word2">>, <<"adm2">>),
|
||||
{ok, _} = core_banned_words:add_banned_word(<<"word3">>, <<"adm3">>),
|
||||
List = core_banned_words:list_banned_words(),
|
||||
?assertEqual(3, length(List)),
|
||||
?assert(lists:any(fun(W) -> W#banned_word.word == <<"word1">> end, List)),
|
||||
?assert(lists:any(fun(W) -> W#banned_word.word == <<"word2">> end, List)),
|
||||
?assert(lists:any(fun(W) -> W#banned_word.word == <<"word3">> end, List)).
|
||||
@@ -2,9 +2,16 @@
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Фикстуры
|
||||
%% ----------------------------------------------------------------
|
||||
setup() ->
|
||||
mnesia:start(),
|
||||
mnesia:create_table(ticket, [
|
||||
catch mnesia:stop(),
|
||||
case mnesia:start() of
|
||||
{atomic, ok} -> ok;
|
||||
ok -> ok
|
||||
end,
|
||||
{atomic, ok} = mnesia:create_table(ticket, [
|
||||
{attributes, record_info(fields, ticket)},
|
||||
{ram_copies, [node()]}
|
||||
]),
|
||||
@@ -12,110 +19,118 @@ setup() ->
|
||||
|
||||
cleanup(_) ->
|
||||
mnesia:delete_table(ticket),
|
||||
mnesia:stop(),
|
||||
ok.
|
||||
mnesia:stop().
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Тесты
|
||||
%% ----------------------------------------------------------------
|
||||
core_ticket_test_() ->
|
||||
{foreach,
|
||||
fun setup/0,
|
||||
fun cleanup/1,
|
||||
[
|
||||
{"Create ticket test", fun test_create_ticket/0},
|
||||
{"Update existing ticket test", fun test_update_ticket/0},
|
||||
{"Get ticket by id test", fun test_get_by_id/0},
|
||||
{"Get ticket by error hash test", fun test_get_by_error_hash/0},
|
||||
{"List all tickets test", fun test_list_all/0},
|
||||
{"List by status test", fun test_list_by_status/0},
|
||||
{"Update status test", fun test_update_status/0},
|
||||
{"Assign ticket test", fun test_assign_ticket/0},
|
||||
{"Add resolution test", fun test_add_resolution/0}
|
||||
]}.
|
||||
{foreach, fun setup/0, fun cleanup/1, [
|
||||
{"Create ticket and retrieve it", fun test_create_and_get/0},
|
||||
{"Update ticket status", fun test_update_status/0},
|
||||
{"Delete ticket and verify removal", fun test_delete_ticket/0},
|
||||
{"List all tickets returns created ones", fun test_list_all/0},
|
||||
{"List tickets by user filters correctly", fun test_list_by_user/0},
|
||||
{"Get ticket stats reflects real counts", fun test_stats/0},
|
||||
{"Update ticket with unknown id fails", fun test_update_not_found/0},
|
||||
{"Delete ticket with unknown id fails", fun test_delete_not_found/0},
|
||||
{"Get ticket with unknown id fails", fun test_get_not_found/0}
|
||||
]}.
|
||||
|
||||
test_create_ticket() ->
|
||||
ErrorMsg = <<"Test error">>,
|
||||
Stacktrace = <<"line 1\nline 2">>,
|
||||
Context = #{user_id => <<"user123">>},
|
||||
%% ── Вспомогательная функция для создания тикета ─────────
|
||||
make_ticket(ErrorMsg) ->
|
||||
Data = #{
|
||||
<<"error_message">> => list_to_binary(ErrorMsg),
|
||||
<<"stacktrace">> => <<"trace">>,
|
||||
<<"reporter_id">> => <<"user123">>,
|
||||
<<"status">> => <<"open">>
|
||||
},
|
||||
{ok, Ticket} = core_ticket:create_ticket(Data),
|
||||
Ticket.
|
||||
|
||||
{ok, Ticket} = core_ticket:create_or_update(ErrorMsg, Stacktrace, Context),
|
||||
%% ── Тесты ─────────────────────────────────────────────────
|
||||
|
||||
?assertEqual(ErrorMsg, Ticket#ticket.error_message),
|
||||
?assertEqual(Stacktrace, Ticket#ticket.stacktrace),
|
||||
?assertEqual(1, Ticket#ticket.count),
|
||||
?assertEqual(open, Ticket#ticket.status),
|
||||
test_create_and_get() ->
|
||||
Ticket = make_ticket("Bug1"),
|
||||
?assert(is_binary(Ticket#ticket.id)),
|
||||
?assert(is_binary(Ticket#ticket.error_hash)).
|
||||
|
||||
test_update_ticket() ->
|
||||
ErrorMsg = <<"Test error">>,
|
||||
Stacktrace = <<"line 1">>,
|
||||
Context = #{},
|
||||
|
||||
{ok, Ticket1} = core_ticket:create_or_update(ErrorMsg, Stacktrace, Context),
|
||||
?assertEqual(1, Ticket1#ticket.count),
|
||||
|
||||
{ok, Ticket2} = core_ticket:create_or_update(ErrorMsg, Stacktrace, Context),
|
||||
?assertEqual(Ticket1#ticket.id, Ticket2#ticket.id),
|
||||
?assertEqual(2, Ticket2#ticket.count),
|
||||
?assert(Ticket2#ticket.last_seen >= Ticket1#ticket.last_seen).
|
||||
|
||||
test_get_by_id() ->
|
||||
{ok, Ticket} = core_ticket:create_or_update(<<"Error">>, <<"">>, #{}),
|
||||
|
||||
{ok, Found} = core_ticket:get_by_id(Ticket#ticket.id),
|
||||
?assertEqual(Ticket#ticket.id, Found#ticket.id),
|
||||
|
||||
{error, not_found} = core_ticket:get_by_id(<<"nonexistent">>).
|
||||
|
||||
test_get_by_error_hash() ->
|
||||
ErrorMsg = <<"Unique error">>,
|
||||
Stacktrace = <<"stack">>,
|
||||
{ok, Ticket} = core_ticket:create_or_update(ErrorMsg, Stacktrace, #{}),
|
||||
|
||||
{ok, Found} = core_ticket:get_by_error_hash(Ticket#ticket.error_hash),
|
||||
?assertEqual(Ticket#ticket.id, Found#ticket.id),
|
||||
|
||||
{error, not_found} = core_ticket:get_by_error_hash(<<"badhash">>).
|
||||
|
||||
test_list_all() ->
|
||||
{ok, _} = core_ticket:create_or_update(<<"Error 1">>, <<"">>, #{}),
|
||||
{ok, _} = core_ticket:create_or_update(<<"Error 2">>, <<"">>, #{}),
|
||||
{ok, _} = core_ticket:create_or_update(<<"Error 3">>, <<"">>, #{}),
|
||||
|
||||
{ok, Tickets} = core_ticket:list_all(),
|
||||
?assertEqual(3, length(Tickets)).
|
||||
|
||||
test_list_by_status() ->
|
||||
{ok, _T1} = core_ticket:create_or_update(<<"E1">>, <<"">>, #{}),
|
||||
{ok, T2} = core_ticket:create_or_update(<<"E2">>, <<"">>, #{}),
|
||||
|
||||
core_ticket:update_status(T2#ticket.id, resolved),
|
||||
|
||||
{ok, Open} = core_ticket:list_by_status(open),
|
||||
?assertEqual(1, length(Open)),
|
||||
|
||||
{ok, Resolved} = core_ticket:list_by_status(resolved),
|
||||
?assertEqual(1, length(Resolved)).
|
||||
{ok, Retrieved} = core_ticket:get_by_id(Ticket#ticket.id),
|
||||
?assertEqual(Ticket#ticket.id, Retrieved#ticket.id).
|
||||
|
||||
test_update_status() ->
|
||||
{ok, Ticket} = core_ticket:create_or_update(<<"Error">>, <<"">>, #{}),
|
||||
Ticket = make_ticket("Bug2"),
|
||||
{ok, Updated} = core_ticket:update_ticket(Ticket#ticket.id,
|
||||
#{<<"status">> => <<"closed">>}),
|
||||
?assertEqual(closed, Updated#ticket.status),
|
||||
{ok, Stored} = core_ticket:get_by_id(Ticket#ticket.id),
|
||||
?assertEqual(closed, Stored#ticket.status).
|
||||
|
||||
{ok, Updated} = core_ticket:update_status(Ticket#ticket.id, in_progress),
|
||||
?assertEqual(in_progress, Updated#ticket.status),
|
||||
test_delete_ticket() ->
|
||||
Ticket = make_ticket("Bug3"),
|
||||
Id = Ticket#ticket.id,
|
||||
{ok, deleted} = core_ticket:delete_ticket(Id),
|
||||
% Проверяем, что тикет больше не читается
|
||||
?assertMatch({error, not_found}, core_ticket:get_by_id(Id)).
|
||||
|
||||
{ok, Resolved} = core_ticket:update_status(Ticket#ticket.id, resolved),
|
||||
?assertEqual(resolved, Resolved#ticket.status).
|
||||
test_list_all() ->
|
||||
T1 = make_ticket("E1"),
|
||||
T2 = make_ticket("E2"),
|
||||
All = core_ticket:list_all(),
|
||||
?assert(length(All) >= 2),
|
||||
Ids = [T#ticket.id || T <- All],
|
||||
?assert(lists:member(T1#ticket.id, Ids)),
|
||||
?assert(lists:member(T2#ticket.id, Ids)).
|
||||
|
||||
test_assign_ticket() ->
|
||||
AdminId = <<"admin123">>,
|
||||
{ok, Ticket} = core_ticket:create_or_update(<<"Error">>, <<"">>, #{}),
|
||||
test_list_by_user() ->
|
||||
% Создаём тикет от пользователя test_user
|
||||
Data = #{
|
||||
<<"error_message">> => <<"from_test_user">>,
|
||||
<<"stacktrace">> => <<"trace">>,
|
||||
<<"reporter_id">> => <<"test_user">>,
|
||||
<<"status">> => <<"open">>
|
||||
},
|
||||
{ok, T1} = core_ticket:create_ticket(Data),
|
||||
% Ещё один тикет от другого пользователя
|
||||
DataOther = #{
|
||||
<<"error_message">> => <<"other">>,
|
||||
<<"stacktrace">> => <<"trace">>,
|
||||
<<"reporter_id">> => <<"other_user">>,
|
||||
<<"status">> => <<"open">>
|
||||
},
|
||||
{ok, _T2} = core_ticket:create_ticket(DataOther),
|
||||
% list_by_user("test_user") должен вернуть ровно один тикет (T1)
|
||||
UserTickets = core_ticket:list_by_user(<<"test_user">>),
|
||||
?assertEqual(1, length(UserTickets)),
|
||||
?assertEqual(T1#ticket.id, (hd(UserTickets))#ticket.id).
|
||||
|
||||
{ok, Assigned} = core_ticket:assign(Ticket#ticket.id, AdminId),
|
||||
?assertEqual(AdminId, Assigned#ticket.assigned_to),
|
||||
?assertEqual(in_progress, Assigned#ticket.status).
|
||||
test_stats() ->
|
||||
Data1 = #{
|
||||
<<"error_message">> => <<"stat1">>,
|
||||
<<"stacktrace">> => <<"trace">>,
|
||||
<<"reporter_id">> => <<"reporter123">>,
|
||||
<<"status">> => <<"open">>
|
||||
},
|
||||
Data2 = #{
|
||||
<<"error_message">> => <<"stat2">>,
|
||||
<<"stacktrace">> => <<"trace">>,
|
||||
<<"reporter_id">> => <<"reporter456">>,
|
||||
<<"status">> => <<"open">>
|
||||
},
|
||||
{ok, _} = core_ticket:create_ticket(Data1),
|
||||
{ok, _} = core_ticket:create_ticket(Data2),
|
||||
Stats = core_ticket:stats(),
|
||||
?assert(is_map(Stats)),
|
||||
?assert(maps:is_key(open, Stats)),
|
||||
?assert(maps:is_key(total, Stats)),
|
||||
% Проверяем, что общее количество тикетов не меньше 2
|
||||
Total = maps:get(total, Stats),
|
||||
?assert(Total >= 2).
|
||||
|
||||
test_add_resolution() ->
|
||||
Note = <<"Fixed in version 1.0">>,
|
||||
{ok, Ticket} = core_ticket:create_or_update(<<"Error">>, <<"">>, #{}),
|
||||
test_update_not_found() ->
|
||||
{error, not_found} = core_ticket:update_ticket(<<"nonexistent">>,
|
||||
#{<<"status">> => <<"closed">>}).
|
||||
|
||||
{ok, Updated} = core_ticket:add_resolution(Ticket#ticket.id, Note),
|
||||
?assertEqual(Note, Updated#ticket.resolution_note).
|
||||
test_delete_not_found() ->
|
||||
{error, not_found} = core_ticket:delete_ticket(<<"nonexistent">>).
|
||||
|
||||
test_get_not_found() ->
|
||||
{error, not_found} = core_ticket:get_by_id(<<"nonexistent">>).
|
||||
@@ -4,9 +4,6 @@
|
||||
-define(JWT_SECRET, <<"test-user-secret-key-32-byt!">>).
|
||||
-define(ADMIN_JWT_SECRET, <<"test-admin-secret-key-32-b">>).
|
||||
|
||||
%% ------------------------------------------------------------------
|
||||
%% Фикстуры
|
||||
%% ------------------------------------------------------------------
|
||||
setup() ->
|
||||
application:set_env(eventhub, jwt_secret, ?JWT_SECRET),
|
||||
application:set_env(eventhub, admin_jwt_secret, ?ADMIN_JWT_SECRET),
|
||||
@@ -18,9 +15,6 @@ cleanup(_) ->
|
||||
application:unset_env(eventhub, admin_jwt_secret),
|
||||
application:stop(jose).
|
||||
|
||||
%% ------------------------------------------------------------------
|
||||
%% Тесты
|
||||
%% ------------------------------------------------------------------
|
||||
logic_auth_test_() ->
|
||||
[
|
||||
{"Password hash test", fun test_password_hash/0},
|
||||
@@ -31,7 +25,6 @@ logic_auth_test_() ->
|
||||
]}
|
||||
].
|
||||
|
||||
%% ── Хеширование паролей (остаётся в logic_auth) ──────────────────
|
||||
test_password_hash() ->
|
||||
Password = <<"secret123">>,
|
||||
{ok, Hash} = logic_auth:hash_password(Password),
|
||||
@@ -39,27 +32,23 @@ test_password_hash() ->
|
||||
{ok, true} = logic_auth:verify_password(Password, Hash),
|
||||
{ok, false} = logic_auth:verify_password(<<"wrong">>, Hash).
|
||||
|
||||
%% ── JWT тесты (перенесены в auth) ─────────────────────────────────
|
||||
test_jwt() ->
|
||||
UserId = <<"user123">>,
|
||||
Role = <<"user">>,
|
||||
Token = auth:generate_user_token(UserId, Role),
|
||||
Token = eventhub_auth:generate_user_token(UserId, Role),
|
||||
?assert(is_binary(Token)),
|
||||
{ok, ReturnedUserId, ReturnedRole} = auth:verify_user_token(Token),
|
||||
{ok, ReturnedUserId, ReturnedRole} = eventhub_auth:verify_user_token(Token),
|
||||
?assertEqual(UserId, ReturnedUserId),
|
||||
?assertEqual(Role, ReturnedRole),
|
||||
% Проверка невалидного токена
|
||||
{error, invalid_token} = auth:verify_user_token(<<"invalid.token.here">>).
|
||||
{error, invalid_token} = eventhub_auth:verify_user_token(<<"invalid.token.here">>).
|
||||
|
||||
test_jwt_expired() ->
|
||||
% Тест на истечение срока пока пропущен, так как требует мока времени
|
||||
ok.
|
||||
|
||||
%% ── Refresh token (перенесён в auth) ────────────────────────────
|
||||
test_refresh_token() ->
|
||||
{Token, ExpiresAt} = auth:generate_refresh_token(<<"user123">>),
|
||||
{Token, ExpiresAt} = eventhub_auth:generate_refresh_token(<<"user123">>),
|
||||
?assert(is_binary(Token)),
|
||||
?assert(size(Token) >= 32),
|
||||
?assert(is_integer(ExpiresAt)),
|
||||
Now = os:system_time(second),
|
||||
?assert(is_tuple(ExpiresAt)),
|
||||
Now = calendar:universal_time(),
|
||||
?assert(ExpiresAt > Now).
|
||||
@@ -2,13 +2,25 @@
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Фикстуры
|
||||
%% ----------------------------------------------------------------
|
||||
setup() ->
|
||||
mnesia:start(),
|
||||
mnesia:create_table(user, [{attributes, record_info(fields, user)}, {ram_copies, [node()]}]),
|
||||
mnesia:create_table(calendar, [{attributes, record_info(fields, calendar)}, {ram_copies, [node()]}]),
|
||||
mnesia:create_table(event, [{attributes, record_info(fields, event)}, {ram_copies, [node()]}]),
|
||||
mnesia:create_table(report, [{attributes, record_info(fields, report)}, {ram_copies, [node()]}]),
|
||||
mnesia:create_table(banned_word, [{attributes, record_info(fields, banned_word)}, {ram_copies, [node()]}]),
|
||||
catch mnesia:stop(),
|
||||
case mnesia:start() of
|
||||
{atomic, ok} -> ok;
|
||||
ok -> ok
|
||||
end,
|
||||
{atomic, ok} = mnesia:create_table(user, [
|
||||
{attributes, record_info(fields, user)}, {ram_copies, [node()]}]),
|
||||
{atomic, ok} = mnesia:create_table(calendar, [
|
||||
{attributes, record_info(fields, calendar)}, {ram_copies, [node()]}]),
|
||||
{atomic, ok} = mnesia:create_table(event, [
|
||||
{attributes, record_info(fields, event)}, {ram_copies, [node()]}]),
|
||||
{atomic, ok} = mnesia:create_table(report, [
|
||||
{attributes, record_info(fields, report)}, {ram_copies, [node()]}]),
|
||||
{atomic, ok} = mnesia:create_table(banned_word, [
|
||||
{attributes, record_info(fields, banned_word)}, {ram_copies, [node()]}]),
|
||||
ok.
|
||||
|
||||
cleanup(_) ->
|
||||
@@ -17,29 +29,36 @@ cleanup(_) ->
|
||||
mnesia:delete_table(event),
|
||||
mnesia:delete_table(calendar),
|
||||
mnesia:delete_table(user),
|
||||
mnesia:stop(),
|
||||
ok.
|
||||
mnesia:stop().
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Тесты
|
||||
%% ----------------------------------------------------------------
|
||||
logic_moderation_test_() ->
|
||||
{foreach,
|
||||
fun setup/0,
|
||||
fun cleanup/1,
|
||||
[
|
||||
{"Create report test", fun test_create_report/0},
|
||||
{"Get reports test", fun test_get_reports/0},
|
||||
{"Resolve report test", fun test_resolve_report/0},
|
||||
{"Add banned word test", fun test_add_banned_word/0},
|
||||
{"Remove banned word test", fun test_remove_banned_word/0},
|
||||
{"Auto freeze by reports test", fun test_auto_freeze/0},
|
||||
{"Freeze/unfreeze calendar test", fun test_freeze_calendar/0},
|
||||
{"Freeze/unfreeze event test", fun test_freeze_event/0},
|
||||
{"Check content test", fun test_check_content/0}
|
||||
]}.
|
||||
{foreach, fun setup/0, fun cleanup/1, [
|
||||
{"Create report test", fun test_create_report/0},
|
||||
{"Get reports test", fun test_get_reports/0},
|
||||
{"Resolve report test", fun test_resolve_report/0},
|
||||
{"Add banned word test", fun test_add_banned_word/0},
|
||||
{"Remove banned word test", fun test_remove_banned_word/0},
|
||||
{"Auto freeze by reports test", fun test_auto_freeze/0},
|
||||
{"Freeze/unfreeze calendar test", fun test_freeze_calendar/0},
|
||||
{"Freeze/unfreeze event test", fun test_freeze_event/0},
|
||||
{"Check content test", fun test_check_content/0}
|
||||
]}.
|
||||
|
||||
%% ── Вспомогательные функции ──────────────────────────────
|
||||
create_test_user(Role) ->
|
||||
UserId = base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}),
|
||||
User = #user{id = UserId, email = <<UserId/binary, "@test.com">>, password_hash = <<"hash">>,
|
||||
role = Role, status = active, created_at = calendar:universal_time(), updated_at = calendar:universal_time()},
|
||||
User = #user{
|
||||
id = UserId,
|
||||
email = <<>>,
|
||||
password_hash = <<"hash">>,
|
||||
role = Role,
|
||||
status = active,
|
||||
created_at = calendar:universal_time(),
|
||||
updated_at = calendar:universal_time()
|
||||
},
|
||||
mnesia:dirty_write(User),
|
||||
UserId.
|
||||
|
||||
@@ -48,15 +67,16 @@ create_test_calendar(OwnerId) ->
|
||||
Calendar#calendar.id.
|
||||
|
||||
create_test_event(CalendarId) ->
|
||||
{ok, Event} = core_event:create(CalendarId, <<"Event">>, {{2026, 6, 1}, {10, 0, 0}}, 60),
|
||||
{ok, Event} = core_event:create(CalendarId, <<"Event">>,
|
||||
{{2026, 6, 1}, {10, 0, 0}}, 60),
|
||||
Event#event.id.
|
||||
|
||||
%% ── Тесты ─────────────────────────────────────────────────
|
||||
test_create_report() ->
|
||||
ReporterId = create_test_user(user),
|
||||
OwnerId = create_test_user(user),
|
||||
CalendarId = create_test_calendar(OwnerId),
|
||||
EventId = create_test_event(CalendarId),
|
||||
|
||||
{ok, Report} = logic_moderation:create_report(ReporterId, event, EventId, <<"Bad content">>),
|
||||
?assertEqual(ReporterId, Report#report.reporter_id),
|
||||
?assertEqual(pending, Report#report.status).
|
||||
@@ -67,9 +87,7 @@ test_get_reports() ->
|
||||
OwnerId = create_test_user(user),
|
||||
CalendarId = create_test_calendar(OwnerId),
|
||||
EventId = create_test_event(CalendarId),
|
||||
|
||||
{ok, _} = logic_moderation:create_report(ReporterId, event, EventId, <<"">>),
|
||||
|
||||
{ok, Reports} = logic_moderation:get_reports(AdminId),
|
||||
?assertEqual(1, length(Reports)).
|
||||
|
||||
@@ -79,7 +97,6 @@ test_resolve_report() ->
|
||||
OwnerId = create_test_user(user),
|
||||
CalendarId = create_test_calendar(OwnerId),
|
||||
EventId = create_test_event(CalendarId),
|
||||
|
||||
{ok, Report} = logic_moderation:create_report(ReporterId, event, EventId, <<"">>),
|
||||
{ok, Resolved} = logic_moderation:resolve_report(AdminId, Report#report.id, reviewed),
|
||||
?assertEqual(reviewed, Resolved#report.status),
|
||||
@@ -87,14 +104,15 @@ test_resolve_report() ->
|
||||
|
||||
test_add_banned_word() ->
|
||||
AdminId = create_test_user(admin),
|
||||
{ok, _} = logic_moderation:add_banned_word(AdminId, <<"badword">>),
|
||||
?assert(core_banned_word:is_banned(<<"badword">>)).
|
||||
{ok, BW} = logic_moderation:add_banned_word(AdminId, <<"badword">>),
|
||||
?assertEqual(<<"badword">>, BW#banned_word.word),
|
||||
?assertEqual(AdminId, BW#banned_word.added_by).
|
||||
|
||||
test_remove_banned_word() ->
|
||||
AdminId = create_test_user(admin),
|
||||
{ok, _} = logic_moderation:add_banned_word(AdminId, <<"badword">>),
|
||||
{ok, removed} = logic_moderation:remove_banned_word(AdminId, <<"badword">>),
|
||||
?assertNot(core_banned_word:is_banned(<<"badword">>)).
|
||||
{ok, deleted} = logic_moderation:remove_banned_word(AdminId, <<"badword">>),
|
||||
?assertEqual([], core_banned_words:list_banned_words()).
|
||||
|
||||
test_auto_freeze() ->
|
||||
Reporter1 = create_test_user(user),
|
||||
@@ -103,12 +121,9 @@ test_auto_freeze() ->
|
||||
OwnerId = create_test_user(user),
|
||||
CalendarId = create_test_calendar(OwnerId),
|
||||
EventId = create_test_event(CalendarId),
|
||||
|
||||
% 3 жалобы должны заморозить событие
|
||||
{ok, _} = logic_moderation:create_report(Reporter1, event, EventId, <<"">>),
|
||||
{ok, _} = logic_moderation:create_report(Reporter2, event, EventId, <<"">>),
|
||||
{ok, _} = logic_moderation:create_report(Reporter3, event, EventId, <<"">>),
|
||||
|
||||
{ok, Event} = core_event:get_by_id(EventId),
|
||||
?assertEqual(frozen, Event#event.status).
|
||||
|
||||
@@ -116,10 +131,8 @@ test_freeze_calendar() ->
|
||||
AdminId = create_test_user(admin),
|
||||
OwnerId = create_test_user(user),
|
||||
CalendarId = create_test_calendar(OwnerId),
|
||||
|
||||
{ok, Frozen} = logic_moderation:freeze_calendar(AdminId, CalendarId),
|
||||
?assertEqual(frozen, Frozen#calendar.status),
|
||||
|
||||
{ok, Unfrozen} = logic_moderation:unfreeze_calendar(AdminId, CalendarId),
|
||||
?assertEqual(active, Unfrozen#calendar.status).
|
||||
|
||||
@@ -128,19 +141,15 @@ test_freeze_event() ->
|
||||
OwnerId = create_test_user(user),
|
||||
CalendarId = create_test_calendar(OwnerId),
|
||||
EventId = create_test_event(CalendarId),
|
||||
|
||||
{ok, Frozen} = logic_moderation:freeze_event(AdminId, EventId),
|
||||
?assertEqual(frozen, Frozen#event.status),
|
||||
|
||||
{ok, Unfrozen} = logic_moderation:unfreeze_event(AdminId, EventId),
|
||||
?assertEqual(active, Unfrozen#event.status).
|
||||
|
||||
test_check_content() ->
|
||||
AdminId = create_test_user(admin),
|
||||
{ok, _} = logic_moderation:add_banned_word(AdminId, <<"bad">>),
|
||||
|
||||
?assertNot(logic_moderation:check_content(<<"Hello">>)),
|
||||
?assert(logic_moderation:check_content(<<"This is bad">>)),
|
||||
|
||||
?assertEqual(<<"Hello">>, logic_moderation:auto_moderate(<<"Hello">>)),
|
||||
?assertEqual(<<"This is ***">>, logic_moderation:auto_moderate(<<"This is bad">>)).
|
||||
@@ -2,104 +2,106 @@
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Фикстуры
|
||||
%% ----------------------------------------------------------------
|
||||
setup() ->
|
||||
mnesia:start(),
|
||||
mnesia:create_table(user, [{attributes, record_info(fields, user)}, {ram_copies, [node()]}]),
|
||||
mnesia:create_table(ticket, [{attributes, record_info(fields, ticket)}, {ram_copies, [node()]}]),
|
||||
catch mnesia:stop(),
|
||||
case mnesia:start() of
|
||||
{atomic, ok} -> ok;
|
||||
ok -> ok
|
||||
end,
|
||||
{atomic, ok} = mnesia:create_table(user, [
|
||||
{attributes, record_info(fields, user)}, {ram_copies, [node()]}]),
|
||||
{atomic, ok} = mnesia:create_table(ticket, [
|
||||
{attributes, record_info(fields, ticket)}, {ram_copies, [node()]}]),
|
||||
% Создаём админа и обычного пользователя
|
||||
Admin = #user{id = <<"admin1">>, email = <<"a@a.a">>, password_hash = <<"h">>,
|
||||
role = admin, status = active,
|
||||
created_at = calendar:universal_time(), updated_at = calendar:universal_time()},
|
||||
User = #user{id = <<"user1">>, email = <<"u@u.u">>, password_hash = <<"h">>,
|
||||
role = user, status = active,
|
||||
created_at = calendar:universal_time(), updated_at = calendar:universal_time()},
|
||||
mnesia:dirty_write(Admin),
|
||||
mnesia:dirty_write(User),
|
||||
ok.
|
||||
|
||||
cleanup(_) ->
|
||||
mnesia:delete_table(ticket),
|
||||
mnesia:delete_table(user),
|
||||
mnesia:stop(),
|
||||
ok.
|
||||
mnesia:delete_table(ticket),
|
||||
mnesia:stop().
|
||||
|
||||
%% ----------------------------------------------------------------
|
||||
%% Тесты
|
||||
%% ----------------------------------------------------------------
|
||||
logic_ticket_test_() ->
|
||||
{foreach,
|
||||
fun setup/0,
|
||||
fun cleanup/1,
|
||||
[
|
||||
{"Report error test", fun test_report_error/0},
|
||||
{"List tickets admin only", fun test_list_tickets_admin_only/0},
|
||||
{"Update status test", fun test_update_status/0},
|
||||
{"Assign ticket test", fun test_assign_ticket/0},
|
||||
{"Resolve ticket test", fun test_resolve_ticket/0},
|
||||
{"Close ticket test", fun test_close_ticket/0},
|
||||
{"Get statistics test", fun test_get_statistics/0}
|
||||
]}.
|
||||
{foreach, fun setup/0, fun cleanup/1, [
|
||||
{"Report error creates ticket", fun test_report_error/0},
|
||||
{"Report duplicate error increments count", fun test_report_duplicate/0},
|
||||
{"List tickets as admin", fun test_list_tickets/0},
|
||||
{"List tickets as non-admin returns error", fun test_list_tickets_forbidden/0},
|
||||
{"Update status as admin", fun test_update_status/0},
|
||||
{"Assign ticket as admin", fun test_assign_ticket/0},
|
||||
{"Resolve ticket as admin", fun test_resolve_ticket/0},
|
||||
{"Close ticket as admin", fun test_close_ticket/0},
|
||||
{"Get statistics as admin", fun test_get_statistics/0}
|
||||
]}.
|
||||
|
||||
create_test_user(Role) ->
|
||||
UserId = base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}),
|
||||
User = #user{id = UserId, email = <<UserId/binary, "@test.com">>, password_hash = <<"hash">>,
|
||||
role = Role, status = active, created_at = calendar:universal_time(), updated_at = calendar:universal_time()},
|
||||
mnesia:dirty_write(User),
|
||||
UserId.
|
||||
%% --- Вспомогательная функция для создания тикета ---
|
||||
report(ErrorMsg) ->
|
||||
logic_ticket:report_error(ErrorMsg, <<"stack">>, #{}).
|
||||
|
||||
%% --- Тесты ---
|
||||
|
||||
test_report_error() ->
|
||||
{ok, Ticket} = logic_ticket:report_error(<<"Test error">>, <<"stack">>, #{}),
|
||||
?assertEqual(<<"Test error">>, Ticket#ticket.error_message),
|
||||
?assertEqual(1, Ticket#ticket.count),
|
||||
{ok, Ticket} = report(<<"Error1">>),
|
||||
?assertEqual(<<"Error1">>, Ticket#ticket.error_message),
|
||||
?assertEqual(1, Ticket#ticket.count).
|
||||
|
||||
{ok, Ticket2} = logic_ticket:report_error(<<"Test error">>, <<"stack">>, #{}),
|
||||
?assertEqual(2, Ticket2#ticket.count).
|
||||
test_report_duplicate() ->
|
||||
{ok, T1} = report(<<"Dup">>),
|
||||
?assertEqual(1, T1#ticket.count),
|
||||
{ok, T2} = report(<<"Dup">>),
|
||||
?assertEqual(2, T2#ticket.count),
|
||||
% Проверяем, что это тот же тикет, а не новый
|
||||
?assertEqual(T1#ticket.id, T2#ticket.id).
|
||||
|
||||
test_list_tickets_admin_only() ->
|
||||
AdminId = create_test_user(admin),
|
||||
UserId = create_test_user(user),
|
||||
test_list_tickets() ->
|
||||
{ok, _} = report(<<"E1">>),
|
||||
Tickets = logic_ticket:list_tickets(<<"admin1">>),
|
||||
?assert(length(Tickets) =:= 1).
|
||||
|
||||
{ok, _} = logic_ticket:report_error(<<"E1">>, <<"">>, #{}),
|
||||
{ok, _} = logic_ticket:report_error(<<"E2">>, <<"">>, #{}),
|
||||
|
||||
{ok, Tickets} = logic_ticket:list_tickets(AdminId),
|
||||
?assertEqual(2, length(Tickets)),
|
||||
|
||||
{error, access_denied} = logic_ticket:list_tickets(UserId).
|
||||
test_list_tickets_forbidden() ->
|
||||
{error, access_denied} = logic_ticket:list_tickets(<<"user1">>).
|
||||
|
||||
test_update_status() ->
|
||||
AdminId = create_test_user(admin),
|
||||
UserId = create_test_user(user),
|
||||
{ok, Ticket} = logic_ticket:report_error(<<"Error">>, <<"">>, #{}),
|
||||
|
||||
{ok, Updated} = logic_ticket:update_status(AdminId, Ticket#ticket.id, in_progress),
|
||||
?assertEqual(in_progress, Updated#ticket.status),
|
||||
|
||||
{error, access_denied} = logic_ticket:update_status(UserId, Ticket#ticket.id, resolved).
|
||||
{ok, Ticket} = report(<<"E2">>),
|
||||
{ok, Updated} = logic_ticket:update_status(<<"admin1">>, Ticket#ticket.id, <<"closed">>),
|
||||
?assertEqual(closed, Updated#ticket.status).
|
||||
|
||||
test_assign_ticket() ->
|
||||
AdminId = create_test_user(admin),
|
||||
AssignToId = create_test_user(admin),
|
||||
{ok, Ticket} = logic_ticket:report_error(<<"Error">>, <<"">>, #{}),
|
||||
|
||||
{ok, Assigned} = logic_ticket:assign_ticket(AdminId, Ticket#ticket.id, AssignToId),
|
||||
?assertEqual(AssignToId, Assigned#ticket.assigned_to),
|
||||
?assertEqual(in_progress, Assigned#ticket.status).
|
||||
{ok, Ticket} = report(<<"E3">>),
|
||||
{ok, Updated} = logic_ticket:assign_ticket(<<"admin1">>, Ticket#ticket.id, <<"dev1">>),
|
||||
?assertEqual(<<"dev1">>, Updated#ticket.assigned_to).
|
||||
|
||||
test_resolve_ticket() ->
|
||||
AdminId = create_test_user(admin),
|
||||
{ok, Ticket} = logic_ticket:report_error(<<"Error">>, <<"">>, #{}),
|
||||
|
||||
{ok, Resolved} = logic_ticket:resolve_ticket(AdminId, Ticket#ticket.id, <<"Fixed">>),
|
||||
?assertEqual(<<"Fixed">>, Resolved#ticket.resolution_note),
|
||||
?assertEqual(resolved, Resolved#ticket.status).
|
||||
{ok, Ticket} = report(<<"E4">>),
|
||||
{ok, Updated} = logic_ticket:resolve_ticket(<<"admin1">>, Ticket#ticket.id, <<"Fixed">>),
|
||||
?assertEqual(closed, Updated#ticket.status),
|
||||
?assertEqual(<<"Fixed">>, Updated#ticket.resolution_note).
|
||||
|
||||
test_close_ticket() ->
|
||||
AdminId = create_test_user(admin),
|
||||
{ok, Ticket} = logic_ticket:report_error(<<"Error">>, <<"">>, #{}),
|
||||
|
||||
{ok, Closed} = logic_ticket:close_ticket(AdminId, Ticket#ticket.id),
|
||||
?assertEqual(closed, Closed#ticket.status).
|
||||
{ok, Ticket} = report(<<"E5">>),
|
||||
{ok, Updated} = logic_ticket:close_ticket(<<"admin1">>, Ticket#ticket.id),
|
||||
?assertEqual(closed, Updated#ticket.status).
|
||||
|
||||
test_get_statistics() ->
|
||||
AdminId = create_test_user(admin),
|
||||
|
||||
{ok, _} = logic_ticket:report_error(<<"E1">>, <<"">>, #{}),
|
||||
{ok, _} = logic_ticket:report_error(<<"E2">>, <<"">>, #{}),
|
||||
{ok, T3} = logic_ticket:report_error(<<"E3">>, <<"">>, #{}),
|
||||
|
||||
logic_ticket:update_status(AdminId, T3#ticket.id, resolved),
|
||||
|
||||
Stats = logic_ticket:get_statistics(AdminId),
|
||||
{ok, _} = report(<<"E6">>),
|
||||
{ok, _} = report(<<"E7">>),
|
||||
{ok, T3} = report(<<"E8">>),
|
||||
logic_ticket:close_ticket(<<"admin1">>, T3#ticket.id),
|
||||
Stats = logic_ticket:get_statistics(<<"admin1">>),
|
||||
?assertEqual(3, maps:get(total_tickets, Stats)),
|
||||
?assertEqual(2, maps:get(open, Stats)),
|
||||
?assertEqual(1, maps:get(resolved, Stats)),
|
||||
?assertEqual(1, maps:get(closed, Stats)),
|
||||
?assertEqual(3, maps:get(total_errors, Stats)).
|
||||
Reference in New Issue
Block a user