-module(admin_handler_user_by_id_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. cleanup(_) -> meck:unload(core_user), meck:unload(handler_auth), meck:unload(cowboy_req). admin_user_by_id_test_() -> {setup, fun setup/0, fun cleanup/1, [ {"GET /admin/users/:id – success", fun test_get_user/0}, {"GET /admin/users/:id – not found", fun test_get_user_not_found/0}, {"GET /admin/users/:id – forbidden", fun test_get_user_forbidden/0}, {"PUT /admin/users/:id – success", fun test_update_user/0}, {"PUT /admin/users/:id – not found", fun test_update_user_not_found/0}, {"DELETE /admin/users/:id – success", fun test_delete_user/0}, {"DELETE /admin/users/:id – not found", fun test_delete_user_not_found/0}, {"POST /admin/users/:id – method not allowed", fun test_wrong_method/0}, {"convert_updates/1", fun test_convert_updates/0} ]}. %% ── GET – success ───────────────────────────────────────── test_get_user() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"admin1">>, Req} end), AdminUser = #user{id = <<"admin1">>, role = admin}, ok = meck:expect(core_user, get_by_id, fun(<<"admin1">>) -> {ok, AdminUser}; (<<"user1">>) -> {ok, #user{id = <<"user1">>, email = <<"u@t.com">>, role = user, status = active, created_at = {{2026,4,27},{12,0,0}}, updated_at = {{2026,4,27},{12,0,0}}}} end), ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"user1">> end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"id">> := <<"user1">>, <<"email">> := <<"u@t.com">>, <<"role">> := <<"user">>} = jsx:decode(RespBody, [return_maps]). %% ── GET – not found ─────────────────────────────────────── test_get_user_not_found() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"GET">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"admin1">>, Req} end), AdminUser = #user{id = <<"admin1">>, role = admin}, ok = meck:expect(core_user, get_by_id, fun(<<"admin1">>) -> {ok, AdminUser}; (_) -> {error, not_found} end), ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"missing">> end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(404, Status), #{<<"error">> := <<"User not found">>} = jsx:decode(RespBody, [return_maps]). %% ── GET – forbidden ─────────────────────────────────────── test_get_user_forbidden() -> 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, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(403, Status), #{<<"error">> := <<"Admin access required">>} = jsx:decode(RespBody, [return_maps]). %% ── PUT – success ───────────────────────────────────────── test_update_user() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"admin1">>, Req} end), AdminUser = #user{id = <<"admin1">>, role = admin}, ok = meck:expect(core_user, get_by_id, fun(<<"admin1">>) -> {ok, AdminUser} end), User = #user{id = <<"user1">>, email = <<"u@t.com">>, role = user, status = frozen, created_at = {{2026,4,27},{12,0,0}}, updated_at = {{2026,4,27},{13,0,0}}}, ok = meck:expect(core_user, update, fun(<<"user1">>, _) -> {ok, User} end), ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"user1">> end), ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, jsx:encode(#{status => <<"frozen">>}), Req} end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"frozen">>} = jsx:decode(RespBody, [return_maps]). %% ── PUT – not found ────────────────────────────────────── test_update_user_not_found() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"PUT">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"admin1">>, Req} end), AdminUser = #user{id = <<"admin1">>, role = admin}, ok = meck:expect(core_user, get_by_id, fun(<<"admin1">>) -> {ok, AdminUser} end), ok = meck:expect(core_user, update, fun(_, _) -> {error, not_found} end), ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"missing">> end), ok = meck:expect(cowboy_req, read_body, fun(Req) -> {ok, <<"{}">>, Req} end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(404, Status), #{<<"error">> := <<"User not found">>} = jsx:decode(RespBody, [return_maps]). %% ── DELETE – success ───────────────────────────────────── test_delete_user() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"DELETE">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"admin1">>, Req} end), AdminUser = #user{id = <<"admin1">>, role = admin}, ok = meck:expect(core_user, get_by_id, fun(<<"admin1">>) -> {ok, AdminUser} end), ok = meck:expect(core_user, delete, fun(<<"user1">>) -> {ok, deleted} end), ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"user1">> end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(200, Status), #{<<"status">> := <<"deleted">>} = jsx:decode(RespBody, [return_maps]). %% ── DELETE – not found ─────────────────────────────────── test_delete_user_not_found() -> ok = meck:expect(cowboy_req, method, fun(_) -> <<"DELETE">> end), ok = meck:expect(handler_auth, authenticate, fun(Req) -> {ok, <<"admin1">>, Req} end), AdminUser = #user{id = <<"admin1">>, role = admin}, ok = meck:expect(core_user, get_by_id, fun(<<"admin1">>) -> {ok, AdminUser} end), ok = meck:expect(core_user, delete, fun(_) -> {error, not_found} end), ok = meck:expect(cowboy_req, binding, fun(id, _) -> <<"missing">> end), ok = meck:expect(cowboy_req, reply, fun(Code, Headers, Body, Req) -> put(test_reply, {Code, Headers, Body, Req}) end), {ok, _, _} = admin_handler_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(404, Status), #{<<"error">> := <<"User not found">>} = jsx:decode(RespBody, [return_maps]). %% ── Wrong method ───────────────────────────────────────── 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_user_by_id:init(req, []), {Status, _, RespBody, _} = erase(test_reply), ?assertEqual(405, Status), #{<<"error">> := <<"Method not allowed">>} = jsx:decode(RespBody, [return_maps]). %% ── convert_updates/1 ──────────────────────────────────── test_convert_updates() -> Updates = [ {<<"status">>, <<"frozen">>}, {<<"role">>, <<"admin">>}, {<<"email">>, <<"test@test.com">>} ], Converted = admin_handler_user_by_id:convert_updates(Updates), ?assertEqual({status, frozen}, lists:keyfind(status, 1, Converted)), ?assertEqual({role, admin}, lists:keyfind(role, 1, Converted)), ?assertEqual({<<"email">>, <<"test@test.com">>}, lists:keyfind(<<"email">>, 1, Converted)).