-module(admin_handler_moderation_tests). -include_lib("eunit/include/eunit.hrl"). -include("records.hrl"). setup() -> ok = meck:new(cowboy_req, [non_strict]), ok = meck:new(handler_auth, [non_strict]), ok = meck:new(core_user, [non_strict]), ok = meck:new(core_calendar, [non_strict]), ok = meck:new(core_event, [non_strict]), ok = meck:new(core_review, [non_strict]), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), ok. cleanup(_) -> meck:unload(core_review), meck:unload(core_event), meck:unload(core_calendar), meck:unload(core_user), meck:unload(handler_auth), meck:unload(cowboy_req). admin_moderation_test_() -> {setup, fun setup/0, fun cleanup/1, [ {"Freeze calendar – success", fun test_freeze_calendar/0}, {"Freeze calendar – not found", fun test_freeze_calendar_not_found/0}, {"Unfreeze calendar – success", fun test_unfreeze_calendar/0}, {"Freeze event – success", fun test_freeze_event/0}, {"Unfreeze event – success", fun test_unfreeze_event/0}, {"Hide review – success", fun test_hide_review/0}, {"Show review – success", fun test_show_review/0}, {"Block user – success", fun test_block_user/0}, {"Unblock user – success", fun test_unblock_user/0}, {"Invalid target type", fun test_invalid_target/0}, {"Invalid action", fun test_invalid_action/0}, {"Missing action field", fun test_missing_action/0}, {"Forbidden – non admin", fun test_forbidden/0}, {"Wrong method – POST", fun test_wrong_method/0} ]}. %% ── Вспомогательные функции ────────────────────────────── prepare_req(TargetType, TargetId, Action) -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end), 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, binding, fun(target_type, _) -> TargetType; (id, _) -> TargetId end), ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(#{<<"action">> => Action}), Req} end). %% ── Календари ─────────────────────────────────────────── test_freeze_calendar() -> prepare_req(<<"calendar">>, <<"c1">>, <<"freeze">>), Frozen = #calendar{id = <<"c1">>, title = <<"Test">>, status = frozen}, ok = meck:expect(core_calendar, freeze, fun(<<"c1">>) -> {ok, Frozen} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"frozen">>} = jsx:decode(RespBody, [return_maps]). test_freeze_calendar_not_found() -> prepare_req(<<"calendar">>, <<"c99">>, <<"freeze">>), ok = meck:expect(core_calendar, freeze, fun(_) -> {error, not_found} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, _, _} = erase(test_reply), ?assertEqual(404, Status). test_unfreeze_calendar() -> prepare_req(<<"calendar">>, <<"c1">>, <<"unfreeze">>), Unfrozen = #calendar{id = <<"c1">>, title = <<"Test">>, status = active}, ok = meck:expect(core_calendar, unfreeze, fun(<<"c1">>) -> {ok, Unfrozen} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"active">>} = jsx:decode(RespBody, [return_maps]). %% ── События ───────────────────────────────────────────── test_freeze_event() -> prepare_req(<<"event">>, <<"e1">>, <<"freeze">>), FrozenE = #event{id = <<"e1">>, title = <<"Event1">>, status = frozen}, ok = meck:expect(core_event, freeze, fun(<<"e1">>) -> {ok, FrozenE} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"frozen">>} = jsx:decode(RespBody, [return_maps]). test_unfreeze_event() -> prepare_req(<<"event">>, <<"e1">>, <<"unfreeze">>), UnfrozenE = #event{id = <<"e1">>, title = <<"Event1">>, status = active}, ok = meck:expect(core_event, unfreeze, fun(<<"e1">>) -> {ok, UnfrozenE} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"active">>} = jsx:decode(RespBody, [return_maps]). %% ── Отзывы ────────────────────────────────────────────── test_hide_review() -> prepare_req(<<"review">>, <<"r1">>, <<"hide">>), Hidden = #review{id = <<"r1">>, status = hidden}, ok = meck:expect(core_review, hide, fun(<<"r1">>) -> {ok, Hidden} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"hidden">>} = jsx:decode(RespBody, [return_maps]). test_show_review() -> prepare_req(<<"review">>, <<"r1">>, <<"show">>), Visible = #review{id = <<"r1">>, status = active}, ok = meck:expect(core_review, show, fun(<<"r1">>) -> {ok, Visible} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"active">>} = jsx:decode(RespBody, [return_maps]). %% ── Пользователи ──────────────────────────────────────── test_block_user() -> prepare_req(<<"user">>, <<"u1">>, <<"block">>), Blocked = #user{id = <<"u1">>, email = <<"user@test.com">>, status = frozen}, ok = meck:expect(core_user, block, fun(<<"u1">>) -> {ok, Blocked} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"frozen">>} = jsx:decode(RespBody, [return_maps]). test_unblock_user() -> prepare_req(<<"user">>, <<"u1">>, <<"unblock">>), Unblocked = #user{id = <<"u1">>, email = <<"user@test.com">>, status = active}, ok = meck:expect(core_user, unblock, fun(<<"u1">>) -> {ok, Unblocked} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"active">>} = jsx:decode(RespBody, [return_maps]). %% ── Ошибки ────────────────────────────────────────────── test_invalid_target() -> prepare_req(<<"bad_type">>, <<"x">>, <<"freeze">>), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, _, _} = erase(test_reply), ?assertEqual(400, Status). test_invalid_action() -> prepare_req(<<"calendar">>, <<"c1">>, <<"delete">>), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, _, _} = erase(test_reply), ?assertEqual(400, Status). test_missing_action() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end), 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, binding, fun(target_type, _) -> <<"calendar">>; (id, _) -> <<"c1">> end), ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(#{<<"other">> => <<"data">>}), Req} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, _, _} = erase(test_reply), ?assertEqual(400, Status). test_forbidden() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {error, 403, <<"Admin access required">>, Req} end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, _, _} = erase(test_reply), ?assertEqual(403, Status). test_wrong_method() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"POST">> end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_moderation:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(405, Status), #{<<"error">> := <<"Method not allowed">>} = jsx:decode(RespBody, [return_maps]).