From 009b35e06901a7218dac1ad3564c7a39eb2e2ca3 Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Sat, 8 Aug 2026 22:19:46 +0200 Subject: [PATCH] Cover the mixed-keychain regroup refusal headlessly The two-keychain branch of VaultViewModel.RegroupChosenHosts had no test: 7.6a's manual walk was the only thing asserting that a mixed set gets the sentence instead of the picker. A ShellFlowTests case now ticks a host in each of two vaults, reads the refusal off the status line, and shows the same command opening the picker once the set is one keychain's again. Check 7.6a cites the test and keeps only the popup wiring for the eye. --- docs/manual-checks.md | 10 ++-- .../ShellFlowTests.cs | 54 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) 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.