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