Frequencies, bands, modes, callsigns, grid squares and the country file live in Nonemm.Core. Nonemm.Contests holds the scoring engine and CQ WW and CQ WPX. Nonemm.Storage writes N1MM's DXLOG schema, and Nonemm.Formats writes Cabrillo 3.0 and reads and writes ADIF. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
146 lines
5.1 KiB
C#
146 lines
5.1 KiB
C#
using Microsoft.Data.Sqlite;
|
|
using Nonemm.Core;
|
|
|
|
namespace Nonemm.Storage.Tests;
|
|
|
|
public class SqliteLogStoreTests : IDisposable
|
|
{
|
|
private readonly string path = Path.Combine(Path.GetTempPath(), $"nonemm-{Guid.NewGuid():N}.s3db");
|
|
|
|
public void Dispose()
|
|
{
|
|
SqliteConnection.ClearAllPools();
|
|
File.Delete(path);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private static Qso Contact(string call, DateTime at, int contestNumber = 1) => new()
|
|
{
|
|
Id = Qso.NewId(),
|
|
TimestampUtc = at,
|
|
Call = Callsign.Parse(call),
|
|
Frequency = Frequency.FromKilohertz(14_025.5),
|
|
Mode = Modes.Cw,
|
|
ContestName = "CQWW",
|
|
ContestNumber = contestNumber,
|
|
SentReport = "599",
|
|
ReceivedReport = "599",
|
|
Zone = 14,
|
|
Points = 3,
|
|
IsMultiplier1 = true,
|
|
CountryPrefix = "DL",
|
|
Continent = "EU",
|
|
WpxPrefix = "DL1",
|
|
};
|
|
|
|
[Fact]
|
|
public void ContactSurvivesARoundTrip()
|
|
{
|
|
DateTime at = new(2026, 5, 30, 12, 34, 56, DateTimeKind.Utc);
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
Qso written = store.Add(Contact("DL1ABC", at));
|
|
|
|
Qso read = store.Qsos(1).Single();
|
|
Assert.Equal(written.Id, read.Id);
|
|
Assert.Equal("DL1ABC", read.Call.Text);
|
|
Assert.Equal(at, read.TimestampUtc);
|
|
Assert.Equal(14_025_500, read.Frequency.Hertz);
|
|
Assert.Equal(Modes.Cw, read.Mode);
|
|
Assert.Equal(14, read.Zone);
|
|
Assert.Equal("EU", read.Continent);
|
|
}
|
|
|
|
[Fact]
|
|
public void SecondContactInTheSameSecondMovesOnASecond()
|
|
{
|
|
DateTime at = new(2026, 5, 30, 12, 34, 56, DateTimeKind.Utc);
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
store.Add(Contact("DL1ABC", at));
|
|
Qso second = store.Add(Contact("DL1ABC", at));
|
|
Assert.Equal(at.AddSeconds(1), second.TimestampUtc);
|
|
Assert.Equal(2, store.Qsos(1).Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void EditedContactKeepsItsIdentity()
|
|
{
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
Qso written = store.Add(Contact("DL1ABC", new DateTime(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc)));
|
|
store.Update(written with { ReceivedReport = "579", Zone = 15 });
|
|
|
|
Qso read = store.Qsos(1).Single();
|
|
Assert.Equal(written.Id, read.Id);
|
|
Assert.Equal("579", read.ReceivedReport);
|
|
Assert.Equal(15, read.Zone);
|
|
}
|
|
|
|
[Fact]
|
|
public void DeletedContactIsGone()
|
|
{
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
Qso written = store.Add(Contact("DL1ABC", new DateTime(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc)));
|
|
store.Delete(written.Id);
|
|
Assert.Empty(store.Qsos(1));
|
|
}
|
|
|
|
[Fact]
|
|
public void ContestsAreNumberedInTurn()
|
|
{
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
ContestInstance first = store.AddContest(new ContestInstance { ContestNumber = 0, ContestName = "CQWW" });
|
|
ContestInstance second = store.AddContest(new ContestInstance { ContestNumber = 0, ContestName = "CQWPX" });
|
|
Assert.Equal(1, first.ContestNumber);
|
|
Assert.Equal(2, second.ContestNumber);
|
|
Assert.Equal(2, store.Contests().Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void ContactsBelongToTheirOwnContest()
|
|
{
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
store.Add(Contact("DL1ABC", new DateTime(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc), contestNumber: 1));
|
|
store.Add(Contact("DL2ABC", new DateTime(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc), contestNumber: 2));
|
|
Assert.Equal("DL1ABC", store.Qsos(1).Single().Call.Text);
|
|
Assert.Equal("DL2ABC", store.Qsos(2).Single().Call.Text);
|
|
}
|
|
|
|
[Fact]
|
|
public void NewDatabaseCarriesN1mmTablesAndVersion()
|
|
{
|
|
using (SqliteLogStore store = SqliteLogStore.Open(path))
|
|
{
|
|
}
|
|
|
|
using SqliteConnection connection = new($"Data Source={path}");
|
|
connection.Open();
|
|
using SqliteCommand command = connection.CreateCommand();
|
|
command.CommandText = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
|
|
using SqliteDataReader row = command.ExecuteReader();
|
|
List<string> tables = [];
|
|
while (row.Read())
|
|
{
|
|
tables.Add(row.GetString(0));
|
|
}
|
|
Assert.Contains("DXLOG", tables);
|
|
Assert.Contains("ContestInstance", tables);
|
|
Assert.Contains("Contest", tables);
|
|
Assert.Contains("Station", tables);
|
|
|
|
using SqliteCommand version = connection.CreateCommand();
|
|
version.CommandText = "PRAGMA user_version";
|
|
Assert.Equal(4L, version.ExecuteScalar());
|
|
}
|
|
|
|
[Fact]
|
|
public void ContactIdCannotBeChanged()
|
|
{
|
|
using SqliteLogStore store = SqliteLogStore.Open(path);
|
|
store.Add(Contact("DL1ABC", new DateTime(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc)));
|
|
using SqliteConnection connection = new($"Data Source={path}");
|
|
connection.Open();
|
|
using SqliteCommand command = connection.CreateCommand();
|
|
command.CommandText = "UPDATE DXLOG SET ID = 'zzzz'";
|
|
Assert.Throws<SqliteException>(() => command.ExecuteNonQuery());
|
|
}
|
|
}
|