test(#1394): pre-existing recover-token specs follow the new non-TTY path
ci/woodpecker/pr/ci Pipeline was successful

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).
This commit is contained in:
2026-08-25 10:41:14 -05:00
parent 96eb0fb010
commit 4dfbab868d
@@ -16,11 +16,20 @@ vi.mock('./daemon.js', () => ({
vi.mock('./login.js', () => ({ vi.mock('./login.js', () => ({
getGatewayUrl: vi.fn().mockReturnValue('http://localhost:14242'), 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('[email protected]'), promptLine: vi.fn().mockResolvedValue('[email protected]'),
promptSecret: vi.fn().mockResolvedValue('test-password'), 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: '[email protected]',
password: 'test-password',
}),
}));
const mockFetch = vi.fn(); const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch); vi.stubGlobal('fetch', mockFetch);
@@ -65,7 +74,7 @@ describe('ensureSession', () => {
expect(mockSignIn).not.toHaveBeenCalled(); 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: '[email protected]' }); mockLoadSession.mockReturnValueOnce({ cookie: 'old-cookie', userId: 'u1', email: '[email protected]' });
mockValidateSession.mockResolvedValueOnce(false); mockValidateSession.mockResolvedValueOnce(false);
const newAuth = { cookie: fakeCookie, userId: 'u2', email: '[email protected]' }; const newAuth = { cookie: fakeCookie, userId: 'u2', email: '[email protected]' };
@@ -76,7 +85,7 @@ describe('ensureSession', () => {
expect(mockSaveSession).toHaveBeenCalledWith(baseUrl, newAuth); 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); mockLoadSession.mockReturnValueOnce(null);
const newAuth = { cookie: fakeCookie, userId: 'u2', email: '[email protected]' }; const newAuth = { cookie: fakeCookie, userId: 'u2', email: '[email protected]' };
mockSignIn.mockResolvedValueOnce(newAuth); mockSignIn.mockResolvedValueOnce(newAuth);
@@ -84,6 +93,10 @@ describe('ensureSession', () => {
const cookie = await ensureSession(baseUrl); const cookie = await ensureSession(baseUrl);
expect(cookie).toBe(fakeCookie); expect(cookie).toBe(fakeCookie);
expect(mockSignIn).toHaveBeenCalled(); 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 () => { it('exits non-zero when signIn fails', async () => {
@@ -111,7 +124,7 @@ describe('runRecoverToken', () => {
vi.spyOn(console, 'error').mockImplementation(() => {}); 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); mockLoadSession.mockReturnValueOnce(null);
const newAuth = { cookie: fakeCookie, userId: 'u2', email: '[email protected]' }; const newAuth = { cookie: fakeCookie, userId: 'u2', email: '[email protected]' };
mockSignIn.mockResolvedValueOnce(newAuth); mockSignIn.mockResolvedValueOnce(newAuth);