using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using Azaion.Missions.E2E.Fixtures; using Azaion.Missions.E2E.Helpers; using Xunit; namespace Azaion.Missions.E2E.Tests.Vehicles; /// /// FT-N-01..03 — vehicle negative scenarios from /// _docs/02_document/tests/blackbox-tests.md § Negative. /// FT-N-08 (generic 500 redacted body) lives in Tests/Errors because it /// owns its own destructive xUnit collection. /// Traces: AC-1.6 (no-match) / AC-1.7 (404) / AC-1.8 (409 in-use). /// [Collection("Vehicles")] [Trait("Category", "Blackbox")] [Trait("db_access", "seed-or-assert-only")] public sealed class NegativeTests : TestBase, IClassFixture { [Fact] [Trait("Traces", "AC-1.6")] [Trait("max_ms", "2000")] public async Task FT_N_01_filter_no_match_returns_empty_array_for_both_casings() { // Arrange DbResetFixture.ResetDatabase(TestEnvironment.DbSideChannel); Seeds.Apply(Seeds.Three_BR01_BR02_MQ9.Sql); var token = await Tokens.MintDefaultAsync(); async Task> FetchAsync(string query) { using var http = new HttpRequestMessage(HttpMethod.Get, "/vehicles?" + query); http.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Jwt); using var resp = await Missions.SendAsync(http); await HttpAssertions.AssertStatusAsync(resp, HttpStatusCode.OK); return await resp.Content.ReadFromJsonAsync>() ?? throw new InvalidOperationException("body deserialized to null"); } // Act var upper = await FetchAsync("name=ZZ"); var lower = await FetchAsync("name=zz"); // Assert Assert.Empty(upper); Assert.Empty(lower); } [Fact] [Trait("Traces", "AC-1.7,AC-8.2")] [Trait("max_ms", "2000")] public async Task FT_N_02_get_vehicle_returns_404_with_problem_envelope() { // Arrange DbResetFixture.ResetDatabase(TestEnvironment.DbSideChannel); var token = await Tokens.MintDefaultAsync(); var randomId = Guid.NewGuid(); // Act using var http = new HttpRequestMessage(HttpMethod.Get, $"/vehicles/{randomId}"); http.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Jwt); using var response = await Missions.SendAsync(http); // Assert await HttpAssertions.AssertProblemEnvelopeAsync(response, HttpStatusCode.NotFound) ; } [Fact] [Trait("Traces", "AC-1.8,AC-8.5")] [Trait("max_ms", "2000")] public async Task FT_N_03_delete_in_use_vehicle_returns_409_and_row_remains() { // Arrange DbResetFixture.ResetDatabase(TestEnvironment.DbSideChannel); Seeds.Apply(Seeds.OneDefaultVehicle.Sql); var vehicleId = Seeds.OneDefaultVehicle.Id; var missionId = Guid.NewGuid(); Seeds.Apply($""" INSERT INTO missions (id, created_date, name, vehicle_id) VALUES ('{missionId}', '2026-05-14T00:00:00Z', 'in-use', '{vehicleId}'); """); var token = await Tokens.MintDefaultAsync(); // Act using var http = new HttpRequestMessage(HttpMethod.Delete, $"/vehicles/{vehicleId}"); http.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Jwt); using var response = await Missions.SendAsync(http); // Assert await HttpAssertions.AssertProblemEnvelopeAsync(response, HttpStatusCode.Conflict) ; var remaining = DbAssertions.ScalarCount( "SELECT COUNT(*) FROM vehicles WHERE id = @id", ("id", vehicleId)); Assert.Equal(1L, remaining); } }