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); } }