%%%------------------------------------------------------------------- %%% @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 Регистрирует пользователя, подтверждает email через админку %% и возвращает результат логина (с refresh_token). -spec register_and_get_refresh(binary(), binary()) -> map(). register_and_get_refresh(Email, Password) -> % Регистрируем пользователя {ok, 201, _, RegBody} = api_test_runner:client_request(post, <<"/v1/register">>, <<>>, jsx:encode(#{email => Email, password => Password})), #{<<"user">> := #{<<"id">> := UserId}} = jsx:decode(list_to_binary(RegBody), [return_maps]), % Верифицируем аккаунт (получаем токен через админку и подтверждаем) AdminToken = api_test_runner:get_admin_token(), api_test_runner:verify_user(AdminToken, UserId), % Логинимся, чтобы получить access_token и 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]).