using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using foodmarket.IntegrationTests.Support;
using Xunit;
namespace foodmarket.IntegrationTests;
/// Sprint 13: POST /api/me/sessions/revoke-all гасит
/// все refresh-токены текущего юзера. После вызова попытка обновить
/// access через refresh — 400.
[Collection(ApiCollection.Name)]
public class SessionRevokeTests
{
private readonly ApiFactory _factory;
public SessionRevokeTests(ApiFactory factory) => _factory = factory;
[Fact]
public async Task Refresh_after_revoke_all_fails()
{
var actor = new ApiActor(_factory.CreateClient());
var slug = $"revoke-{Guid.NewGuid():N}";
var email = $"{slug}@example.kz";
const string password = "Passw0rd!";
// Signup + первый login (получаем access + refresh).
(await actor.SignupAsync(email, password, $"RevokeOrg-{slug}")).EnsureSuccessStatusCode();
var (access1, refresh1) = await GetTokenPairAsync(actor.Http, email, password);
access1.Should().NotBeNullOrEmpty();
refresh1.Should().NotBeNullOrEmpty();
// Используем access для revoke-all.
actor.Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", access1);
var revokeResp = await actor.Http.PostAsync("/api/me/sessions/revoke-all", null);
revokeResp.EnsureSuccessStatusCode();
var revokedJson = await revokeResp.Content.ReadFromJsonAsync();
revokedJson.GetProperty("revokedAuthorizations").GetInt32().Should().BeGreaterThan(0,
"при revoke-all должна быть погашена хотя бы одна authorization");
// Попытка использовать ТОТ ЖЕ refresh-токен → 400 invalid_grant.
using var refreshResp = await actor.Http.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary
{
["grant_type"] = "refresh_token",
["refresh_token"] = refresh1!,
["client_id"] = "food-market-web",
["scope"] = "openid profile email roles api offline_access",
}));
((int)refreshResp.StatusCode).Should().BeOneOf(400, 401);
}
private static async Task<(string AccessToken, string? RefreshToken)> GetTokenPairAsync(
HttpClient http, string email, string password)
{
using var resp = await http.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary
{
["grant_type"] = "password",
["username"] = email,
["password"] = password,
["client_id"] = "food-market-web",
["scope"] = "openid profile email roles api offline_access",
}));
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadFromJsonAsync();
var access = json.GetProperty("access_token").GetString()!;
string? refresh = json.TryGetProperty("refresh_token", out var r) ? r.GetString() : null;
return (access, refresh);
}
}