|
- using System.Net;
- using System.Net.Http.Headers;
- using System.Net.Http.Json;
- using Campaign_Tracker.Server.ElectionCycles;
- using Campaign_Tracker.Server.ExtensionData;
- using Campaign_Tracker.Server.LegacyData;
- using Campaign_Tracker.Server.LegacyData.Models;
- using Campaign_Tracker.Server.Municipalities;
- using Microsoft.Extensions.DependencyInjection;
-
- namespace Campaign_Tracker.Server.Tests;
-
- public sealed class ElectionCycleKanbanReadModelTests
- {
- [Fact]
- public async Task GetKanban_GroupsActiveAssignmentsAndUnassignedMunicipalities_AC1_AC3()
- {
- await using var factory = new AuthIntegrationTestFactory();
- using var client = CreateClient(factory, "client-services");
-
- await CreateProfile(client, "FAIR01", "Fairview Display");
- await CreateProfile(client, "LAKE02", null);
- await CreateProfile(client, "PINE03", null);
-
- var response = await client.GetAsync("/api/election-cycles/kanban");
-
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var board = await response.Content.ReadFromJsonAsync<ElectionCycleKanbanDto>();
- Assert.NotNull(board);
-
- var primary = Assert.Single(board.Lanes, lane => lane.CycleId == "2026-primary");
- Assert.Equal("2026 Primary", primary.CycleName);
- var fairview = Assert.Single(primary.Cards, card => card.JCode == "FAIR01");
- Assert.Equal("Fairview Display", fairview.MunicipalityName);
- Assert.Equal("Ready", fairview.CycleJobStatus);
- Assert.Equal("FAIR01", fairview.LegacyJoinKey);
- Assert.Equal("/election-cycles/jobs/job-fair01-primary", fairview.QuickOpenHref);
-
- var unassigned = Assert.Single(board.Lanes, lane => lane.CycleId == ElectionCycleKanbanReadModel.UnassignedCycleId);
- Assert.Contains(unassigned.Cards, card =>
- card.JCode == "PINE03" &&
- card.CycleName == "Unassigned" &&
- card.CycleJobStatus == "Unassigned");
- }
-
- [Fact]
- public async Task ReadModel_ReturnsOneCardPerMunicipalityCyclePair_AC2()
- {
- var legacy = new InMemoryLegacyDataAccess();
- var profiles = BuildProfileRepository(legacy);
- await profiles.CreateAsync("LAKE02", null, "test@example.test");
- var jobs = new InMemoryElectionCycleJobRepository(
- [
- new ElectionCycleJobAssignment(
- "job-lake-primary",
- "LAKE02",
- "2026-primary",
- "2026 Primary",
- "In progress",
- IsActive: true),
- new ElectionCycleJobAssignment(
- "job-lake-special",
- "LAKE02",
- "2026-special",
- "2026 Special",
- "Blocked",
- IsActive: true),
- ], TimeProvider.System);
- var sut = new ElectionCycleKanbanReadModel(profiles, jobs);
-
- var board = await sut.GetAsync();
-
- var lakeCards = board.Lanes.SelectMany(lane => lane.Cards)
- .Where(card => card.JCode == "LAKE02")
- .ToArray();
- Assert.Equal(2, lakeCards.Length);
- Assert.Contains(lakeCards, card => card.CycleId == "2026-primary");
- Assert.Contains(lakeCards, card => card.CycleId == "2026-special");
- }
-
- [Fact]
- public async Task ReadModel_ExcludesInactiveAssignmentsFromCycleLanes_AC1()
- {
- var legacy = new InMemoryLegacyDataAccess();
- var profiles = BuildProfileRepository(legacy);
- await profiles.CreateAsync("FAIR01", null, "test@example.test");
- var jobs = new InMemoryElectionCycleJobRepository(
- [
- new ElectionCycleJobAssignment(
- "job-fair-old",
- "FAIR01",
- "2024-general",
- "2024 General",
- "Complete",
- IsActive: false),
- ], TimeProvider.System);
- var sut = new ElectionCycleKanbanReadModel(profiles, jobs);
-
- var board = await sut.GetAsync();
-
- Assert.DoesNotContain(board.Lanes, lane => lane.CycleId == "2024-general");
- var unassigned = Assert.Single(board.Lanes, lane => lane.CycleId == ElectionCycleKanbanReadModel.UnassignedCycleId);
- Assert.Contains(unassigned.Cards, card => card.JCode == "FAIR01");
- }
-
- [Fact]
- public async Task GetKanban_RequiresClientServicesRole_AC1()
- {
- await using var factory = new AuthIntegrationTestFactory();
- using var noToken = factory.CreateClient();
- using var production = CreateClient(factory, "production");
-
- var missingToken = await noToken.GetAsync("/api/election-cycles/kanban");
- var wrongRole = await production.GetAsync("/api/election-cycles/kanban");
-
- Assert.Equal(HttpStatusCode.Unauthorized, missingToken.StatusCode);
- Assert.Equal(HttpStatusCode.Forbidden, wrongRole.StatusCode);
- }
-
- [Fact]
- public async Task ReadModel_UsesReadOnlyLegacyAndExtensionRepositories_AC1()
- {
- ILegacyDataAccess legacy = new InMemoryLegacyDataAccess(
- jurisdictions:
- [
- new("FAIR01", "Fairview Borough", "100 Main St", "Fairview, PA 16415", null, null),
- ]);
- var profiles = BuildProfileRepository(legacy);
- await profiles.CreateAsync("FAIR01", null, "test@example.test");
- var jobs = new InMemoryElectionCycleJobRepository([], TimeProvider.System);
- var sut = new ElectionCycleKanbanReadModel(profiles, jobs);
-
- var board = await sut.GetAsync();
-
- Assert.Contains(board.Lanes.Single(lane => lane.CycleId == ElectionCycleKanbanReadModel.UnassignedCycleId).Cards,
- card => card.JCode == "FAIR01");
- Assert.True(typeof(ILegacyDataAccess).GetMethods().All(method =>
- method.Name.StartsWith("Get", StringComparison.Ordinal)));
- }
-
- private static InMemoryMunicipalityProfileRepository BuildProfileRepository(
- ILegacyDataAccess legacy)
- => new(
- new LegacyLinkValidator(legacy),
- legacy,
- TimeProvider.System);
-
- private static HttpClient CreateClient(AuthIntegrationTestFactory factory, string role)
- {
- var client = factory.CreateClient();
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
- "Bearer", AuthIntegrationTestFactory.CreateToken("cs@example.test", role));
- return client;
- }
-
- private static async Task CreateProfile(
- HttpClient client,
- string jCode,
- string? displayName)
- {
- var response = await client.PostAsJsonAsync("/api/municipalities/profiles", new
- {
- jCode,
- displayName,
- });
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
-
- private sealed record ElectionCycleKanbanDto(ElectionCycleKanbanLaneDto[] Lanes);
-
- private sealed record ElectionCycleKanbanLaneDto(
- string CycleId,
- string CycleName,
- ElectionCycleKanbanCardDto[] Cards);
-
- private sealed record ElectionCycleKanbanCardDto(
- string CardId,
- string MunicipalityName,
- string JCode,
- string CycleId,
- string CycleName,
- string CycleJobStatus,
- string LegacyJoinKey,
- string QuickOpenHref);
- }
|