using System.Text; namespace DodoSSH.Client.Domain.Tests; /// /// A group: one name, and the reasons it is only that. /// /// /// There is very little behaviour here to test, which is itself the design — every field that was considered /// and left out (a parent, a member list) was left out because of what it would do to the merge. What these /// tests pin is that the envelope round-trips, that a nameless group cannot be stored, and that renaming the /// same group on two machines is reported rather than silently resolved. /// public sealed class HostGroupSecretTests { [Fact] public void AGroup_RoundTrips() { var group = new HostGroupSecret { Label = "production" }; HostGroupSecretCodec.TryDecode(HostGroupSecretCodec.Encode(group), out var document) .ShouldBeTrue(); document.ShouldNotBeNull(); document.Group.ShouldBe(group); document.SchemaVersion.ShouldBe(HostGroupSecretCodec.CurrentSchemaVersion); document.IsReadOnly.ShouldBeFalse(); } [Theory] [InlineData("")] [InlineData(" ")] public void AGroupWithNoName_IsRefused(string label) { new HostGroupSecret { Label = label }.TryValidate(out var reason).ShouldBeFalse(); reason.ShouldNotBeNull(); } [Fact] public void AGroupWrittenByANewerClient_IsReadableButNotWritableHere() { var payload = Encoding.UTF8.GetBytes( """ {"schemaVersion":99,"label":"production","colour":"a field this build has never heard of"} """); HostGroupSecretCodec.TryDecode(payload, out var document).ShouldBeTrue(); document.ShouldNotBeNull(); document.Group.Label.ShouldBe("production"); document.IsReadOnly.ShouldBeTrue(); } [Fact] public void AnUnnamedPayload_FailsToDecodeRatherThanProducingABlankGroup() { // Failing closed, as every codec in this folder does: a group with no name is indistinguishable in // the sidebar from the ungrouped heading it would sit beside. var payload = Encoding.UTF8.GetBytes("""{"schemaVersion":1}"""); HostGroupSecretCodec.TryDecode(payload, out var document).ShouldBeFalse(); document.ShouldBeNull(); } [Fact] public void TwoDifferentRenames_AreReportedWithBothNames() { var ancestor = new HostGroupSecret { Label = "production" }; var result = HostGroupSecretMerge.Merge( ancestor, ancestor with { Label = "prod" }, ancestor with { Label = "live" }); result.Merged.Label.ShouldBe("live"); var conflict = result.Conflicts.ShouldHaveSingleItem(); conflict.Field.ShouldBe(nameof(HostGroupSecret.Label)); conflict.Kept.ShouldBe("live"); conflict.Discarded.ShouldBe("prod"); } /// /// The case that would collide if membership were held on the group instead of on each host: two people /// filing two different machines into one group at the same time. It cannot reach the merge at all, /// because neither of those actions writes to this item. /// [Fact] public void FilingHostsIntoAGroup_DoesNotTouchTheGroup() { var ancestor = new HostGroupSecret { Label = "production" }; var result = HostGroupSecretMerge.Merge(ancestor, ancestor, ancestor); result.HasConflicts.ShouldBeFalse(); result.Merged.ShouldBe(ancestor); } }