using System.Net; using System.Net.Http.Json; using System.Text.Json; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; /// /// Tests for barcode-assisted batch creation via cover sheet codes. /// [Collection("Database")] public class CoverSheetBatchTests : IAsyncLifetime { private readonly ApiFixture _fixture; private HttpClient _intakeClient = null!; public CoverSheetBatchTests(ApiFixture fixture) => _fixture = fixture; public async Task InitializeAsync() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await DbResetHelper.ResetAsync(db); await DataSeeder.SeedAsync(db); _intakeClient = await AuthHelper.LoginAsync(_fixture, "intake1"); } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task Create_WithCoverSheetCode_CreatesBatchAndRedeemsCoverSheet() { var code = await GenerateCoverSheetCodeAsync("VITALS_SHEET", "BACKFILL"); var response = await UploadWithCoverSheetAsync(code); response.StatusCode.Should().Be(HttpStatusCode.Created); var body = await response.Content.ReadFromJsonAsync(); var batchId = body.GetProperty("data").GetProperty("id").GetGuid(); body.GetProperty("data").GetProperty("batchType").GetString() .Should().Be("VITALS_SHEET"); body.GetProperty("data").GetProperty("track").GetString() .Should().Be("BACKFILL"); using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var sheet = await db.CoverSheets.FirstAsync(c => c.Code == code); sheet.IsUsed.Should().BeTrue(); sheet.BatchId.Should().Be(batchId); sheet.UsedAt.Should().NotBeNull(); } [Fact] public async Task Create_WithUsedCoverSheetCode_Returns409() { var code = await GenerateCoverSheetCodeAsync("VITALS_SHEET", "BACKFILL"); var first = await UploadWithCoverSheetAsync(code); first.EnsureSuccessStatusCode(); var second = await UploadWithCoverSheetAsync(code); second.StatusCode.Should().Be(HttpStatusCode.Conflict); var body = await second.Content.ReadFromJsonAsync(); body.GetProperty("error").GetProperty("code").GetString() .Should().Be("COVER_SHEET_ALREADY_USED"); } [Fact] public async Task Create_WithUnknownCoverSheetCode_Returns404() { var response = await UploadWithCoverSheetAsync("VCR-CS-DEADBEEF"); response.StatusCode.Should().Be(HttpStatusCode.NotFound); var body = await response.Content.ReadFromJsonAsync(); body.GetProperty("error").GetProperty("code").GetString() .Should().Be("COVER_SHEET_NOT_FOUND"); } [Fact] public async Task Create_WithCoverSheetPreAssigned_AutoAssignsBatch() { Guid entryUserId; using (var scope = _fixture.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); entryUserId = await BatchSeedHelper.UserIdAsync(db, "entry1"); } var code = await GenerateCoverSheetCodeAsync( "LAB_RESULTS", "BACKFILL", assignToUserId: entryUserId); var response = await UploadWithCoverSheetAsync(code); response.EnsureSuccessStatusCode(); var body = await response.Content.ReadFromJsonAsync(); body.GetProperty("data").GetProperty("batchType").GetString() .Should().Be("LAB_RESULTS"); body.GetProperty("data").GetProperty("enteredByUserId").GetGuid() .Should().Be(entryUserId); body.GetProperty("data").GetProperty("status").GetString() .Should().Be("IN_ENTRY"); } private async Task GenerateCoverSheetCodeAsync( string batchType, string track, Guid? assignToUserId = null) { var payload = new { count = 1, batchType, track, assignToUserId }; var response = await _intakeClient.PostAsJsonAsync("/api/v1/cover-sheets/generate", payload); response.EnsureSuccessStatusCode(); var body = await response.Content.ReadFromJsonAsync(); return body.GetProperty("data")[0].GetProperty("code").GetString()!; } private async Task UploadWithCoverSheetAsync(string coverSheetCode) { var fileContent = new ByteArrayContent( System.Text.Encoding.ASCII.GetBytes( $"%PDF-1.4\n%%EOF\n%test-{Guid.NewGuid()}")); fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf"); var formData = new MultipartFormDataContent { { fileContent, "file", "test.pdf" }, { new StringContent(coverSheetCode), "coverSheetCode" } }; return await _intakeClient.PostAsync("/api/v1/digitization-batches", formData); } }