diff --git a/src/LageBuch.App/AppPaths.cs b/src/LageBuch.App/AppPaths.cs
index 48937db..5195702 100644
--- a/src/LageBuch.App/AppPaths.cs
+++ b/src/LageBuch.App/AppPaths.cs
@@ -1,6 +1,6 @@
namespace LageBuch.App;
-public static class AppPaths
+internal static class AppPaths
{
public static string AppDataDir =>
GetAppDataDir(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
diff --git a/src/LageBuch.App/LageBuch.App.csproj b/src/LageBuch.App/LageBuch.App.csproj
index fe0c7a4..1cdc692 100644
--- a/src/LageBuch.App/LageBuch.App.csproj
+++ b/src/LageBuch.App/LageBuch.App.csproj
@@ -30,4 +30,8 @@
+
+
+
+
diff --git a/src/LageBuch.App/Services/IncidentHostController.cs b/src/LageBuch.App/Services/IncidentHostController.cs
index ffad321..a18cee3 100644
--- a/src/LageBuch.App/Services/IncidentHostController.cs
+++ b/src/LageBuch.App/Services/IncidentHostController.cs
@@ -14,7 +14,7 @@ namespace LageBuch.App.Services;
/// reachable over loopback, the LAN, and a tailnet at once. Lives in the desktop head so ASP.NET
/// Core stays out of the cross-platform AppLogic/Android build.
///
-public sealed class IncidentHostController : IIncidentHostController
+internal sealed class IncidentHostController : IIncidentHostController
{
private readonly IClock _clock;
private readonly string _appVersion;
diff --git a/src/LageBuch.App/Services/StorageProviderFileDialogService.cs b/src/LageBuch.App/Services/StorageProviderFileDialogService.cs
index ebad125..1b0c480 100644
--- a/src/LageBuch.App/Services/StorageProviderFileDialogService.cs
+++ b/src/LageBuch.App/Services/StorageProviderFileDialogService.cs
@@ -6,7 +6,7 @@
namespace LageBuch.App.Services;
-public sealed class StorageProviderFileDialogService : IFileDialogService
+internal sealed class StorageProviderFileDialogService : IFileDialogService
{
private static readonly FilePickerFileType Incident =
new("Einsatzdokumentation") { Patterns = new[] { "*.fwincident" } };
diff --git a/src/LageBuch.App/Services/SystemAlarmService.cs b/src/LageBuch.App/Services/SystemAlarmService.cs
index 0fac673..a642b27 100644
--- a/src/LageBuch.App/Services/SystemAlarmService.cs
+++ b/src/LageBuch.App/Services/SystemAlarmService.cs
@@ -14,7 +14,7 @@ namespace LageBuch.App.Services;
/// Every path degrades to a silent no-op when the asset or the player is missing, so a build with
/// no voice clip yet — or a host without the CLI player — simply stays quiet rather than crashing.
///
-public sealed class SystemAlarmService : IAlarmService
+internal sealed class SystemAlarmService : IAlarmService
{
private const uint SndAsync = 0x0001; // play asynchronously
private const uint SndNodefault = 0x0002; // no default beep if it fails
diff --git a/src/LageBuch.AppLogic/Services/IncidentStore.cs b/src/LageBuch.AppLogic/Services/IncidentStore.cs
index 410055d..270baf5 100644
--- a/src/LageBuch.AppLogic/Services/IncidentStore.cs
+++ b/src/LageBuch.AppLogic/Services/IncidentStore.cs
@@ -5,14 +5,13 @@ namespace LageBuch.AppLogic.Services;
public sealed class IncidentStore : IIncidentStore
{
- private readonly IncidentRepository _repository = new();
private readonly IIncidentFileStore _fileStore = new IncidentFileStore();
- public void Save(string path, Incident incident) => _repository.Save(path, incident);
+ public void Save(string path, Incident incident) => IncidentRepository.Save(path, incident);
- public Incident Load(string path) => _repository.Load(path);
+ public Incident Load(string path) => IncidentRepository.Load(path);
- public IncidentState? TryReadState(string path) => _repository.TryReadState(path);
+ public IncidentState? TryReadState(string path) => IncidentRepository.TryReadState(path);
public void SaveFileBytes(string path, string storageFileName, byte[] bytes) =>
_fileStore.SaveBytes(path, storageFileName, bytes);
diff --git a/src/LageBuch.AppLogic/Services/MasterDataProvider.cs b/src/LageBuch.AppLogic/Services/MasterDataProvider.cs
index b50aa4c..61db4a4 100644
--- a/src/LageBuch.AppLogic/Services/MasterDataProvider.cs
+++ b/src/LageBuch.AppLogic/Services/MasterDataProvider.cs
@@ -5,18 +5,17 @@ namespace LageBuch.AppLogic.Services;
public sealed class MasterDataProvider : IMasterDataProvider
{
private readonly string _path;
- private readonly MasterDataStore _store = new();
private MasterDataSet? _cached;
public MasterDataProvider(string masterDataPath) => _path = masterDataPath;
- public MasterDataSet Get() => _cached ??= _store.GetOrCreate(_path);
+ public MasterDataSet Get() => _cached ??= MasterDataStore.GetOrCreate(_path);
public void Save(MasterDataSet set)
{
- _store.Save(_path, set);
+ MasterDataStore.Save(_path, set);
// Re-read rather than trust the in-memory copy: the store is the canonical shape
// (e.g. personnel comes back name-sorted), so callers see exactly what a fresh start would.
- _cached = _store.GetOrCreate(_path);
+ _cached = MasterDataStore.GetOrCreate(_path);
}
}
diff --git a/src/LageBuch.AppLogic/ViewModels/AboutViewModel.cs b/src/LageBuch.AppLogic/ViewModels/AboutViewModel.cs
index 2f1e804..949319a 100644
--- a/src/LageBuch.AppLogic/ViewModels/AboutViewModel.cs
+++ b/src/LageBuch.AppLogic/ViewModels/AboutViewModel.cs
@@ -23,13 +23,18 @@ public AboutViewModel(IFileDialogService dialogs, string version)
Version = version;
}
+ [SuppressMessage("Performance", "CA1822", Justification = "XAML {Binding} target in AboutView; binding requires an instance property.")]
public string AppName => "Lagebuch";
+ [SuppressMessage("Performance", "CA1822", Justification = "XAML {Binding} target in AboutView; binding requires an instance property.")]
public string Descriptor => "Einsatzdokumentation";
public string Version { get; }
+ [SuppressMessage("Performance", "CA1822", Justification = "XAML {Binding} target in AboutView; binding requires an instance property.")]
public string RepositoryUrl => RepoUrl;
// Kept in sync with the LICENSE file in the repo root.
+ [SuppressMessage("Performance", "CA1822", Justification = "XAML {Binding} target in AboutView; binding requires an instance property.")]
public string LicenseLine => "Veröffentlicht unter der MIT-Lizenz.";
+ [SuppressMessage("Performance", "CA1822", Justification = "XAML {Binding} target in AboutView; binding requires an instance property.")]
public string CopyrightLine => "Copyright © 2026 Thomas Müller";
[ObservableProperty]
diff --git a/src/LageBuch.Persistence/IncidentRepository.cs b/src/LageBuch.Persistence/IncidentRepository.cs
index 5c6c2c3..ab74970 100644
--- a/src/LageBuch.Persistence/IncidentRepository.cs
+++ b/src/LageBuch.Persistence/IncidentRepository.cs
@@ -10,7 +10,7 @@ public sealed class IncidentRepository
{
private const string Iso = "O";
- public void Save(string path, Incident incident)
+ public static void Save(string path, Incident incident)
{
ArgumentNullException.ThrowIfNull(incident);
using var cn = SqliteConnectionFactory.OpenReadWrite(path);
@@ -269,9 +269,9 @@ private static void WriteChecklist(SqliteConnection cn, SqliteTransaction tx, IR
/// `state` lives in the base schema's incident_meta, so this works across schema versions;
/// any failure (missing, corrupt, locked, too new) returns null so the overview degrades quietly.
///
- [SuppressMessage("Design", "CA1031",
+[SuppressMessage("Design", "CA1031",
Justification = "Try-read: missing, corrupt, locked or too-new reads all degrade to null (see comment).")]
- public IncidentState? TryReadState(string path)
+ public static IncidentState? TryReadState(string path)
{
if (!File.Exists(path))
return null;
@@ -289,7 +289,7 @@ private static void WriteChecklist(SqliteConnection cn, SqliteTransaction tx, IR
}
}
- public Incident Load(string path)
+ public static Incident Load(string path)
{
// Check before opening: SQLite would otherwise report a missing file as a bare "unable to
// open database file", and the caller cannot tell that apart from a corrupt one.
diff --git a/src/LageBuch.Persistence/MasterData/MasterDataStore.cs b/src/LageBuch.Persistence/MasterData/MasterDataStore.cs
index 90edccd..2a598ea 100644
--- a/src/LageBuch.Persistence/MasterData/MasterDataStore.cs
+++ b/src/LageBuch.Persistence/MasterData/MasterDataStore.cs
@@ -11,7 +11,7 @@ public sealed class MasterDataStore
/// its contents. Nothing is seeded: the app ships with no master data, so a fresh database comes
/// back empty and is populated only by — i.e. the editor's Import.
///
- public MasterDataSet GetOrCreate(string path)
+ public static MasterDataSet GetOrCreate(string path)
{
using var cn = SqliteConnectionFactory.OpenReadWrite(path);
EnsureSchema(cn);
@@ -22,7 +22,7 @@ public MasterDataSet GetOrCreate(string path)
/// Replaces the master data with , in the given order. A full transactional
/// replace, so deletes and reorders take effect exactly as supplied.
///
- public void Save(string path, MasterDataSet set)
+ public static void Save(string path, MasterDataSet set)
{
ArgumentNullException.ThrowIfNull(set);
using var cn = SqliteConnectionFactory.OpenReadWrite(path);
diff --git a/tests/LageBuch.Acceptance.Tests/TestAppBuilder.cs b/tests/LageBuch.Acceptance.Tests/TestAppBuilder.cs
index c5773f8..81a42b0 100644
--- a/tests/LageBuch.Acceptance.Tests/TestAppBuilder.cs
+++ b/tests/LageBuch.Acceptance.Tests/TestAppBuilder.cs
@@ -5,7 +5,7 @@
namespace LageBuch.Acceptance.Tests;
-public static class TestAppBuilder
+internal static class TestAppBuilder
{
// UseHeadlessDrawing = false keeps the real Skia text/render backend (bundled via
// Avalonia.Desktop) so embedded custom fonts can be rasterized. The default headless
diff --git a/tests/LageBuch.Documents.Tests/PdfAssert.cs b/tests/LageBuch.Documents.Tests/PdfAssert.cs
index 4acf7c7..3443dfe 100644
--- a/tests/LageBuch.Documents.Tests/PdfAssert.cs
+++ b/tests/LageBuch.Documents.Tests/PdfAssert.cs
@@ -3,7 +3,7 @@
namespace LageBuch.Documents.Tests;
-public static class PdfAssert
+internal static class PdfAssert
{
private static readonly byte[] PdfHeader = { 0x25, 0x50, 0x44, 0x46, 0x2D }; // "%PDF-"
diff --git a/tests/LageBuch.Domain.Tests/IncidentCreationTests.cs b/tests/LageBuch.Domain.Tests/IncidentCreationTests.cs
index f2b5b53..3ff2a64 100644
--- a/tests/LageBuch.Domain.Tests/IncidentCreationTests.cs
+++ b/tests/LageBuch.Domain.Tests/IncidentCreationTests.cs
@@ -4,7 +4,7 @@
namespace LageBuch.Domain.Tests;
-public sealed class FixedClock : IClock
+internal sealed class FixedClock : IClock
{
public FixedClock(DateTimeOffset now) => Now = now;
public DateTimeOffset Now { get; set; }
diff --git a/tests/LageBuch.Persistence.Tests/ClosedIncidentReadOnlyTests.cs b/tests/LageBuch.Persistence.Tests/ClosedIncidentReadOnlyTests.cs
index 06afcf5..cbd51bd 100644
--- a/tests/LageBuch.Persistence.Tests/ClosedIncidentReadOnlyTests.cs
+++ b/tests/LageBuch.Persistence.Tests/ClosedIncidentReadOnlyTests.cs
@@ -22,7 +22,7 @@ private void SaveClosedIncident()
var op = new SessionOperator("Müller");
var incident = Incident.Start(clock, op);
incident.Close(clock, op);
- new IncidentRepository().Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
}
[Fact]
@@ -39,7 +39,7 @@ public void ReadOnly_connection_rejects_writes()
public void Load_of_closed_incident_returns_readonly_domain_state()
{
SaveClosedIncident();
- var loaded = new IncidentRepository().Load(_path);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal(IncidentState.Closed, loaded.State);
Assert.Throws(
() => loaded.AddForceUnit(new Clock(), new Domain.SessionOperator("Müller"), "FFB", 1));
diff --git a/tests/LageBuch.Persistence.Tests/CoMeasurementPersistenceTests.cs b/tests/LageBuch.Persistence.Tests/CoMeasurementPersistenceTests.cs
index 60ec15d..2e9fa38 100644
--- a/tests/LageBuch.Persistence.Tests/CoMeasurementPersistenceTests.cs
+++ b/tests/LageBuch.Persistence.Tests/CoMeasurementPersistenceTests.cs
@@ -37,10 +37,10 @@ private static Incident CreateIncidentWithBuilding()
public void SaveLoad_RoundTrip_BuildingsAndDwellings()
{
var original = CreateIncidentWithBuilding();
- var repo = new IncidentRepository();
- repo.Save(_path, original);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, original);
+
+ var loaded = IncidentRepository.Load(_path);
Assert.Single(loaded.Buildings);
Assert.Equal("Haus A", loaded.Buildings[0].Name);
@@ -69,10 +69,9 @@ public void SaveLoad_RoundTrip_NullableFields()
var incident = Incident.Start(clock, op);
incident.AddCoBuilding(clock, op, "Haus A", 1, 1);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
- var loaded = repo.Load(_path);
+ var loaded = IncidentRepository.Load(_path);
var dwelling = loaded.Dwellings[0];
Assert.Null(dwelling.CoValue);
diff --git a/tests/LageBuch.Persistence.Tests/IncidentRepositoryLoadTests.cs b/tests/LageBuch.Persistence.Tests/IncidentRepositoryLoadTests.cs
index f9ab9e0..c098fbf 100644
--- a/tests/LageBuch.Persistence.Tests/IncidentRepositoryLoadTests.cs
+++ b/tests/LageBuch.Persistence.Tests/IncidentRepositoryLoadTests.cs
@@ -20,7 +20,7 @@ public void Loading_a_missing_file_reports_it_and_creates_nothing()
{
Assert.False(File.Exists(_path));
- var ex = Assert.Throws(() => new IncidentRepository().Load(_path));
+ var ex = Assert.Throws(() => IncidentRepository.Load(_path));
// The path rides on FileName, not in the message: the Home banner already prefixes the
// filename, so repeating it there reads as a stutter.
@@ -35,7 +35,7 @@ public void Loading_a_file_that_is_not_a_database_reports_it_without_truncating_
// Picking the wrong file in the open dialog must not damage whatever was picked.
File.WriteAllText(_path, "nicht wirklich eine Einsatzdatei");
- Assert.ThrowsAny(() => new IncidentRepository().Load(_path));
+ Assert.ThrowsAny(() => IncidentRepository.Load(_path));
Assert.Equal("nicht wirklich eine Einsatzdatei", File.ReadAllText(_path));
diff --git a/tests/LageBuch.Persistence.Tests/IncidentRepositorySaveTests.cs b/tests/LageBuch.Persistence.Tests/IncidentRepositorySaveTests.cs
index 686c79a..6f7acc2 100644
--- a/tests/LageBuch.Persistence.Tests/IncidentRepositorySaveTests.cs
+++ b/tests/LageBuch.Persistence.Tests/IncidentRepositorySaveTests.cs
@@ -27,7 +27,7 @@ public void Save_writes_meta_and_journal_rows()
incident.SetIncidentNumber(new IncidentNumber("B 1.2 260715 4242"));
incident.AddJournalEntry(clock, op, EtbDirection.Incoming, "Meldung", from: "ILS");
- new IncidentRepository().Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
using var cn = SqliteConnectionFactory.OpenReadOnly(_path);
using var cmd = cn.CreateCommand();
@@ -49,7 +49,7 @@ public void Save_writes_one_etb_entry_edits_row_per_edit()
incident.EditJournalEntry(clock, op, entry.Id, "Erste Korrektur");
incident.EditJournalEntry(clock, op, entry.Id, "Zweite Korrektur");
- new IncidentRepository().Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
using var cn = SqliteConnectionFactory.OpenReadOnly(_path);
using var cmd = cn.CreateCommand();
@@ -62,8 +62,8 @@ public void Save_writes_one_etb_entry_edits_row_per_edit()
public void Legacy_ils_number_loads_as_the_incident_number()
{
var clock = new Clock();
- var repo = new IncidentRepository();
- repo.Save(_path, Incident.Start(clock, new SessionOperator("Müller")));
+
+ IncidentRepository.Save(_path, Incident.Start(clock, new SessionOperator("Müller")));
// Simulate a file written before the unification: the number lived in ils_number and
// incident_number was empty.
@@ -75,7 +75,7 @@ public void Legacy_ils_number_loads_as_the_incident_number()
}
SqliteConnection.ClearAllPools();
- Assert.Equal("4711", repo.Load(_path).IncidentNumber!.Value);
+ Assert.Equal("4711", IncidentRepository.Load(_path).IncidentNumber!.Value);
}
[Fact]
@@ -84,9 +84,9 @@ public void Save_is_idempotent_overwrite_not_append()
var clock = new Clock();
var op = new SessionOperator("Müller");
var incident = Incident.Start(clock, op);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- repo.Save(_path, incident);
+
+ IncidentRepository.Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
using var cn = SqliteConnectionFactory.OpenReadOnly(_path);
using var cmd = cn.CreateCommand();
diff --git a/tests/LageBuch.Persistence.Tests/IncidentRoundTripTests.cs b/tests/LageBuch.Persistence.Tests/IncidentRoundTripTests.cs
index 63e3bbd..65908f0 100644
--- a/tests/LageBuch.Persistence.Tests/IncidentRoundTripTests.cs
+++ b/tests/LageBuch.Persistence.Tests/IncidentRoundTripTests.cs
@@ -53,9 +53,8 @@ public void Open_incident_round_trips_all_fields()
incident.AddTask(clock, op, "Nachfordern", null, TaskImportance.Low, TaskUrgency.Low, 30);
incident.SetTaskCompleted(incident.Tasks[1].Id, true, clock, op);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal(incident.Id, loaded.Id);
Assert.Equal(IncidentState.Open, loaded.State);
@@ -130,9 +129,8 @@ public void An_edited_status_and_bemerkung_survive_a_round_trip()
incident.UpdateForceUnit(clock, op, unit.Id, "Im Einsatz", "Innenangriff");
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
var reloaded = Assert.Single(loaded.Forces);
Assert.Equal("Im Einsatz", reloaded.Status);
@@ -154,9 +152,8 @@ public void An_edited_etb_entry_and_its_history_survive_a_round_trip()
var editor = new SessionOperator("Schmidt");
incident.EditJournalEntry(clock, editor, entry.Id, "Lagemeldung korrigiert");
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
var reloaded = loaded.Journal.Single(e => e.Id == entry.Id);
Assert.Equal("Lagemeldung korrigiert", reloaded.Text);
@@ -178,9 +175,8 @@ public void Closed_incident_round_trips_and_stays_closed()
clock.Now = clock.Now.AddHours(2);
incident.Close(clock, op);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal(IncidentState.Closed, loaded.State);
Assert.Equal(incident.ClosedAt, loaded.ClosedAt);
@@ -224,9 +220,8 @@ public void Scba_trupps_round_trip_across_all_states()
entryPressure: 300);
var waitingId = waiting.Id;
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal(4, loaded.ScbaTrupps.Count);
@@ -278,9 +273,8 @@ public void Attached_files_metadata_round_trips()
clock.Now = clock.Now.AddMinutes(1);
incident.AddFile(clock, op, "bericht.pdf", "application/pdf", 4096);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal(2, loaded.Files.Count);
Assert.Equal("brand.jpg", loaded.Files[0].FileName);
@@ -305,8 +299,8 @@ public void A_file_row_written_before_display_name_existed_falls_back_to_the_fil
var op = new SessionOperator("Müller", "FFB 12/1");
var incident = Incident.Start(clock, op);
incident.AddFile(clock, op, "brand.jpg", "image/jpeg", 2048);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
+
+ IncidentRepository.Save(_path, incident);
using (var cn = SqliteConnectionFactory.OpenReadWrite(_path))
using (var cmd = cn.CreateCommand())
@@ -316,7 +310,7 @@ public void A_file_row_written_before_display_name_existed_falls_back_to_the_fil
}
SqliteConnection.ClearAllPools();
- var loaded = repo.Load(_path);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal("brand.jpg", Assert.Single(loaded.Files).DisplayName);
}
@@ -329,9 +323,8 @@ public void Incident_timers_round_trip()
var incident = Incident.Start(clock, op);
incident.UpsertTimer("ils-reminder", clock.Now, intervalMinutes: 15, recurringIntervalMinutes: 30, isRunning: true);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
- var loaded = repo.Load(_path);
+ IncidentRepository.Save(_path, incident);
+ var loaded = IncidentRepository.Load(_path);
var timer = loaded.FindTimer("ils-reminder");
Assert.NotNull(timer);
@@ -352,12 +345,11 @@ public void A_removed_unit_and_its_edit_trail_do_not_survive_a_round_trip()
incident.AddForceUnit(clock, op, "FFB Wache 1", 6, callSign: "FFB 1/40/1", scbaCount: 2, officerCount: 1);
incident.UpdateForceStrength(clock, op, incident.Forces[0].Id, officerCount: 1, personnelCount: 9, scbaCount: 4);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
incident.RemoveForceUnit(clock, op, incident.Forces[0].Id);
- repo.Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
- var loaded = repo.Load(_path);
+ var loaded = IncidentRepository.Load(_path);
Assert.Empty(loaded.Forces);
Assert.Equal((0, 0), (loaded.TotalPersonnel, loaded.TotalScba));
}
@@ -371,7 +363,7 @@ public void V13_file_gains_an_empty_tasks_table_and_still_loads()
var op = new SessionOperator("Müller");
var incident = Incident.Start(clock, op, "Brand");
incident.AddTask(clock, op, "Alt", null, TaskImportance.Medium, TaskUrgency.Medium, 15);
- new IncidentRepository().Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
using (var cn = new SqliteConnection($"Data Source={_path}"))
{
@@ -382,7 +374,7 @@ public void V13_file_gains_an_empty_tasks_table_and_still_loads()
}
SqliteConnection.ClearAllPools();
- var loaded = new IncidentRepository().Load(_path);
+ var loaded = IncidentRepository.Load(_path);
Assert.Empty(loaded.Tasks); // old file has no tasks — loads cleanly, migrates to V14
Assert.Equal("Brand", loaded.Keyword);
diff --git a/tests/LageBuch.Persistence.Tests/IncidentStateProbeTests.cs b/tests/LageBuch.Persistence.Tests/IncidentStateProbeTests.cs
index 5ef3acc..abab27b 100644
--- a/tests/LageBuch.Persistence.Tests/IncidentStateProbeTests.cs
+++ b/tests/LageBuch.Persistence.Tests/IncidentStateProbeTests.cs
@@ -23,34 +23,34 @@ private void Save(bool closed)
var op = new SessionOperator("Müller");
var incident = Incident.Start(clock, op);
if (closed) incident.Close(clock, op);
- new IncidentRepository().Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
}
[Fact]
public void Open_incident_reads_as_Open()
{
Save(closed: false);
- Assert.Equal(IncidentState.Open, new IncidentRepository().TryReadState(_path));
+ Assert.Equal(IncidentState.Open, IncidentRepository.TryReadState(_path));
}
[Fact]
public void Closed_incident_reads_as_Closed()
{
Save(closed: true);
- Assert.Equal(IncidentState.Closed, new IncidentRepository().TryReadState(_path));
+ Assert.Equal(IncidentState.Closed, IncidentRepository.TryReadState(_path));
}
[Fact]
public void Missing_file_reads_as_null()
{
- Assert.Null(new IncidentRepository().TryReadState(_path));
+ Assert.Null(IncidentRepository.TryReadState(_path));
}
[Fact]
public void Garbage_file_reads_as_null_instead_of_throwing()
{
File.WriteAllText(_path, "this is not a sqlite database");
- Assert.Null(new IncidentRepository().TryReadState(_path));
+ Assert.Null(IncidentRepository.TryReadState(_path));
}
[Fact]
@@ -59,7 +59,7 @@ public void Probing_does_not_modify_the_file()
Save(closed: false);
var before = File.ReadAllBytes(_path);
- new IncidentRepository().TryReadState(_path);
+ IncidentRepository.TryReadState(_path);
Assert.Equal(before, File.ReadAllBytes(_path));
}
diff --git a/tests/LageBuch.Persistence.Tests/MasterDataStoreTests.cs b/tests/LageBuch.Persistence.Tests/MasterDataStoreTests.cs
index ba80f7b..39c94ce 100644
--- a/tests/LageBuch.Persistence.Tests/MasterDataStoreTests.cs
+++ b/tests/LageBuch.Persistence.Tests/MasterDataStoreTests.cs
@@ -18,22 +18,22 @@ public void GetOrCreate_returns_an_empty_set_for_a_fresh_database()
{
// The app ships with no seed, so a brand-new masterdata.db comes back empty -- populated
// only by Save (the editor's Import), never by a compiled-in default.
- var set = new MasterDataStore().GetOrCreate(_path);
+ var set = MasterDataStore.GetOrCreate(_path);
Assert.True(set.IsEmpty);
}
[Fact]
public void GetOrCreate_is_idempotent_and_never_seeds()
{
- var store = new MasterDataStore();
- Assert.True(store.GetOrCreate(_path).IsEmpty);
- Assert.True(store.GetOrCreate(_path).IsEmpty); // still empty on a second open
+
+ Assert.True(MasterDataStore.GetOrCreate(_path).IsEmpty);
+ Assert.True(MasterDataStore.GetOrCreate(_path).IsEmpty); // still empty on a second open
}
[Fact]
public void Save_then_GetOrCreate_round_trips_every_category_including_personnel()
{
- var store = new MasterDataStore();
+
var set = MasterDataSet.Empty with
{
Roles = new[] { "EL", "ZF" },
@@ -51,9 +51,9 @@ public void Save_then_GetOrCreate_round_trips_every_category_including_personnel
Links = new[] { new Link("Wetterdienst", "https://dwd.de") },
Personnel = new[] { new Person("Mustermann", "Max", "ZF", "Land 1", "01 71 / 1 23 45 67") },
};
- store.Save(_path, set);
+ MasterDataStore.Save(_path, set);
- var reopened = store.GetOrCreate(_path);
+ var reopened = MasterDataStore.GetOrCreate(_path);
Assert.Equal(new[] { "EL", "ZF" }, reopened.Roles);
Assert.Equal(new[] { "B", "THL" }, reopened.Einsatzarten);
Assert.Equal(
@@ -70,7 +70,7 @@ public void Save_then_GetOrCreate_round_trips_every_category_including_personnel
[Fact]
public void Vehicles_round_trip_with_wache_callsign_and_seats()
{
- var store = new MasterDataStore();
+
var set = MasterDataSet.Empty with
{
Vehicles = new[]
@@ -79,9 +79,9 @@ public void Vehicles_round_trip_with_wache_callsign_and_seats()
new Vehicle("Aich", "Aich 42/1", 6),
},
};
- store.Save(_path, set);
+ MasterDataStore.Save(_path, set);
- var reopened = store.GetOrCreate(_path);
+ var reopened = MasterDataStore.GetOrCreate(_path);
Assert.Equal(set.Vehicles, reopened.Vehicles);
}
@@ -89,13 +89,13 @@ public void Vehicles_round_trip_with_wache_callsign_and_seats()
[Fact]
public void Personnel_optional_fields_round_trip_as_null()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with
{
Personnel = new[] { new Person("Musterfrau", "Erika", null, null, null) },
});
- var erika = store.GetOrCreate(_path).Personnel.Single();
+ var erika = MasterDataStore.GetOrCreate(_path).Personnel.Single();
Assert.Null(erika.Role);
Assert.Null(erika.CallSign);
Assert.Null(erika.Phone);
@@ -104,8 +104,8 @@ public void Personnel_optional_fields_round_trip_as_null()
[Fact]
public void Personnel_come_back_name_sorted()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with
{
Personnel = new[]
{
@@ -114,24 +114,24 @@ public void Personnel_come_back_name_sorted()
},
});
- var names = store.GetOrCreate(_path).Personnel.Select(p => p.LastName).ToList();
+ var names = MasterDataStore.GetOrCreate(_path).Personnel.Select(p => p.LastName).ToList();
Assert.Equal(new[] { "Amsel", "Zieger" }, names);
}
[Fact]
public void Save_round_trips_an_added_and_a_removed_street()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with
{
Streets = new[] { new Street("Alt Str.", "FFB"), new Street("Bahnhofstr.", "FFB") },
});
- var current = store.GetOrCreate(_path);
+ var current = MasterDataStore.GetOrCreate(_path);
var edited = current with { Streets = current.Streets.Skip(1).Append(new Street("Neu Str.", "Aich")).ToList() };
- store.Save(_path, edited);
+ MasterDataStore.Save(_path, edited);
- var reopened = store.GetOrCreate(_path);
+ var reopened = MasterDataStore.GetOrCreate(_path);
Assert.Contains(reopened.Streets, s => s.Name == "Neu Str." && s.District == "Aich");
Assert.DoesNotContain(reopened.Streets, s => s.Name == "Alt Str.");
Assert.Equal(2, reopened.Streets.Count);
@@ -140,17 +140,17 @@ public void Save_round_trips_an_added_and_a_removed_street()
[Fact]
public void Save_round_trips_an_added_and_a_removed_link()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with
{
Links = new[] { new Link("Alt Link", "https://old.example"), new Link("Wetterdienst", "https://dwd.de") },
});
- var current = store.GetOrCreate(_path);
+ var current = MasterDataStore.GetOrCreate(_path);
var edited = current with { Links = current.Links.Skip(1).Append(new Link("Neu Link", "https://new.example")).ToList() };
- store.Save(_path, edited);
+ MasterDataStore.Save(_path, edited);
- var reopened = store.GetOrCreate(_path);
+ var reopened = MasterDataStore.GetOrCreate(_path);
Assert.Contains(reopened.Links, l => l.Name == "Neu Link" && l.Url == "https://new.example");
Assert.DoesNotContain(reopened.Links, l => l.Name == "Alt Link");
Assert.Equal(2, reopened.Links.Count);
@@ -159,15 +159,15 @@ public void Save_round_trips_an_added_and_a_removed_link()
[Fact]
public void Save_round_trips_a_checklist_reorder_and_delete()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with
{
ChecklistTemplateAufbau = Items("A", "B", "C"),
});
- store.Save(_path, MasterDataSet.Empty with { ChecklistTemplateAufbau = Items("C", "A") });
+ MasterDataStore.Save(_path, MasterDataSet.Empty with { ChecklistTemplateAufbau = Items("C", "A") });
- Assert.Equal(Items("C", "A"), store.GetOrCreate(_path).ChecklistTemplateAufbau);
+ Assert.Equal(Items("C", "A"), MasterDataStore.GetOrCreate(_path).ChecklistTemplateAufbau);
static IReadOnlyList Items(params string[] texts) =>
texts.Select(t => new ChecklistTemplateItem(t, false)).ToList();
@@ -176,14 +176,14 @@ static IReadOnlyList Items(params string[] texts) =>
[Fact]
public void Save_round_trips_aufbau_and_abbau_independently_with_mandatory_flags()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with
{
ChecklistTemplateAufbau = new[] { new ChecklistTemplateItem("Fahrzeug prüfen", true) },
ChecklistTemplateAbbau = new[] { new ChecklistTemplateItem("Material zählen", false) },
});
- var reopened = store.GetOrCreate(_path);
+ var reopened = MasterDataStore.GetOrCreate(_path);
Assert.Equal(new ChecklistTemplateItem("Fahrzeug prüfen", true), Assert.Single(reopened.ChecklistTemplateAufbau));
Assert.Equal(new ChecklistTemplateItem("Material zählen", false), Assert.Single(reopened.ChecklistTemplateAbbau));
}
@@ -205,7 +205,7 @@ public void A_pre_split_database_widens_in_place_and_reads_everything_as_optiona
}
SqliteConnection.ClearAllPools();
- var set = new MasterDataStore().GetOrCreate(_path);
+ var set = MasterDataStore.GetOrCreate(_path);
Assert.Equal(new ChecklistTemplateItem("Altes Item", false), Assert.Single(set.ChecklistTemplateAufbau));
Assert.Empty(set.ChecklistTemplateAbbau);
@@ -214,12 +214,12 @@ public void A_pre_split_database_widens_in_place_and_reads_everything_as_optiona
[Fact]
public void A_value_deleted_through_Save_stays_deleted()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with { Roles = new[] { "EL", "ZF" } });
- store.Save(_path, MasterDataSet.Empty with { Roles = new[] { "ZF" } });
+ MasterDataStore.Save(_path, MasterDataSet.Empty with { Roles = new[] { "EL", "ZF" } });
- Assert.DoesNotContain("EL", store.GetOrCreate(_path).Roles);
+ MasterDataStore.Save(_path, MasterDataSet.Empty with { Roles = new[] { "ZF" } });
+
+ Assert.DoesNotContain("EL", MasterDataStore.GetOrCreate(_path).Roles);
}
[Fact]
@@ -229,24 +229,24 @@ public void A_person_without_a_first_name_displays_as_the_last_name_alone()
[Fact]
public void A_fresh_database_reads_the_default_settings()
{
- var set = new MasterDataStore().GetOrCreate(_path);
+ var set = MasterDataStore.GetOrCreate(_path);
Assert.Equal(IncidentSettings.Defaults, set.Settings);
}
[Fact]
public void Save_then_GetOrCreate_round_trips_settings()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with { Settings = new IncidentSettings(12, 33, 25, 18, 40, 4, 55) });
- Assert.Equal(new IncidentSettings(12, 33, 25, 18, 40, 4, 55), store.GetOrCreate(_path).Settings);
+ MasterDataStore.Save(_path, MasterDataSet.Empty with { Settings = new IncidentSettings(12, 33, 25, 18, 40, 4, 55) });
+
+ Assert.Equal(new IncidentSettings(12, 33, 25, 18, 40, 4, 55), MasterDataStore.GetOrCreate(_path).Settings);
}
[Fact]
public void A_missing_setting_key_falls_back_to_its_default()
{
- var store = new MasterDataStore();
- store.Save(_path, MasterDataSet.Empty with { Settings = new IncidentSettings(12, 33, 25, 18, 40, 4, 55) });
+
+ MasterDataStore.Save(_path, MasterDataSet.Empty with { Settings = new IncidentSettings(12, 33, 25, 18, 40, 4, 55) });
// Simulate a store written before a setting existed: drop one row, which read must backfill.
using (var cn = new SqliteConnection($"Data Source={_path}"))
@@ -258,7 +258,7 @@ public void A_missing_setting_key_falls_back_to_its_default()
}
SqliteConnection.ClearAllPools();
- var settings = store.GetOrCreate(_path).Settings;
+ var settings = MasterDataStore.GetOrCreate(_path).Settings;
Assert.Equal(IncidentSettings.Defaults.ReturnPressureBar, settings.ReturnPressureBar);
Assert.Equal(12, settings.IlsReminderIntervalMinutes); // the other keys are untouched
}
diff --git a/tests/LageBuch.Persistence.Tests/MigrationForwardCompatTests.cs b/tests/LageBuch.Persistence.Tests/MigrationForwardCompatTests.cs
index 8f952a8..a3967d4 100644
--- a/tests/LageBuch.Persistence.Tests/MigrationForwardCompatTests.cs
+++ b/tests/LageBuch.Persistence.Tests/MigrationForwardCompatTests.cs
@@ -49,8 +49,7 @@ public void Loading_a_pre_v2_incident_file_upgrades_and_does_not_crash()
var incident = Domain.Incident.Start(clock, op, "Brand");
incident.AddForceUnit(clock, op, "FFB", 12);
- var repo = new IncidentRepository();
- repo.Save(_path, incident);
+ IncidentRepository.Save(_path, incident);
using (var cn = SqliteConnectionFactory.OpenReadWrite(_path))
using (var cmd = cn.CreateCommand())
@@ -63,7 +62,7 @@ public void Loading_a_pre_v2_incident_file_upgrades_and_does_not_crash()
SqliteConnection.ClearAllPools();
// Act + Assert: opening the old file must upgrade it in place, not throw.
- var loaded = repo.Load(_path);
+ var loaded = IncidentRepository.Load(_path);
Assert.Equal("Brand", loaded.Keyword);
Assert.Equal(12, loaded.TotalPersonnel);