Files
Nonemm/tests/Nonemm.Spotting.Tests/BandmapTests.cs
T
erikbrockoandClaude Sonnet 5.5 9d0de3b129 Set the bandmap spot timeout separately for CW/digital and phone
CW and digital spots time out after 10 minutes and phone spots after 30
by default, both settings on the telnet Filters tab. The mode comes from
the band plan; a frequency it has no boundaries for counts as phone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 12:27:55 +00:00

122 lines
3.6 KiB
C#

using Nonemm.Core;
namespace Nonemm.Spotting.Tests;
public class BandmapTests
{
private static readonly DateTime Now = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc);
private static Spot At(string call, double kilohertz, DateTime? at = null) =>
new(Callsign.Parse(call), Frequency.FromKilohertz(kilohertz), at ?? Now, SpotSource.Cluster);
[Fact]
public void SpotsComeBackInFrequencyOrder()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_100));
map.Add(At("JA1XYZ", 14_010));
Assert.Equal(["JA1XYZ", "DL1ABC"], map.On(Bands.Band20M).Select(s => s.Call.Text));
}
[Fact]
public void EachSpotLivesAsLongAsItsOwnLifetime()
{
Bandmap map = new(clock: () => Now)
{
LifetimeFor = spot => TimeSpan.FromMinutes(spot.Frequency.Kilohertz < 14_100 ? 10 : 30),
};
map.Add(At("DL1ABC", 14_025));
map.Add(At("JA1XYZ", 14_200));
map.DropOlderThan(Now.AddMinutes(15));
Assert.Equal(["JA1XYZ"], map.All().Select(s => s.Call.Text));
}
[Fact]
public void AStationThatMovesKeepsOneEntryOnItsBand()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_010));
map.Add(At("DL1ABC", 14_020));
Spot only = Assert.Single(map.On(Bands.Band20M));
Assert.Equal(14_020_000, only.Frequency.Hertz);
}
[Fact]
public void TheSameStationOnAnotherBandIsASeparateEntry()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_010));
map.Add(At("DL1ABC", 7_010));
Assert.Equal(2, map.All().Count);
}
[Fact]
public void SpotsOlderThanAnHourAreDropped()
{
DateTime arriving = Now.AddHours(-2);
Bandmap map = new(clock: () => arriving);
map.Add(At("DL1ABC", 14_010));
arriving = Now.AddMinutes(-5);
map.Add(At("JA1XYZ", 14_020));
map.DropOlderThan(Now);
Assert.Equal("JA1XYZ", Assert.Single(map.All()).Call.Text);
}
/// Nodes get their clocks wrong and replay their backlog on connect. Age is
/// counted from when the spot arrived, so a spot stamped hours ago still
/// shows for the hour after it turned up.
[Fact]
public void ASpotStampedLongAgoIsKeptIfItOnlyJustArrived()
{
Bandmap map = new(clock: () => Now);
map.Add(At("DL1ABC", 14_010, Now.AddHours(-4)));
map.DropOlderThan(Now);
Assert.Single(map.All());
}
[Fact]
public void ARespottedStationStartsItsHourAgain()
{
DateTime arriving = Now.AddMinutes(-50);
Bandmap map = new(clock: () => arriving);
map.Add(At("DL1ABC", 14_010));
arriving = Now;
map.Add(At("DL1ABC", 14_012));
map.DropOlderThan(Now.AddMinutes(30));
Assert.Single(map.All());
}
/// N1MM's Mark button leaves this where the radio is, to say the frequency
/// is busy.
[Fact]
public void AMarkIsLabelledWithTheTimeAndIsNotAStation()
{
Spot mark = Spot.Mark(
Frequency.FromKilohertz(14_030),
new DateTime(2026, 1, 1, 22, 31, 5, DateTimeKind.Utc));
Assert.Equal("BUSY@22:31:05", mark.Call.Text);
Assert.False(mark.IsStation);
}
[Fact]
public void TheNearestSpotInsideTheWindowIsFound()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_010));
map.Add(At("JA1XYZ", 14_050));
Assert.Equal(
"DL1ABC",
map.Near(Frequency.FromKilohertz(14_010.2), Frequency.FromKilohertz(0.5))?.Call.Text);
Assert.Null(map.Near(Frequency.FromKilohertz(14_030), Frequency.FromKilohertz(0.5)));
}
}