From 60f69d0b80c4950224d1ae56fb74b3b594f391bb Mon Sep 17 00:00:00 2001 From: Vladimir Rogojin Date: Tue, 26 May 2026 22:08:30 +0200 Subject: [PATCH] fix(cli)(sphere-sdk#282): route `wallet use` confirmation to STDERR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Residual #2 of the §D `manual-test-full-recovery.sh` ALL GREEN campaign. The `sphere wallet use ` subcommand printed its confirmation lines ✓ Switched to wallet profile: Nametag: L1 Addr: via `console.log` (stdout). The harness captures `sphere balance > file` snapshots; some snapshot blocks bracket the `wallet use` invocation outside the redirect (peer2: `sphere wallet use alice ; sphere balance > file`), others inside a subshell (peer1: `( … sphere wallet use alice && sphere balance ) > file`). The two flows yield different captured-stdout content for the same logical operation, so the resulting peer1-vs-peer2 diff failed assertion even though both wallets had identical balances. Fix: route the entire confirmation block (success and `(wallet not initialized in this profile)` fallback) through `console.error`. Errors (usage hint, profile-not-found) were already on stderr, so this brings the success path in line with them. Behaviour for human operators is unchanged — terminal sessions still see the banner; only `>` / `|` stdout pipelines are now unaffected by it. Side-benefit: any future shell tooling that pipes `sphere wallet use | …` no longer has to filter the banner out of the consumed stream. Tests * `test/integration/cli-wallet-profile.integration.test.ts` — the "`wallet use alice` switches the active profile" assertion now expects the banner on `r.stderr` (with a negative match on `r.stdout`) per the new contract. * Full integration suite: 25 / 25 passed. * Full default unit suite: 119 / 119 passed. Refs sphere-sdk#282 --- src/legacy/legacy-cli.ts | 18 ++++++++++++++---- .../cli-wallet-profile.integration.test.ts | 6 +++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/legacy/legacy-cli.ts b/src/legacy/legacy-cli.ts index 16fc81c..e097412 100644 --- a/src/legacy/legacy-cli.ts +++ b/src/legacy/legacy-cli.ts @@ -1982,19 +1982,29 @@ async function main(): Promise { } if (switchToProfile(profileName)) { - console.log(`✓ Switched to wallet profile: ${profileName}`); + // Issue sphere-sdk#282 Residual #2 — confirmation output + // goes to STDERR so that pipelines capturing the NEXT + // command's stdout (e.g. `sphere wallet use alice && + // sphere balance > file`) don't accidentally include the + // wallet-use banner in the captured snapshot. Without + // this, the same logical command sequence produces + // different captured-stdout content depending on whether + // the harness redirects the `wallet use` invocation + // separately or groups it in a subshell — see the + // peer1-vs-peer2 asymmetry in `manual-test-full-recovery.sh`. + console.error(`✓ Switched to wallet profile: ${profileName}`); // Show wallet status try { const sphere = await getSphere(); const identity = sphere.identity; if (identity) { - console.log(` Nametag: ${identity.nametag || '(not set)'}`); - console.log(` L1 Addr: ${identity.l1Address}`); + console.error(` Nametag: ${identity.nametag || '(not set)'}`); + console.error(` L1 Addr: ${identity.l1Address}`); } await closeSphere(); } catch { - console.log(' (wallet not initialized in this profile)'); + console.error(' (wallet not initialized in this profile)'); } } else { console.error(`Profile "${profileName}" not found.`); diff --git a/test/integration/cli-wallet-profile.integration.test.ts b/test/integration/cli-wallet-profile.integration.test.ts index eb44975..45c3dbe 100644 --- a/test/integration/cli-wallet-profile.integration.test.ts +++ b/test/integration/cli-wallet-profile.integration.test.ts @@ -230,7 +230,11 @@ describe('sphere-cli — wallet profile CRUD lifecycle (offline)', () => { it('`wallet use alice` switches the active profile', () => { const r = runSphere(env, ['wallet', 'use', 'alice'], { timeoutMs: 15_000 }); expect(r.status).toBe(0); - expect(r.stdout).toMatch(/Switched to wallet profile:\s*alice/); + // sphere-sdk#282 Residual #2 — confirmation lives on STDERR so + // downstream `sphere wallet use … && sphere balance > file` shell + // pipelines don't fold the banner into the captured snapshot. + expect(r.stderr).toMatch(/Switched to wallet profile:\s*alice/); + expect(r.stdout).not.toMatch(/Switched to wallet profile/); // Verify by re-reading current. const current = runSphere(env, ['wallet', 'current'], { timeoutMs: 15_000 });