using System.Net; using System.Net.Http.Json; using System.Text.Json; using Azaion.E2E.Helpers; using FluentAssertions; using Xunit; namespace Azaion.E2E.Tests; [Collection("E2E")] public sealed class HardwareBindingTests { private static readonly JsonSerializerOptions ResponseJsonOptions = new() { PropertyNameCaseInsensitive = true }; private const string TestUserPassword = "TestPass1234"; private const string SampleHardware = "CPU: TestCPU. GPU: TestGPU. Memory: 16384. DriveSerial: TESTDRIVE001."; private sealed record ErrorResponse(int ErrorCode, string Message); private readonly TestFixture _fixture; public HardwareBindingTests(TestFixture fixture) => _fixture = fixture; [Fact] public async Task First_hardware_check_binds_and_returns_200_true() { // Arrange string? email = null; try { var candidateEmail = $"hwtest-{Guid.NewGuid()}@azaion.com"; using (var adminClient = _fixture.CreateAuthenticatedClient(_fixture.AdminToken)) { using var createResp = await adminClient.PostAsync("/users", new { Email = candidateEmail, Password = TestUserPassword, Role = 10 }); createResp.EnsureSuccessStatusCode(); } email = candidateEmail; using var loginClient = _fixture.CreateApiClient(); var userToken = await loginClient.LoginAsync(email, TestUserPassword); using var userClient = _fixture.CreateAuthenticatedClient(userToken); // Act using var response = await userClient.PostAsync("/resources/check", new { Hardware = SampleHardware }); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var body = await response.Content.ReadFromJsonAsync(ResponseJsonOptions); body.Should().BeTrue(); } finally { if (email is not null) { using var adminCleanup = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); using var del = await adminCleanup.DeleteAsync($"/users/{Uri.EscapeDataString(email)}"); del.EnsureSuccessStatusCode(); } } } [Fact] public async Task Repeat_hardware_check_with_same_hardware_returns_200_true() { // Arrange string? email = null; try { var candidateEmail = $"hwtest-{Guid.NewGuid()}@azaion.com"; using (var adminClient = _fixture.CreateAuthenticatedClient(_fixture.AdminToken)) { using var createResp = await adminClient.PostAsync("/users", new { Email = candidateEmail, Password = TestUserPassword, Role = 10 }); createResp.EnsureSuccessStatusCode(); } email = candidateEmail; using var loginClient = _fixture.CreateApiClient(); var userToken = await loginClient.LoginAsync(email, TestUserPassword); using var userClient = _fixture.CreateAuthenticatedClient(userToken); using (var first = await userClient.PostAsync("/resources/check", new { Hardware = SampleHardware })) { first.EnsureSuccessStatusCode(); } // Act using var response = await userClient.PostAsync("/resources/check", new { Hardware = SampleHardware }); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var body = await response.Content.ReadFromJsonAsync(ResponseJsonOptions); body.Should().BeTrue(); } finally { if (email is not null) { using var adminCleanup = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); using var del = await adminCleanup.DeleteAsync($"/users/{Uri.EscapeDataString(email)}"); del.EnsureSuccessStatusCode(); } } } [Fact] public async Task Hardware_mismatch_returns_409_with_error_code_40() { // Arrange const string hardwareA = "HARDWARE_A"; const string hardwareB = "HARDWARE_B"; string? email = null; try { var candidateEmail = $"hwtest-{Guid.NewGuid()}@azaion.com"; using (var adminClient = _fixture.CreateAuthenticatedClient(_fixture.AdminToken)) { using var createResp = await adminClient.PostAsync("/users", new { Email = candidateEmail, Password = TestUserPassword, Role = 10 }); createResp.EnsureSuccessStatusCode(); } email = candidateEmail; using var loginClient = _fixture.CreateApiClient(); var userToken = await loginClient.LoginAsync(email, TestUserPassword); using var userClient = _fixture.CreateAuthenticatedClient(userToken); using (var bind = await userClient.PostAsync("/resources/check", new { Hardware = hardwareA })) { bind.EnsureSuccessStatusCode(); } // Act using var response = await userClient.PostAsync("/resources/check", new { Hardware = hardwareB }); // Assert response.StatusCode.Should().Be(HttpStatusCode.Conflict); var err = await response.Content.ReadFromJsonAsync(ResponseJsonOptions); err.Should().NotBeNull(); err!.ErrorCode.Should().Be(40); } finally { if (email is not null) { using var adminCleanup = _fixture.CreateAuthenticatedClient(_fixture.AdminToken); using var del = await adminCleanup.DeleteAsync($"/users/{Uri.EscapeDataString(email)}"); del.EnsureSuccessStatusCode(); } } } }