66 * db/user, and interactive-login to cover all branches without real HTTP
77 * calls or database access.
88 *
9- * The interactive TTY prompt tests use mock.module () at the top of this file
9+ * The interactive TTY prompt tests use vi.mock () at the top of this file
1010 * to stub node:tty (so isatty(0) returns true) and the logger module (so
1111 * `.prompt()` is controllable).
1212 */
@@ -16,56 +16,63 @@ import {
1616 beforeEach ,
1717 describe ,
1818 expect ,
19- mock ,
20- spyOn ,
2119 test ,
22- } from "bun:test" ;
20+ vi ,
21+ } from "vitest" ;
2322
2423// Mock isatty to simulate interactive terminal for the re-auth prompt path.
2524// Bun's ESM wrapper for CJS built-ins exposes `default` + `ReadStream` +
2625// `WriteStream` — all must be present.
27- const mockIsatty = mock ( ( ) => false ) ;
28- class FakeReadStream { }
29- class FakeWriteStream { }
30- const ttyExports = {
31- isatty : mockIsatty ,
32- ReadStream : FakeReadStream ,
33- WriteStream : FakeWriteStream ,
34- } ;
35- mock . module ( "node:tty" , ( ) => ( {
26+ const { mockIsatty, FakeReadStream, FakeWriteStream, ttyExports, noop, mockPrompt, fakeLog } = vi . hoisted ( ( ) => {
27+ const mockIsatty = vi . fn ( ( ) => false ) ;
28+ class FakeReadStream { }
29+ class FakeWriteStream { }
30+ const ttyExports = {
31+ isatty : mockIsatty ,
32+ ReadStream : FakeReadStream ,
33+ WriteStream : FakeWriteStream ,
34+ } ;
35+
36+ /** No-op placeholder for unused logger methods. */
37+ function noop ( ) {
38+ // intentional no-op
39+ }
40+
41+ // Mock the logger module to intercept the .prompt() call made by the
42+ // module-scoped `log = logger.withTag("auth.login")` in login.ts.
43+ const mockPrompt = vi . fn (
44+ ( ) : Promise < boolean | symbol > => Promise . resolve ( true )
45+ ) ;
46+ const fakeLog : {
47+ prompt : typeof mockPrompt ;
48+ info : ReturnType < typeof vi . fn > ;
49+ warn : ReturnType < typeof vi . fn > ;
50+ error : ReturnType < typeof vi . fn > ;
51+ debug : ReturnType < typeof vi . fn > ;
52+ success : ReturnType < typeof vi . fn > ;
53+ withTag : ( ) => typeof fakeLog ;
54+ } = {
55+ prompt : mockPrompt ,
56+ info : vi . fn ( noop ) ,
57+ warn : vi . fn ( noop ) ,
58+ error : vi . fn ( noop ) ,
59+ debug : vi . fn ( noop ) ,
60+ success : vi . fn ( noop ) ,
61+ withTag : ( ) => fakeLog ,
62+ } ;
63+
64+ return { mockIsatty, FakeReadStream, FakeWriteStream, ttyExports, noop, mockPrompt, fakeLog } ;
65+ } ) ;
66+
67+ vi . mock ( "node:tty" , ( ) => ( {
3668 ...ttyExports ,
3769 default : ttyExports ,
3870} ) ) ;
3971
40- /** No-op placeholder for unused logger methods. */
41- function noop ( ) {
42- // intentional no-op
43- }
44-
45- // Mock the logger module to intercept the .prompt() call made by the
46- // module-scoped `log = logger.withTag("auth.login")` in login.ts.
47- const mockPrompt = mock ( ( ) : Promise < boolean | symbol > => Promise . resolve ( true ) ) ;
48- const fakeLog : {
49- prompt : typeof mockPrompt ;
50- info : ReturnType < typeof mock > ;
51- warn : ReturnType < typeof mock > ;
52- error : ReturnType < typeof mock > ;
53- debug : ReturnType < typeof mock > ;
54- success : ReturnType < typeof mock > ;
55- withTag : ( ) => typeof fakeLog ;
56- } = {
57- prompt : mockPrompt ,
58- info : mock ( noop ) ,
59- warn : mock ( noop ) ,
60- error : mock ( noop ) ,
61- debug : mock ( noop ) ,
62- success : mock ( noop ) ,
63- withTag : ( ) => fakeLog ,
64- } ;
65- mock . module ( "../../../src/lib/logger.js" , ( ) => ( {
72+ vi . mock ( "../../../src/lib/logger.js" , ( ) => ( {
6673 logger : fakeLog ,
67- setLogLevel : mock ( noop ) ,
68- attachSentryReporter : mock ( noop ) ,
74+ setLogLevel : vi . fn ( noop ) ,
75+ attachSentryReporter : vi . fn ( noop ) ,
6976 LOG_LEVEL_NAMES : [ "error" , "warn" , "log" , "info" , "debug" , "trace" ] ,
7077 LOG_LEVEL_ENV_VAR : "SENTRY_LOG_LEVEL" ,
7178 parseLogLevel : ( name : string ) => {
@@ -76,7 +83,7 @@ mock.module("../../../src/lib/logger.js", () => ({
7683 getEnvLogLevel : ( ) => null ,
7784} ) ) ;
7885
79- // Dynamic import: must run AFTER mock.module () so login.ts picks up fakeLog.
86+ // Dynamic import: must run AFTER vi.mock () so login.ts picks up fakeLog.
8087const { loginCommand, rcTokenHint } = await import (
8188 "../../../src/commands/auth/login.js"
8289) ;
@@ -121,12 +128,12 @@ function createContext() {
121128 const stdoutChunks : string [ ] = [ ] ;
122129 const context = {
123130 stdout : {
124- write : mock ( ( s : string ) => {
131+ write : vi . fn ( ( s : string ) => {
125132 stdoutChunks . push ( s ) ;
126133 } ) ,
127134 } ,
128135 stderr : {
129- write : mock ( ( _s : string ) => {
136+ write : vi . fn ( ( _s : string ) => {
130137 // unused — diagnostics go through logger
131138 } ) ,
132139 } ,
@@ -165,19 +172,19 @@ describe("loginCommand.func --token path", () => {
165172 let func : LoginFunc ;
166173
167174 beforeEach ( async ( ) => {
168- isAuthenticatedSpy = spyOn ( dbAuth , "isAuthenticated" ) ;
169- isEnvTokenActiveSpy = spyOn ( dbAuth , "isEnvTokenActive" ) ;
170- setAuthTokenSpy = spyOn ( dbAuth , "setAuthToken" ) ;
171- getUserRegionsSpy = spyOn ( apiClient , "getUserRegions" ) ;
172- clearAuthSpy = spyOn ( dbAuth , "clearAuth" ) ;
173- getCurrentUserSpy = spyOn ( apiClient , "getCurrentUser" ) ;
174- setUserInfoSpy = spyOn ( dbUser , "setUserInfo" ) ;
175- runInteractiveLoginSpy = spyOn ( interactiveLogin , "runInteractiveLogin" ) ;
176- hasStoredAuthCredentialsSpy = spyOn ( dbAuth , "hasStoredAuthCredentials" ) ;
175+ isAuthenticatedSpy = vi . spyOn ( dbAuth , "isAuthenticated" ) ;
176+ isEnvTokenActiveSpy = vi . spyOn ( dbAuth , "isEnvTokenActive" ) ;
177+ setAuthTokenSpy = vi . spyOn ( dbAuth , "setAuthToken" ) ;
178+ getUserRegionsSpy = vi . spyOn ( apiClient , "getUserRegions" ) ;
179+ clearAuthSpy = vi . spyOn ( dbAuth , "clearAuth" ) ;
180+ getCurrentUserSpy = vi . spyOn ( apiClient , "getCurrentUser" ) ;
181+ setUserInfoSpy = vi . spyOn ( dbUser , "setUserInfo" ) ;
182+ runInteractiveLoginSpy = vi . spyOn ( interactiveLogin , "runInteractiveLogin" ) ;
183+ hasStoredAuthCredentialsSpy = vi . spyOn ( dbAuth , "hasStoredAuthCredentials" ) ;
177184 // Prevent warmOrgCache() fire-and-forget from hitting real fetch.
178185 // After successful login, warmOrgCache() calls listOrganizationsUncached()
179186 // which triggers API calls that leak as "unexpected fetch" warnings.
180- listOrgsUncachedSpy = spyOn ( apiClient , "listOrganizationsUncached" ) ;
187+ listOrgsUncachedSpy = vi . spyOn ( apiClient , "listOrganizationsUncached" ) ;
181188 listOrgsUncachedSpy . mockResolvedValue ( [ ] ) ;
182189 isEnvTokenActiveSpy . mockReturnValue ( false ) ;
183190 hasStoredAuthCredentialsSpy . mockReturnValue ( false ) ;
@@ -429,7 +436,7 @@ describe("loginCommand.func --token path", () => {
429436/**
430437 * Tests for the interactive TTY re-authentication prompt.
431438 *
432- * Uses the module-level `mock.module ()` on node:tty (so `isatty(0)` returns
439+ * Uses the module-level `vi.mock ()` on node:tty (so `isatty(0)` returns
433440 * true) and the logger (so `.prompt()` is controllable).
434441 */
435442describe ( "login re-authentication interactive prompt" , ( ) => {
@@ -443,20 +450,20 @@ describe("login re-authentication interactive prompt", () => {
443450
444451 function createPromptContext ( ) {
445452 return {
446- stdout : { write : mock ( ( ) => true ) } ,
447- stderr : { write : mock ( ( ) => true ) } ,
453+ stdout : { write : vi . fn ( ( ) => true ) } ,
454+ stderr : { write : vi . fn ( ( ) => true ) } ,
448455 cwd : "/tmp" ,
449456 } ;
450457 }
451458
452459 beforeEach ( async ( ) => {
453- isAuthenticatedSpy = spyOn ( dbAuth , "isAuthenticated" ) ;
454- isEnvTokenActiveSpy = spyOn ( dbAuth , "isEnvTokenActive" ) ;
455- clearAuthSpy = spyOn ( dbAuth , "clearAuth" ) ;
456- runInteractiveLoginSpy = spyOn ( interactiveLogin , "runInteractiveLogin" ) ;
457- getUserInfoSpy = spyOn ( dbUser , "getUserInfo" ) ;
460+ isAuthenticatedSpy = vi . spyOn ( dbAuth , "isAuthenticated" ) ;
461+ isEnvTokenActiveSpy = vi . spyOn ( dbAuth , "isEnvTokenActive" ) ;
462+ clearAuthSpy = vi . spyOn ( dbAuth , "clearAuth" ) ;
463+ runInteractiveLoginSpy = vi . spyOn ( interactiveLogin , "runInteractiveLogin" ) ;
464+ getUserInfoSpy = vi . spyOn ( dbUser , "getUserInfo" ) ;
458465 // Prevent warmOrgCache() fire-and-forget from hitting real fetch.
459- listOrgsUncachedSpy = spyOn ( apiClient , "listOrganizationsUncached" ) ;
466+ listOrgsUncachedSpy = vi . spyOn ( apiClient , "listOrganizationsUncached" ) ;
460467 listOrgsUncachedSpy . mockResolvedValue ( [ ] ) ;
461468
462469 // Defaults
@@ -567,18 +574,18 @@ describe("login re-authentication interactive prompt", () => {
567574 getUserInfoSpy . mockReturnValue ( undefined ) ;
568575 mockPrompt . mockResolvedValue ( true ) ;
569576
570- const setAuthTokenSpy = spyOn ( dbAuth , "setAuthToken" ) ;
577+ const setAuthTokenSpy = vi . spyOn ( dbAuth , "setAuthToken" ) ;
571578 setAuthTokenSpy . mockImplementation ( noop ) ;
572- const getUserRegionsSpy = spyOn ( apiClient , "getUserRegions" ) ;
579+ const getUserRegionsSpy = vi . spyOn ( apiClient , "getUserRegions" ) ;
573580 getUserRegionsSpy . mockResolvedValue ( [ ] ) ;
574- const getCurrentUserSpy = spyOn ( apiClient , "getCurrentUser" ) ;
581+ const getCurrentUserSpy = vi . spyOn ( apiClient , "getCurrentUser" ) ;
575582 getCurrentUserSpy . mockResolvedValue ( {
576583 id : "42" ,
577584 name : "Jane" ,
578585 username : "jane" ,
579586 email : "jane@example.com" ,
580587 } ) ;
581- const setUserInfoSpy = spyOn ( dbUser , "setUserInfo" ) ;
588+ const setUserInfoSpy = vi . spyOn ( dbUser , "setUserInfo" ) ;
582589 setUserInfoSpy . mockReturnValue ( undefined ) ;
583590
584591 const context = createPromptContext ( ) ;
0 commit comments