using DodoSSH.Contracts; using DodoSSH.Domain; namespace DodoSSH.Api.Tests; /// /// The two entity-type enums have to agree, and nothing but this makes them. /// /// /// /// SyncService converts between the wire's and the change log's /// with a raw (ChangeEntityType)(int) cast in both directions. That /// works only because two independently maintained enums in two assemblies happen to number their members /// identically — and they do not even name them identically: Host = 1 against SshHost = 1. /// /// /// If they ever drift, nothing fails loudly. Changes get filed in the log under a different item type's /// name, and the next delta pull loads rows of the wrong type — or none — for entities the client asked /// about. That is silent data loss on the sync path, which is precisely the failure this project has /// designed everything else to avoid, so the alignment gets a test rather than a comment. /// /// /// Numeric alignment only. The two lists are deliberately not aligned with /// CryptoSpec.AadResourceType, which also carries None/User/Device/Vault and therefore numbers the /// same item types differently — asserting three-way equality would be asserting something false. /// /// public sealed class EntityTypeAlignmentTests { [Fact] public void EveryWireEntityType_HasAChangeLogTypeWithTheSameValue() { var changeValues = Enum.GetValues().Select(value => (int)value).ToHashSet(); foreach (var wire in Enum.GetValues()) { changeValues.ShouldContain( (int)wire, $"SyncEntityType.{wire} = {(int)wire} has no ChangeEntityType with that value, so " + "SyncService's cast would produce an undefined enum value."); } } [Fact] public void EveryChangeLogType_HasAWireTypeWithTheSameValue() { // The other direction matters just as much: a pull converts stored changes back to wire types, so a // change-log type with no wire counterpart would be served to clients as an undefined enum. var wireValues = Enum.GetValues().Select(value => (int)value).ToHashSet(); foreach (var change in Enum.GetValues()) { wireValues.ShouldContain( (int)change, $"ChangeEntityType.{change} = {(int)change} has no SyncEntityType with that value."); } } [Fact] public void TheTwoEnums_HaveTheSameNumberOfMembers() { // Catches a member added to one list only, which the two checks above would miss if it reused a // value already present in the other. Enum.GetValues().Length.ShouldBe(Enum.GetValues().Length); } /// /// Spot-checked by name as well, because the pairing that is easiest to get wrong is the one whose names /// differ. Someone adding an item type may reasonably assume the lists are name-matched, notice /// Host has no Host on the other side, and renumber to "fix" it. /// [Fact] public void TheDifferentlyNamedPair_IsTheOneThatMatches() { ((int)SyncEntityType.Host).ShouldBe((int)ChangeEntityType.SshHost); ((int)SyncEntityType.SshKey).ShouldBe((int)ChangeEntityType.SshKey); } }