From 4dfbab868d774b8aa9c70470c44b5836c4f1c49a Mon Sep 17 00:00:00 2001 From: ops-deploy-01 Date: Tue, 25 Aug 2026 10:41:14 -0500 Subject: [PATCH] test(#1394): pre-existing recover-token specs follow the new non-TTY path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old specs mocked promptLine/promptSecret and relied on the prompt path being taken unconditionally — which is precisely the behavior #1394 removes (non-TTY must NOT sit on prompts). Under vitest stdin is non-TTY, so the new code correctly routed to piped-stdin credentials and the specs hung on the unmocked reader (4/7 timeouts). The specs now mock the piped-credentials seam with the same fixed values and assert that seam (not the prompts) was consulted; titles state the actual path. All 7 pass; suite 1667/1667 (remaining local failure is the known host pi-invariant, CI pins pi 0.84.1). --- .../commands/gateway/recover-token.spec.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/mosaic/src/commands/gateway/recover-token.spec.ts b/packages/mosaic/src/commands/gateway/recover-token.spec.ts index d00bc0db..aef6c75b 100644 --- a/packages/mosaic/src/commands/gateway/recover-token.spec.ts +++ b/packages/mosaic/src/commands/gateway/recover-token.spec.ts @@ -16,11 +16,20 @@ vi.mock('./daemon.js', () => ({ vi.mock('./login.js', () => ({ getGatewayUrl: vi.fn().mockReturnValue('http://localhost:14242'), - // promptLine/promptSecret are used by ensureSession; return fixed values so tests don't block on stdin + // promptLine/promptSecret are used by ensureSession on the TTY path; return fixed + // values so tests never block on stdin. promptLine: vi.fn().mockResolvedValue('test@example.com'), promptSecret: vi.fn().mockResolvedValue('test-password'), })); +// #1394: non-TTY runs resolve credentials from piped stdin instead of prompts. +vi.mock('./piped-credentials.js', () => ({ + readCredentialsFromPipedStdin: vi.fn().mockResolvedValue({ + email: 'test@example.com', + password: 'test-password', + }), +})); + const mockFetch = vi.fn(); vi.stubGlobal('fetch', mockFetch); @@ -65,7 +74,7 @@ describe('ensureSession', () => { expect(mockSignIn).not.toHaveBeenCalled(); }); - it('prompts for credentials and signs in when stored session is invalid', async () => { + it('resolves piped-stdin credentials and signs in when stored session is invalid', async () => { mockLoadSession.mockReturnValueOnce({ cookie: 'old-cookie', userId: 'u1', email: 'a@b.com' }); mockValidateSession.mockResolvedValueOnce(false); const newAuth = { cookie: fakeCookie, userId: 'u2', email: 'a@b.com' }; @@ -76,7 +85,7 @@ describe('ensureSession', () => { expect(mockSaveSession).toHaveBeenCalledWith(baseUrl, newAuth); }); - it('prompts for credentials when no session exists', async () => { + it('resolves piped-stdin credentials when no session exists', async () => { mockLoadSession.mockReturnValueOnce(null); const newAuth = { cookie: fakeCookie, userId: 'u2', email: 'a@b.com' }; mockSignIn.mockResolvedValueOnce(newAuth); @@ -84,6 +93,10 @@ describe('ensureSession', () => { const cookie = await ensureSession(baseUrl); expect(cookie).toBe(fakeCookie); expect(mockSignIn).toHaveBeenCalled(); + // The non-TTY path resolves credentials from the piped-stdin seam, not prompts. + expect( + vi.mocked(await import('./piped-credentials.js')).readCredentialsFromPipedStdin, + ).toHaveBeenCalled(); }); it('exits non-zero when signIn fails', async () => { @@ -111,7 +124,7 @@ describe('runRecoverToken', () => { vi.spyOn(console, 'error').mockImplementation(() => {}); }); - it('prompts for login, mints a token, and persists it when no session exists', async () => { + it('signs in via piped stdin, mints a token, and persists it when no session exists', async () => { mockLoadSession.mockReturnValueOnce(null); const newAuth = { cookie: fakeCookie, userId: 'u2', email: 'admin@test.com' }; mockSignIn.mockResolvedValueOnce(newAuth);