55 pollLoginStatus ,
66 type LoginUrlResponse ,
77} from '../../login/login-flow'
8+ import { createMockApiClient } from '../helpers/mock-api-client'
89
10+ import type { ApiResponse } from '../../utils/codebuff-api'
911import type { Logger } from '@codebuff/common/types/contracts/logger'
1012
1113type MockLogger = {
@@ -28,59 +30,70 @@ describe('First-Time Login Flow (helpers)', () => {
2830 expiresAt : '2025-12-31T23:59:59Z' ,
2931 }
3032
31- const fetchMock = mock ( async ( input : RequestInfo , init ?: RequestInit ) => {
32- expect ( typeof input ) . toBe ( 'string ' )
33- expect ( String ( input ) ) . toBe ( 'https://cli.test/api/auth/cli/code' )
34- expect ( init ?. method ) . toBe ( 'POST' )
35- expect ( init ?. headers ) . toEqual ( { 'Content-Type' : 'application/json' } )
36- expect ( init ?. body ) . toBe ( JSON . stringify ( { fingerprintId : 'finger-001' } ) )
37- return new Response ( JSON . stringify ( responsePayload ) , { status : 200 } )
33+ const loginCodeMock = mock ( async ( req : { fingerprintId : string } ) => {
34+ expect ( req . fingerprintId ) . toBe ( 'finger-001 ' )
35+ return {
36+ ok : true ,
37+ status : 200 ,
38+ data : responsePayload ,
39+ } as ApiResponse < LoginUrlResponse >
3840 } )
3941
42+ const apiClient = createMockApiClient ( { loginCode : loginCodeMock } )
43+
4044 const result = await generateLoginUrl (
41- { fetch : fetchMock as any , logger } ,
45+ { logger , apiClient } ,
4246 { baseUrl : 'https://cli.test' , fingerprintId : 'finger-001' } ,
4347 )
4448
4549 expect ( result ) . toEqual ( responsePayload )
46- expect ( fetchMock . mock . calls . length ) . toBe ( 1 )
50+ expect ( loginCodeMock . mock . calls . length ) . toBe ( 1 )
4751 } )
4852
4953 test ( 'pollLoginStatus resolves with user after handling transient 401 responses' , async ( ) => {
5054 const logger = createLogger ( )
51- const responses : Array < Response > = [
52- new Response ( null , { status : 401 } ) ,
53- new Response ( null , { status : 401 } ) ,
54- new Response (
55- JSON . stringify ( {
55+ const apiResponses : Array < ApiResponse < { user ?: unknown } > > = [
56+ { ok : false , status : 401 } ,
57+ { ok : false , status : 401 } ,
58+ {
59+ ok : true ,
60+ status : 200 ,
61+ data : {
5662 user : {
5763 id : 'new-user-123' ,
5864 name : 'New User' ,
5965 email : 'new@codebuff.dev' ,
6066 authToken : 'token-123' ,
6167 } ,
62- } ) ,
63- { status : 200 } ,
64- ) ,
68+ } ,
69+ } ,
6570 ]
6671 let callCount = 0
6772
68- const fetchMock = mock ( async ( input : RequestInfo ) => {
69- const url = new URL ( String ( input ) )
70- expect ( url . searchParams . get ( 'fingerprintId' ) ) . toBe ( 'finger-abc' )
71- expect ( url . searchParams . get ( 'fingerprintHash' ) ) . toBe ( 'hash-xyz' )
72- expect ( url . searchParams . get ( 'expiresAt' ) ) . toBe ( '2030-01-02T03:04:05Z' )
73+ const loginStatusMock = mock (
74+ async ( req : {
75+ fingerprintId : string
76+ fingerprintHash : string
77+ expiresAt : string
78+ } ) => {
79+ expect ( req . fingerprintId ) . toBe ( 'finger-abc' )
80+ expect ( req . fingerprintHash ) . toBe ( 'hash-xyz' )
81+ expect ( req . expiresAt ) . toBe ( '2030-01-02T03:04:05Z' )
82+
83+ const response =
84+ apiResponses [ callCount ] ?? apiResponses [ apiResponses . length - 1 ]
85+ callCount += 1
86+ return response
87+ } ,
88+ )
7389
74- const response = responses [ callCount ] ?? responses [ responses . length - 1 ]
75- callCount += 1
76- return response
77- } )
90+ const apiClient = createMockApiClient ( { loginStatus : loginStatusMock } )
7891
7992 const result = await pollLoginStatus (
8093 {
81- fetch : fetchMock as any ,
8294 sleep : async ( ) => { } ,
8395 logger,
96+ apiClient,
8497 } ,
8598 {
8699 baseUrl : 'https://cli.test' ,
@@ -97,7 +110,7 @@ describe('First-Time Login Flow (helpers)', () => {
97110 expect ( result . attempts ) . toBe ( 3 )
98111 const user = result . user as { id ?: unknown }
99112 expect ( user ?. id ) . toBe ( 'new-user-123' )
100- expect ( fetchMock . mock . calls . length ) . toBe ( 3 )
113+ expect ( loginStatusMock . mock . calls . length ) . toBe ( 3 )
101114 } )
102115
103116 test ( 'pollLoginStatus times out when user never appears' , async ( ) => {
@@ -106,20 +119,22 @@ describe('First-Time Login Flow (helpers)', () => {
106119 const intervalMs = 5000
107120 const timeoutMs = 20000
108121
109- const fetchMock = mock ( async ( ) => {
110- return new Response ( null , { status : 401 } )
122+ const loginStatusMock = mock ( async ( ) => {
123+ return { ok : false , status : 401 } as ApiResponse < { user ?: unknown } >
111124 } )
112125
126+ const apiClient = createMockApiClient ( { loginStatus : loginStatusMock } )
127+
113128 const sleep = async ( ) => {
114129 nowTime += intervalMs
115130 }
116131
117132 const result = await pollLoginStatus (
118133 {
119- fetch : fetchMock as any ,
120134 sleep,
121135 logger,
122136 now : ( ) => nowTime ,
137+ apiClient,
123138 } ,
124139 {
125140 baseUrl : 'https://cli.test' ,
@@ -132,26 +147,26 @@ describe('First-Time Login Flow (helpers)', () => {
132147 )
133148
134149 expect ( result . status ) . toBe ( 'timeout' )
135- expect ( fetchMock . mock . calls . length ) . toBeGreaterThan ( 0 )
150+ expect ( loginStatusMock . mock . calls . length ) . toBeGreaterThan ( 0 )
136151 } )
137152
138153 test ( 'pollLoginStatus stops when caller aborts' , async ( ) => {
139154 const logger = createLogger ( )
140- let attempts = 0
141- const fetchMock = mock ( async ( ) => {
142- attempts += 1
143- return new Response ( null , { status : 401 } )
155+ const loginStatusMock = mock ( async ( ) => {
156+ return { ok : false , status : 401 } as ApiResponse < { user ?: unknown } >
144157 } )
145158
159+ const apiClient = createMockApiClient ( { loginStatus : loginStatusMock } )
160+
146161 let shouldContinue = true
147162
148163 const resultPromise = pollLoginStatus (
149164 {
150- fetch : fetchMock as any ,
151165 sleep : async ( ) => {
152166 shouldContinue = false
153167 } ,
154168 logger,
169+ apiClient,
155170 } ,
156171 {
157172 baseUrl : 'https://cli.test' ,
@@ -164,6 +179,6 @@ describe('First-Time Login Flow (helpers)', () => {
164179
165180 const result = await resultPromise
166181 expect ( result . status ) . toBe ( 'aborted' )
167- expect ( fetchMock . mock . calls . length ) . toBeGreaterThan ( 0 )
182+ expect ( loginStatusMock . mock . calls . length ) . toBeGreaterThan ( 0 )
168183 } )
169184} )
0 commit comments