using Nonemm.Core; namespace Nonemm.Network.Tests; /// The checksum two stations compare during a resync. What matters is that the /// same contacts hash the same whatever order they are handed over in, that a /// change anywhere in a contact changes it, and that the columns each station /// works out for itself do not. public class LogChecksumTests { private static readonly DateTime Start = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc); [Fact] public void TheSameContactsHashTheSameInAnyOrder() { Qso one = Contact("G3XYZ", 0); Qso two = Contact("DL1ABC", 1); Assert.Equal(LogChecksum.Of([one, two]), LogChecksum.Of([two, one])); } /// Two contacts at the same second are ordered by callsign, the way N1MM /// reads them out of the database. [Fact] public void ContactsAtTheSameSecondAreOrderedByCall() { Qso one = Contact("G3XYZ", 0); Qso two = Contact("DL1ABC", 0); Assert.Equal(LogChecksum.Of([one, two]), LogChecksum.Of([two, one])); } [Fact] public void AChangedExchangeChangesTheChecksum() { Qso qso = Contact("G3XYZ", 0); Assert.NotEqual( LogChecksum.Of([qso]), LogChecksum.Of([qso with { ReceivedNumber = 42 }])); } /// Points and the multiplier flags are worked out from the rules at each /// station, and the two are allowed to disagree about them while holding /// the same contact. [Fact] public void ScoringDoesNotChangeTheChecksum() { Qso qso = Contact("G3XYZ", 0); Assert.Equal( LogChecksum.Of([qso]), LogChecksum.Of([qso with { Points = 3, IsMultiplier1 = true, IsMultiplier2 = true }])); } [Fact] public void AnEmptyBlockHashesToTheEmptyChecksum() { Assert.Equal(LogChecksum.Of([]), LogChecksum.Of([])); } /// A block is the contacts of one computer inside one stretch of time. The /// end of the block is not in it, so two blocks side by side neither share /// a contact nor drop one. [Fact] public void ABlockHoldsOneStationsContactsInsideTheHour() { List log = [ Contact("G3XYZ", 0) with { StationName = "RUN-PC" }, Contact("DL1ABC", 30) with { StationName = "RUN-PC" }, Contact("OK1AB", 45) with { StationName = "MULT-PC" }, Contact("F5XY", 60) with { StationName = "RUN-PC" }, ]; IReadOnlyList block = LogChecksum.Block(log, "run-pc", Start, TimeSpan.FromHours(1)); Assert.Equal(["DL1ABC", "G3XYZ"], block.Select(q => q.Call.Text).Order()); } private static Qso Contact(string call, int minutes) => new() { Id = $"{call}-{minutes}", TimestampUtc = Start.AddMinutes(minutes), Call = Callsign.Parse(call), Frequency = Frequency.FromKilohertz(14_025), Mode = Modes.Cw, ContestName = "CQWW", ContestNumber = 2, SentNumber = 41, StationName = "RUN-PC", }; }