101 lines
3.8 KiB
C#
101 lines
3.8 KiB
C#
using System.Net.Http.Json;
|
||
using System.Text.Json;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using Microsoft.AspNetCore.Mvc.Testing;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Vanguard.Server.Api;
|
||
using Xunit.Sdk;
|
||
|
||
namespace Vanguard.Server.Tests;
|
||
|
||
/// <summary>
|
||
/// Полный матч через HTTP + WebSocket с включённым Orleans: проверяет не
|
||
/// только грану, но и связку registry → adapter → endpoint → silo.
|
||
/// </summary>
|
||
[Collection("Orleans")]
|
||
public class OrleansServerE2ETests
|
||
{
|
||
[Fact]
|
||
public async Task Two_Clients_Play_A_Full_Match_With_Orleans_Enabled()
|
||
{
|
||
using var factory = NewFactory();
|
||
using var http = factory.CreateClient();
|
||
var deck = ServerTestData.BuildDeck();
|
||
|
||
var firstQueue = await EnqueueAsync(http, "p1", deck);
|
||
var secondQueue = await EnqueueAsync(http, "p2", deck);
|
||
|
||
var firstStatus = await WaitForMatchAsync(http, firstQueue.QueueId, "p1");
|
||
var secondStatus = await WaitForMatchAsync(http, secondQueue.QueueId, "p2");
|
||
|
||
Assert.Equal(firstStatus.MatchId, secondStatus.MatchId);
|
||
|
||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(120));
|
||
|
||
await using var first = await TestGameClient.ConnectAsync(
|
||
factory, firstStatus.MatchId!, firstStatus.Ticket!, "p1", 0, cts.Token);
|
||
|
||
await using var second = await TestGameClient.ConnectAsync(
|
||
factory, secondStatus.MatchId!, secondStatus.Ticket!, "p2", 0, cts.Token);
|
||
|
||
await Task.WhenAll(
|
||
first.PlayRandomlyAsync(seed: 11, cts.Token),
|
||
second.PlayRandomlyAsync(seed: 22, cts.Token));
|
||
|
||
Assert.True(first.View.IsFinished, "Первый клиент не увидел завершение партии.");
|
||
Assert.True(second.View.IsFinished, "Второй клиент не увидел завершение партии.");
|
||
Assert.Equal(first.View.Winner, second.View.Winner);
|
||
|
||
var health = await http.GetFromJsonAsync<JsonElement>("/health");
|
||
Assert.True(health.GetProperty("matches").GetInt32() >= 1);
|
||
}
|
||
|
||
private static WebApplicationFactory<Program> NewFactory() =>
|
||
new WebApplicationFactory<Program>().WithWebHostBuilder(builder =>
|
||
builder.ConfigureAppConfiguration((_, config) =>
|
||
config.AddInMemoryCollection(new Dictionary<string, string?>
|
||
{
|
||
["Orleans:Enabled"] = "true",
|
||
})));
|
||
|
||
private static async Task<QueueAccepted> EnqueueAsync(
|
||
HttpClient http,
|
||
string playerId,
|
||
List<string> deck)
|
||
{
|
||
var response = await http.PostAsJsonAsync(
|
||
"/api/game/queue",
|
||
new QueueRequest(playerId, "classic-ranked", deck));
|
||
|
||
if (!response.IsSuccessStatusCode)
|
||
throw new XunitException(await response.Content.ReadAsStringAsync());
|
||
|
||
return (await response.Content.ReadFromJsonAsync<QueueAccepted>())!;
|
||
}
|
||
|
||
private static async Task<QueueStatusResponse> WaitForMatchAsync(
|
||
HttpClient http,
|
||
Guid queueId,
|
||
string playerId)
|
||
{
|
||
for (var attempt = 0; attempt < 200; attempt++)
|
||
{
|
||
var status = await http.GetFromJsonAsync<QueueStatusResponse>(
|
||
$"/api/game/queue/{queueId}?playerId={playerId}");
|
||
|
||
if (status is null)
|
||
throw new XunitException("Очередь исчезла.");
|
||
|
||
if (status.Status == "matched")
|
||
return status;
|
||
|
||
if (status.Status is "failed" or "expired" or "cancelled")
|
||
throw new XunitException($"Очередь в состоянии {status.Status}: {status.Reason}");
|
||
|
||
await Task.Delay(50);
|
||
}
|
||
|
||
throw new XunitException("Матч не был найден за 10 секунд.");
|
||
}
|
||
}
|