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);
}
}