using DodoSSH.Contracts; namespace DodoSSH.Client.Sync.Tests; /// /// The registry of synchronised item types, and the one property that has to hold about it. /// /// /// Adding an item type touches a cipher, a codec, a merge, a repository and a view. The failure this pins is /// the one that none of those would reveal: a type that can be created, encrypted, merged and listed /// perfectly, and is never asked for in a pull — so it works on the machine that made it and exists nowhere /// else. Deriving the filter from the registry is what prevents it; these tests are what notice if the /// derivation stops holding. /// public sealed class ItemKindsTests { [Fact] public void ThePullFilterNamesEveryTypeThisBuildSynchronises() { ItemKinds.SyncedTypes.ShouldBe( [ SyncEntityType.Host, SyncEntityType.SshKey, SyncEntityType.Credential, SyncEntityType.KnownHostKey, SyncEntityType.HostGroup, SyncEntityType.Snippet, SyncEntityType.ConnectionLogEntry, SyncEntityType.ActivityLogEntry, SyncEntityType.ObjectStore, ]); } [Fact] public void ThePullFilterIsNotEmpty() { // Stated separately from the list above because the consequence of an empty one is quiet rather // than loud: the contract says an empty filter means every type, so the client would ask the server // for everything it holds and then discard most of the answer in ApplyAsync. The way it could // actually become empty is a static initialisation order slip — SyncedTypes projects Registry, and a // reordering of the two declarations would leave it reading an unassigned array. ItemKinds.SyncedTypes.ShouldNotBeEmpty(); } [Fact] public async Task EveryTypeInThePullFilter_HasAReconciler() { using var harness = await SyncHarness.CreateAsync(); var reconcilers = ItemKinds.Reconcilers( harness.First.Outbox, harness.First.Conflicts, harness.First.Keyring); reconcilers.Keys.Order().ShouldBe(ItemKinds.SyncedTypes.Order()); } }