using Nonemm.Contests; using Nonemm.Contests.Rules; using Nonemm.Core; using Nonemm.Core.Country; using Nonemm.Storage; namespace Nonemm.Session.Tests; public class LoggingSessionTests { private const string Countries = """ Germany: 14: 28: EU: 51.00: -10.00: -1.0: DL: DL,DK,DJ; Japan: 25: 45: AS: 36.40: -138.38: -9.0: JA: JA,JH,JR; """; private static readonly StationInfo Me = new() { Callsign = "DL1ABC", CqZone = 14, ItuZone = 28, Continent = "EU", CountryPrefix = "DL", }; private static LoggingSession Session(Contest? contest = null) { FakeLogStore store = new(); ContestInstance instance = store.AddContest(new ContestInstance { ContestNumber = 0, ContestName = "CQWW", }); return new LoggingSession( store, contest ?? new CqWorldWide(ModeCategory.Cw), instance, Me, CountryFile.Parse(Countries)); } private static void Type(LoggingSession session, string call, string zone) { session.Entry.Call = call; session.Entry.Set(ExchangeSlot.ReceivedReport, "599"); session.Entry.Set(ExchangeSlot.Zone, zone); } [Fact] public void LoggingAContactPutsItInTheLogAndClearsTheEntry() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); Assert.Equal("JA1XYZ", logged.Call.Text); Assert.Equal(25, logged.Zone); Assert.Equal(3, logged.Points); Assert.Equal("", session.Entry.Call); Assert.Single(session.Log.Qsos); } [Fact] public void CountryAndPrefixAreFilledInFromTheCountryFile() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); Assert.Equal("JA", logged.CountryPrefix); Assert.Equal("AS", logged.Continent); Assert.Equal("JA1", logged.WpxPrefix); } [Fact] public void AnIncompleteExchangeIsNotLogged() { LoggingSession session = Session(); session.Entry.Call = "JA1XYZ"; Assert.Throws(() => session.LogContact()); } [Fact] public void TheSameStationOnTheSameBandReadsAsADupe() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); session.LogContact(); Type(session, "JA1XYZ", "25"); Assert.True(session.Verdict()?.IsDupe); } [Fact] public void MovingBandClearsTheDupe() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); session.LogContact(); session.Tune(Frequency.FromKilohertz(7_025)); Type(session, "JA1XYZ", "25"); Assert.False(session.Verdict()?.IsDupe); } [Fact] public void SerialNumbersCountUp() { LoggingSession session = Session(new CqWpx(ModeCategory.Cw)); Assert.Equal(1, session.SentNumber); session.Entry.Call = "JA1XYZ"; session.Entry.Set(ExchangeSlot.ReceivedReport, "599"); session.Entry.Set(ExchangeSlot.SerialNumber, "12"); session.LogContact(); Assert.Equal(2, session.SentNumber); } [Fact] public void SpaceMovesThroughTheBoxesAndBackToTheCall() { LoggingSession session = Session(); Assert.Equal(0, session.Entry.Focus); session.Entry.Advance(); Assert.Equal(1, session.Entry.Focus); session.Entry.Advance(); session.Entry.Advance(); Assert.Equal(0, session.Entry.Focus); } [Theory] [InlineData("14025", 14_025_000)] [InlineData("14.025", 14_025_000)] [InlineData("7025.5", 7_025_500)] public void AFrequencyTypedIntoTheCallBoxIsRecognised(string typed, long hertz) { LoggingSession session = Session(); session.Entry.Call = typed; Assert.Equal(hertz, session.PendingQsy()?.Hertz); } [Theory] [InlineData("JA1XYZ")] [InlineData("12000")] public void ACallOrAnOutOfBandNumberIsNotAQsy(string typed) { LoggingSession session = Session(); session.Entry.Call = typed; Assert.Null(session.PendingQsy()); } [Fact] public void AContactDeletedFromTheLogGivesItsMultiplierBack() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso first = session.LogContact(); Type(session, "JA2XYZ", "25"); session.LogContact(); Assert.Equal(2, session.Log.Tally.TotalMultipliers); session.Delete(first.Id); Assert.Single(session.Log.Qsos); Assert.Equal(2, session.Log.Tally.TotalMultipliers); Assert.True(session.Log.Qsos.Single().IsMultiplier1); } [Fact] public void EditingAContactRescoresTheLog() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); Assert.Equal(3, session.Log.Tally.Points); QsoEdit edit = session.Edit(logged.Id, QsoField.Call, "DL2XYZ"); Assert.True(edit.IsAccepted); Assert.Equal("DL2XYZ", session.Log.Qsos.Single().Call.Text); Assert.Equal(0, session.Log.Tally.Points); } [Fact] public void ARefusedEditLeavesTheLogAlone() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); QsoEdit edit = session.Edit(logged.Id, QsoField.Zone, "41"); Assert.False(edit.IsAccepted); Assert.Equal(25, session.Log.Qsos.Single().Zone); } /// The other stations need the call and time the contact had before, because /// that is how they find their own copy of it. [Fact] public void AnEditReportsWhatTheContactUsedToBe() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); QsoChange? change = null; session.Edited += (_, c) => change = c; session.Edit(logged.Id, QsoField.Call, "JA2XYZ"); Assert.Equal("JA1XYZ", change?.OldCall); Assert.Equal(logged.TimestampUtc, change?.OldTimestampUtc); Assert.Equal("JA2XYZ", change?.Qso.Call.Text); } /// The country file gets a call wrong now and then. Correcting the country /// by hand has to change the score, or the correction is decoration. [Fact] public void CorrectingTheCountryChangesTheScore() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); Assert.Equal(3, session.Log.Tally.Points); session.Edit(logged.Id, QsoField.CountryPrefix, "DL"); Assert.Equal(0, session.Log.Tally.Points); } [Fact] public void ADeleteReportsTheContactThatWent() { LoggingSession session = Session(); Type(session, "JA1XYZ", "25"); Qso logged = session.LogContact(); Qso? gone = null; session.Deleted += (_, q) => gone = q; session.Delete(logged.Id); Assert.Equal("JA1XYZ", gone?.Call.Text); } [Fact] public void StoredContactsAreReadBackWhenTheSessionStarts() { FakeLogStore store = new(); ContestInstance instance = store.AddContest(new ContestInstance { ContestNumber = 0, ContestName = "CQWW" }); LoggingSession first = new(store, new CqWorldWide(ModeCategory.Cw), instance, Me, CountryFile.Parse(Countries)); Type(first, "JA1XYZ", "25"); first.LogContact(); LoggingSession second = new(store, new CqWorldWide(ModeCategory.Cw), instance, Me, CountryFile.Parse(Countries)); Assert.Single(second.Log.Qsos); Assert.Equal(3, second.Log.Tally.Points); } }