Files
EventHubBack/test/api/users/user_tickets_tests.erl
T

186 lines
8.1 KiB
Erlang
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
%%%-------------------------------------------------------------------
%%% @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">>),
PrimaryMsg = api_test_runner:unique_ticket_message(<<"Something broke">>),
DedupeMsg = api_test_runner:unique_ticket_message(<<"Dedupe me">>),
% Создаём тикет
#{<<"id">> := TicketId} = api_test_runner:client_post(<<"/v1/tickets">>, Token,
#{error_message => PrimaryMsg, stacktrace => <<"line 42">>}),
test_create_ticket(Token, api_test_runner:unique_ticket_message(<<"Test bug">>)),
test_create_ticket_dedupe(Token, DedupeMsg),
test_create_ticket_manual(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, PrimaryMsg),
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(), binary()) -> ok.
test_create_ticket(Token, ErrorMessage) ->
ct:pal(" TEST: Create a ticket"),
Resp = api_test_runner:client_request(post, <<"/v1/tickets">>, Token,
jsx:encode(#{
error_message => ErrorMessage,
stacktrace => <<"trace">>,
source => <<"frontend">>,
context => #{route => <<"/test">>, build => <<"dev">>}
})),
{ok, 201, _, Body} = Resp,
Decoded = jsx:decode(list_to_binary(Body), [return_maps]),
#{<<"id">> := Id, <<"status">> := Status, <<"source">> := Source, <<"error_hash">> := Hash} = Decoded,
?assert(is_binary(Id)),
?assertEqual(<<"open">>, Status),
?assertEqual(<<"frontend">>, Source),
?assert(is_binary(Hash)),
?assert(byte_size(Hash) > 0),
ct:pal(" OK: ticket ~s created", [Id]).
%% @doc Повторный POST с тем же сообщением увеличивает count.
-spec test_create_ticket_dedupe(binary(), binary()) -> ok.
test_create_ticket_dedupe(Token, ErrorMessage) ->
ct:pal(" TEST: Dedupe ticket by hash"),
Payload = jsx:encode(#{
error_message => ErrorMessage,
stacktrace => <<"same stack">>,
source => <<"frontend">>
}),
{ok, 201, _, Body1} = api_test_runner:client_request(post, <<"/v1/tickets">>, Token, Payload),
#{<<"id">> := Id1, <<"count">> := Count1} = jsx:decode(list_to_binary(Body1), [return_maps]),
{ok, 201, _, Body2} = api_test_runner:client_request(post, <<"/v1/tickets">>, Token, Payload),
#{<<"id">> := Id2, <<"count">> := Count2} = jsx:decode(list_to_binary(Body2), [return_maps]),
?assertEqual(Id1, Id2),
?assertEqual(Count1 + 1, Count2),
ct:pal(" OK: count bumped to ~p", [Count2]).
%% @doc Ручной клиентский репорт: source=manual без stacktrace.
-spec test_create_ticket_manual(binary()) -> ok.
test_create_ticket_manual(Token) ->
ct:pal(" TEST: Create manual ticket"),
Resp = api_test_runner:client_request(post, <<"/v1/tickets">>, Token,
jsx:encode(#{
error_message => api_test_runner:unique_ticket_message(<<"Button does nothing">>),
source => <<"manual">>,
context => #{steps => <<"1. Open calendar\n2. Click share">>}
})),
{ok, 201, _, Body} = Resp,
#{<<"source">> := Source, <<"error_hash">> := Hash} =
jsx:decode(list_to_binary(Body), [return_maps]),
?assertEqual(<<"manual">>, Source),
?assert(is_binary(Hash)),
?assert(byte_size(Hash) > 0),
ct:pal(" OK: manual ticket created").
%% @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(), binary()) -> ok.
test_get_ticket(Token, TicketId, ExpectedMessage) ->
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(ExpectedMessage, 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").