using Campaign_Tracker.Server.ExtensionData; using Campaign_Tracker.Server.LegacyData; using Campaign_Tracker.Server.LegacyData.Models; namespace Campaign_Tracker.Server.Tests; public sealed class LegacyLinkIntegrityServiceTests { private static readonly DateTimeOffset FixedNow = new(2026, 5, 6, 12, 0, 0, TimeSpan.Zero); private static LegacyLinkIntegrityService BuildSut( ILegacyDataAccess data, params ILegacyLinkedRecordProvider[] providers) { var validator = new LegacyLinkValidator(data); var time = new FakeTimeProvider(FixedNow); return new LegacyLinkIntegrityService(providers, validator, time); } // ── AC #4 — nightly integrity check with no extension records ──────────── [Fact] public async Task CheckAsync_NoProviders_ReturnsFullyConsistentReport_AC4() { var sut = BuildSut(new InMemoryLegacyDataAccess()); var report = await sut.CheckAsync(); Assert.True(report.IsConsistent); Assert.Equal(0, report.TotalRecords); Assert.Equal(0, report.FailedRecords); Assert.Equal(100.0, report.ConsistencyPercentage); Assert.Empty(report.Failures); Assert.Equal(FixedNow, report.CheckedAt); } // ── AC #4 — consistent records produce clean report ────────────────────── [Fact] public async Task CheckAsync_AllValidLinks_Reports100PercentConsistency_AC4() { var data = new InMemoryLegacyDataAccess( jurisdictions: [new("FAIR01", "Fairview", null, null, null, null)]); var provider = new StubProvider( [ new StubRecord("MunicipalityProfile", "mp-001", LegacyLinkReference.ForJurisdiction("FAIR01")), new StubRecord("MunicipalityProfile", "mp-002", LegacyLinkReference.ForJurisdiction("FAIR01")), ]); var sut = BuildSut(data, provider); var report = await sut.CheckAsync(); Assert.True(report.IsConsistent); Assert.Equal(2, report.TotalRecords); Assert.Equal(2, report.ConsistentRecords); Assert.Equal(0, report.FailedRecords); Assert.Equal(100.0, report.ConsistencyPercentage); Assert.Empty(report.Failures); } // ── AC #4 — failing links are flagged with descriptive reason ──────────── [Fact] public async Task CheckAsync_InvalidLink_FlagsRecordWithReason_AC4() { var data = new InMemoryLegacyDataAccess(jurisdictions: []); var provider = new StubProvider( [ new StubRecord("MunicipalityProfile", "mp-ghost", LegacyLinkReference.ForJurisdiction("GONE01")), ]); var sut = BuildSut(data, provider); var report = await sut.CheckAsync(); Assert.False(report.IsConsistent); Assert.Equal(1, report.TotalRecords); Assert.Equal(0, report.ConsistentRecords); Assert.Equal(1, report.FailedRecords); Assert.Single(report.Failures); var failure = report.Failures[0]; Assert.Equal("MunicipalityProfile", failure.RecordType); Assert.Equal("mp-ghost", failure.RecordId); Assert.Equal(LegacyLinkType.JurisdictionJCode, failure.Reference.Type); Assert.Equal("GONE01", failure.Reference.Value); Assert.Contains("GONE01", failure.Reason); } [Fact] public async Task CheckAsync_MixedValidity_ReportsCorrectConsistencyPercentage_AC4() { var data = new InMemoryLegacyDataAccess( jurisdictions: [new("GOOD01", "Good", null, null, null, null)]); var provider = new StubProvider( [ new StubRecord("MunicipalityProfile", "mp-good", LegacyLinkReference.ForJurisdiction("GOOD01")), new StubRecord("MunicipalityProfile", "mp-bad", LegacyLinkReference.ForJurisdiction("MISSING")), ]); var sut = BuildSut(data, provider); var report = await sut.CheckAsync(); Assert.False(report.IsConsistent); Assert.Equal(2, report.TotalRecords); Assert.Equal(1, report.ConsistentRecords); Assert.Equal(1, report.FailedRecords); Assert.Equal(50.0, report.ConsistencyPercentage); } // ── AC #4 — multiple providers aggregate correctly ─────────────────────── [Fact] public async Task CheckAsync_MultipleProviders_AggregatesAllRecords_AC4() { var data = new InMemoryLegacyDataAccess( jurisdictions: [new("FAIR01", "Fairview", null, null, null, null)], kits: [new LegacyKit(101, "FAIR01", null, null, null, null, false, false, null, null, null, null, null, null)]); var providerA = new StubProvider( [ new StubRecord("MunicipalityProfile", "mp-001", LegacyLinkReference.ForJurisdiction("FAIR01")), ]); var providerB = new StubProvider( [ new StubRecord("ElectionJob", "ej-001", LegacyLinkReference.ForKit(101)), ]); var sut = BuildSut(data, providerA, providerB); var report = await sut.CheckAsync(); Assert.True(report.IsConsistent); Assert.Equal(2, report.TotalRecords); Assert.Equal(0, report.FailedRecords); } // ── Helpers ─────────────────────────────────────────────────────────────── private sealed class StubRecord(string recordType, string recordId, LegacyLinkReference link) : ILegacyLinkedRecord { public string RecordType => recordType; public string RecordId => recordId; public LegacyLinkReference LegacyLink => link; } private sealed class StubProvider(IReadOnlyList records) : ILegacyLinkedRecordProvider { public Task> GetAllAsync(CancellationToken cancellationToken = default) => Task.FromResult(records); } private sealed class FakeTimeProvider(DateTimeOffset utcNow) : TimeProvider { public override DateTimeOffset GetUtcNow() => utcNow; } }