%%%------------------------------------------------------------------- %%% @doc Тесты клиентского API для обновления токена. %%% %%% Покрывает эндпоинты: %%% POST /v1/refresh %%% %%% Проверяет: %%% - успешное обновление токена по валидному refresh_token %%% - ошибку 401 при невалидном refresh_token %%% - ошибку 400 при отсутствии refresh_token в теле запроса %%% @end %%%------------------------------------------------------------------- -module(user_refresh_tests). -include_lib("eunit/include/eunit.hrl"). -export([test/0]). %%%=================================================================== %%% Главная тестовая функция %%%=================================================================== -spec test() -> ok. test() -> ct:pal("=== User Refresh Tests ==="), Token = api_test_runner:get_user_token(), % Получаем refresh_token через логин (или регистрацию) Email = api_test_runner:unique_email(<<"refresh">>), Password = <<"StrongPass1!">>, #{<<"refresh_token">> := RefreshToken} = register_and_get_refresh(Email, Password), test_successful_refresh(RefreshToken), test_invalid_refresh_token(), test_missing_refresh_token(), ct:pal("=== All user refresh tests passed ==="), ok. %%%=================================================================== %%% Тестовые функции %%%=================================================================== %% @doc Успешное обновление: 200 OK, возвращает новую пару токенов. -spec test_successful_refresh(binary()) -> ok. test_successful_refresh(RefreshToken) -> ct:pal(" TEST: Successful token refresh"), Resp = api_test_runner:client_request(post, <<"/v1/refresh">>, <<>>, jsx:encode(#{refresh_token => RefreshToken})), {ok, 200, _, Body} = Resp, #{<<"token">> := NewToken, <<"refresh_token">> := NewRefresh} = jsx:decode(list_to_binary(Body), [return_maps]), ?assert(is_binary(NewToken)), ?assert(is_binary(NewRefresh)), ?assertNotEqual(RefreshToken, NewRefresh), ct:pal(" OK: got new token pair"). %% @doc Невалидный refresh_token: 401 Unauthorized. -spec test_invalid_refresh_token() -> ok. test_invalid_refresh_token() -> ct:pal(" TEST: Invalid refresh token"), Resp = api_test_runner:client_request(post, <<"/v1/refresh">>, <<>>, jsx:encode(#{refresh_token => <<"invalid_token_here">>})), {ok, 401, _, _} = Resp, ct:pal(" OK: got 401"). %% @doc Отсутствие refresh_token в теле: 400 Bad Request. -spec test_missing_refresh_token() -> ok. test_missing_refresh_token() -> ct:pal(" TEST: Missing refresh_token field"), Resp = api_test_runner:client_request(post, <<"/v1/refresh">>, <<>>, jsx:encode(#{})), ?assertMatch({ok, 400, _, _}, Resp), ct:pal(" OK: got 400"). %%%=================================================================== %%% Вспомогательные функции %%%=================================================================== %% @doc Регистрирует пользователя, выполняет логин и возвращает refresh_token. -spec register_and_get_refresh(binary(), binary()) -> map(). register_and_get_refresh(Email, Password) -> % Регистрируем _ = api_test_runner:client_request(post, <<"/v1/register">>, <<>>, jsx:encode(#{email => Email, password => Password})), % Логинимся, чтобы получить refresh_token {ok, 200, _, Body} = api_test_runner:client_request(post, <<"/v1/login">>, <<>>, jsx:encode(#{email => Email, password => Password})), jsx:decode(list_to_binary(Body), [return_maps]).