%%%------------------------------------------------------------------- %%% @doc Тесты клиентского API для работы с тикетами. %%% %%% Покрывает эндпоинты: %%% POST /v1/tickets %%% GET /v1/tickets %%% GET /v1/tickets/:id %%% %%% Проверяет: %%% - успешное создание тикета %%% - получение списка своих тикетов %%% - получение конкретного тикета по ID %%% - ошибку 400 при отсутствии обязательных полей %%% - ошибку 401 без токена %%% - ошибку 403 при попытке доступа к чужому тикету %%% @end %%%------------------------------------------------------------------- -module(user_tickets_tests). -include_lib("eunit/include/eunit.hrl"). -export([test/0]). %%%=================================================================== %%% Главная тестовая функция %%%=================================================================== -spec test() -> ok. test() -> ct:pal("=== User Tickets Tests ==="), Token = api_test_runner:get_user_token(), StrangerEmail = api_test_runner:unique_email(<<"stranger">>), StrangerToken = api_test_runner:register_and_login(StrangerEmail, <<"pass">>), % Создаём тикет #{<<"id">> := TicketId} = api_test_runner:client_post(<<"/v1/tickets">>, Token, #{error_message => <<"Something broke">>, stacktrace => <<"line 42">>}), test_create_ticket(Token), test_create_ticket_missing_fields(Token), test_create_ticket_unauthorized(), test_list_tickets(Token, TicketId), test_list_tickets_unauthorized(), test_get_ticket(Token, TicketId), test_get_ticket_forbidden(StrangerToken, TicketId), test_get_ticket_not_found(Token), test_get_ticket_unauthorized(TicketId), ct:pal("=== All user tickets tests passed ==="), ok. %%%=================================================================== %%% Тестовые функции %%%=================================================================== %% @doc Успешное создание тикета: 201 Created. -spec test_create_ticket(binary()) -> ok. test_create_ticket(Token) -> ct:pal(" TEST: Create a ticket"), Resp = api_test_runner:client_request(post, <<"/v1/tickets">>, Token, jsx:encode(#{error_message => <<"Test bug">>, stacktrace => <<"trace">>})), {ok, 201, _, Body} = Resp, #{<<"id">> := Id, <<"status">> := Status} = jsx:decode(list_to_binary(Body), [return_maps]), ?assert(is_binary(Id)), ?assertEqual(<<"open">>, Status), ct:pal(" OK: ticket ~s created", [Id]). %% @doc Отсутствие обязательного поля error_message: 400 Bad Request. -spec test_create_ticket_missing_fields(binary()) -> ok. test_create_ticket_missing_fields(Token) -> ct:pal(" TEST: Create ticket without error_message"), Resp = api_test_runner:client_request(post, <<"/v1/tickets">>, Token, jsx:encode(#{stacktrace => <<"trace">>})), ?assertMatch({ok, 400, _, _}, Resp), ct:pal(" OK: got 400"). %% @doc Создание тикета без токена: 401 Unauthorized. -spec test_create_ticket_unauthorized() -> ok. test_create_ticket_unauthorized() -> ct:pal(" TEST: Create ticket without token"), Resp = api_test_runner:client_request(post, <<"/v1/tickets">>, <<>>, jsx:encode(#{error_message => <<"bug">>})), ?assertMatch({ok, 401, _, _}, Resp), ct:pal(" OK: got 401"). %% @doc GET /v1/tickets – получение списка своих тикетов. -spec test_list_tickets(binary(), binary()) -> ok. test_list_tickets(Token, ExpectedTicketId) -> ct:pal(" TEST: List my tickets"), Tickets = api_test_runner:client_get(<<"/v1/tickets">>, Token), ?assert(is_list(Tickets)), ?assert(length(Tickets) >= 1), ?assert(lists:any(fun(T) -> maps:get(<<"id">>, T) =:= ExpectedTicketId end, Tickets)), ct:pal(" OK: my ticket found"). %% @doc GET /v1/tickets без токена: 401 Unauthorized. -spec test_list_tickets_unauthorized() -> ok. test_list_tickets_unauthorized() -> ct:pal(" TEST: List tickets without token"), Resp = api_test_runner:client_request(get, <<"/v1/tickets">>, <<>>), ?assertMatch({ok, 401, _, _}, Resp), ct:pal(" OK: got 401"). %% @doc GET /v1/tickets/:id – получение своего тикета. -spec test_get_ticket(binary(), binary()) -> ok. test_get_ticket(Token, TicketId) -> ct:pal(" TEST: Get my ticket by ID"), Path = <<"/v1/tickets/", TicketId/binary>>, Ticket = api_test_runner:client_get(Path, Token), ?assertEqual(TicketId, maps:get(<<"id">>, Ticket)), ?assertEqual(<<"Something broke">>, maps:get(<<"error_message">>, Ticket)), ct:pal(" OK: got my ticket"). %% @doc GET /v1/tickets/:id – попытка доступа к чужому тикету (403). -spec test_get_ticket_forbidden(binary(), binary()) -> ok. test_get_ticket_forbidden(StrangerToken, TicketId) -> ct:pal(" TEST: Get ticket that is not mine"), Path = <<"/v1/tickets/", TicketId/binary>>, Resp = api_test_runner:client_request(get, Path, StrangerToken), ?assertMatch({ok, 403, _, _}, Resp), ct:pal(" OK: got 403"). %% @doc GET /v1/tickets/:id – несуществующий тикет (404). -spec test_get_ticket_not_found(binary()) -> ok. test_get_ticket_not_found(Token) -> ct:pal(" TEST: Get non-existent ticket"), Resp = api_test_runner:client_request(get, <<"/v1/tickets/fakeid">>, Token), ?assertMatch({ok, 404, _, _}, Resp), ct:pal(" OK: got 404"). %% @doc GET /v1/tickets/:id – без токена (401). -spec test_get_ticket_unauthorized(binary()) -> ok. test_get_ticket_unauthorized(TicketId) -> ct:pal(" TEST: Get ticket without token"), Path = <<"/v1/tickets/", TicketId/binary>>, Resp = api_test_runner:client_request(get, Path, <<>>), ?assertMatch({ok, 401, _, _}, Resp), ct:pal(" OK: got 401").