diff --git a/docs/manual-checks.md b/docs/manual-checks.md index 6d97601..46926ea 100644 --- a/docs/manual-checks.md +++ b/docs/manual-checks.md @@ -984,10 +984,12 @@ across that line would leave everyone else in the shared vault seeing a machine **Failure means:** the set's write is `ChangingTheGroupOfTheChosenHosts_FilesThemAllAtOnce` again — one command reads the whole set, so "filed one of three" has no half-gesture to hide in the way the old drag's -payload did. The refusal, though, is asserted by **nothing automated at all**: `VaultViewModel.RegroupChosenHosts` -counts the distinct vaults and no test raises it, so this check is the only thing between that sentence and -silence. A panel that does open over a mixed set is the worse half — it would offer one keychain's groups -for another keychain's machines, which is the half-filed set the refusal exists to prevent. +payload did. The refusal is covered headlessly too: +`RegroupingHostsChosenAcrossTwoKeychains_IsRefusedBeforeThePickerOpens` ticks a host in each of two +keychains and asserts no picker opens and the sentence is on the status line. What is left for the eye is +7.9's wiring, as in 7.6 — and a panel that does open over a mixed set is the worse half: it would offer one +keychain's groups for another keychain's machines, which is the half-filed set the refusal exists to +prevent. ### 7.7 A click still selects, and a double click still connects diff --git a/tests/DodoSSH.Client.App.Tests/ShellFlowTests.cs b/tests/DodoSSH.Client.App.Tests/ShellFlowTests.cs index 88887b7..898525f 100644 --- a/tests/DodoSSH.Client.App.Tests/ShellFlowTests.cs +++ b/tests/DodoSSH.Client.App.Tests/ShellFlowTests.cs @@ -5845,6 +5845,60 @@ public sealed class ShellFlowTests : IAsyncLifetime Host(vault, "staging").Host.GroupId.ShouldBeNull("it was never ticked"); } + /// + /// The other refusal at the same door, and the one that needs two keychains to raise: a group is an + /// item of one vault, so filing a mixed set under it would leave everyone else in the shared vault + /// seeing a machine filed under nothing. Checked when the picker is asked for and over the whole set — + /// see — rather than once per host mid-write, which is + /// why no panel opens at all and the status line's sentence has to carry the whole explanation. + /// + [Fact] + public async Task RegroupingHostsChosenAcrossTwoKeychains_IsRefusedBeforeThePickerOpens() + { + await UnlockedAsync(); + + var vaults = shell.Vaults; + + await vaults.LoadAsync(Token); + + vaults.NewVaultCommand.Execute(null); + vaults.NewVaultName = "Platform secrets"; + + await vaults.CreateVaultCommand.ExecuteAsync(null); + + var vault = shell.Vault!; + var sharedVaultId = vaults.SelectedVault!.VaultId; + + await vault.LoadAsync(Token); + + await AddHostAsync(vault, "prod-db"); + + vault.NewHostCommand.Execute(null); + vault.EditorSelectedVault = + vault.EditorVaultChoices.Single(choice => choice.VaultId == sharedVaultId); + vault.EditorLabel = "prod-web"; + vault.EditorHostname = "web.internal"; + + await vault.SaveHostCommand.ExecuteAsync(null); + + vault.ChooseHostCommand.Execute(Host(vault, "prod-db")); + vault.ToggleHostChoiceCommand.Execute(Host(vault, "prod-web")); + + vault.RegroupChosenHostsCommand.Execute(null); + + vault.IsRegroupingChosenHosts.ShouldBeFalse("a group belongs to one keychain"); + vault.Status.ShouldStartWith("These hosts are in more than one keychain"); + vault.IsChoosingHosts.ShouldBeTrue("the set was refused, not dissolved"); + + // Unticking the visitor is all it takes: the refusal is about the set, not a latch the screen has + // to be talked out of. + vault.ToggleHostChoiceCommand.Execute(Host(vault, "prod-web")); + + vault.RegroupChosenHostsCommand.Execute(null); + + vault.IsRegroupingChosenHosts.ShouldBeTrue(vault.Status); + } + /// /// Duplicating keeps the group and the tags, which is the whole difference between it and a copy into /// another vault: the copy stays in the same keychain, so everything it points at is still there.