22 * Tests for device flow authentication
33 */
44
5- import {
5+ import { describe , it , expect , beforeEach , jest } from '@jest/globals' ;
6+
7+ // Mock the API client constructor function
8+ const mockPost = jest . fn ( ) ;
9+ const MockSplitmarkAPIClient = jest . fn ( ) . mockImplementation ( ( ) => ( {
10+ post : mockPost ,
11+ } ) ) ;
12+
13+ // Mock the credentials module
14+ const mockSaveToken = jest . fn ( ) ;
15+ const mockClearToken = jest . fn ( ) ;
16+
17+ // Use unstable_mockModule to properly mock ES modules
18+ jest . unstable_mockModule ( '../../../src/cloud/api/client.js' , ( ) => ( {
19+ default : MockSplitmarkAPIClient ,
20+ } ) ) ;
21+
22+ jest . unstable_mockModule ( '../../../src/cloud/storage/credentials.js' , ( ) => ( {
23+ saveToken : mockSaveToken ,
24+ clearToken : mockClearToken ,
25+ } ) ) ;
26+
27+ // Import the functions to test after mocking
28+ const {
629 initiateDeviceFlow,
730 pollDeviceToken,
831 completeDeviceFlow,
932 loginWithDeviceFlow,
10- } from '../../../src/cloud/api/auth.js' ;
11-
12- // Mock the API client
13- jest . mock ( '../../../src/cloud/api/client.js' , ( ) => {
14- return {
15- __esModule : true ,
16- default : jest . fn ( ) . mockImplementation ( ( ) => ( {
17- post : jest . fn ( ) ,
18- } ) ) ,
19- } ;
20- } ) ;
21-
22- // Mock credential storage
23- jest . mock ( '../../../src/cloud/storage/credentials.js' , ( ) => ( {
24- saveToken : jest . fn ( ) ,
25- } ) ) ;
33+ } = await import ( '../../../src/cloud/api/auth.js' ) ;
2634
2735describe ( 'Device Flow Authentication' , ( ) => {
2836 beforeEach ( ( ) => {
@@ -31,19 +39,14 @@ describe('Device Flow Authentication', () => {
3139
3240 describe ( 'initiateDeviceFlow' , ( ) => {
3341 it ( 'should initiate device flow and return codes' , async ( ) => {
34- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
35- const mockPost = jest . fn ( ) . mockResolvedValue ( {
36- deviceCode : 'device_123' ,
37- userCode : 'ABCD-1234' ,
38- verificationUrl : 'https://splitmark.app/cli-auth' ,
39- expiresIn : 600 ,
42+ mockPost . mockResolvedValue ( {
43+ device_code : 'device_123' ,
44+ user_code : 'ABCD-1234' ,
45+ verification_uri : 'https://splitmark.app/cli-auth' ,
46+ expires_in : 600 ,
4047 interval : 5 ,
4148 } ) ;
4249
43- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
44- post : mockPost ,
45- } ) ) ;
46-
4750 const result = await initiateDeviceFlow ( 'https://api.splitmark.app' ) ;
4851
4952 expect ( result ) . toEqual ( {
@@ -56,39 +59,29 @@ describe('Device Flow Authentication', () => {
5659 } ) ;
5760
5861 expect ( mockPost ) . toHaveBeenCalledWith (
59- '/cli/device-code' ,
62+ '/auth/ cli/device-code' ,
6063 { } ,
6164 { skipAuth : true , rateLimitType : 'auth' }
6265 ) ;
6366 } ) ;
6467
6568 it ( 'should handle verificationUrlComplete' , async ( ) => {
66- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
67- const mockPost = jest . fn ( ) . mockResolvedValue ( {
68- deviceCode : 'device_123' ,
69- userCode : 'ABCD-1234' ,
70- verificationUrl : 'https://splitmark.app/cli-auth' ,
71- verificationUrlComplete : 'https://splitmark.app/cli-auth?code=ABCD-1234' ,
72- expiresIn : 600 ,
69+ mockPost . mockResolvedValue ( {
70+ device_code : 'device_123' ,
71+ user_code : 'ABCD-1234' ,
72+ verification_uri : 'https://splitmark.app/cli-auth' ,
73+ verification_uri_complete : 'https://splitmark.app/cli-auth?code=ABCD-1234' ,
74+ expires_in : 600 ,
7375 interval : 5 ,
7476 } ) ;
7577
76- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
77- post : mockPost ,
78- } ) ) ;
79-
8078 const result = await initiateDeviceFlow ( 'https://api.splitmark.app' ) ;
8179
8280 expect ( result . verificationUrlComplete ) . toBe ( 'https://splitmark.app/cli-auth?code=ABCD-1234' ) ;
8381 } ) ;
8482
8583 it ( 'should handle API errors' , async ( ) => {
86- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
87- const mockPost = jest . fn ( ) . mockRejectedValue ( new Error ( 'Network error' ) ) ;
88-
89- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
90- post : mockPost ,
91- } ) ) ;
84+ mockPost . mockRejectedValue ( new Error ( 'Network error' ) ) ;
9285
9386 await expect ( initiateDeviceFlow ( 'https://api.splitmark.app' ) ) . rejects . toThrow (
9487 'Failed to initiate device flow'
@@ -98,25 +91,17 @@ describe('Device Flow Authentication', () => {
9891
9992 describe ( 'pollDeviceToken' , ( ) => {
10093 it ( 'should return pending when authorization is pending' , async ( ) => {
101- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
102- const mockPost = jest . fn ( ) . mockResolvedValue ( {
94+ mockPost . mockResolvedValue ( {
10395 error : 'authorization_pending' ,
10496 } ) ;
10597
106- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
107- post : mockPost ,
108- } ) ) ;
109-
11098 const result = await pollDeviceToken ( 'https://api.splitmark.app' , 'device_123' ) ;
11199
112100 expect ( result ) . toEqual ( { pending : true } ) ;
113101 } ) ;
114102
115103 it ( 'should return token and user on success' , async ( ) => {
116- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
117- const { saveToken } = require ( '../../../src/cloud/storage/credentials.js' ) ;
118-
119- const mockPost = jest . fn ( ) . mockResolvedValue ( {
104+ mockPost . mockResolvedValue ( {
120105 token : 'jwt_token_123' ,
121106 user : {
122107 id : 'user_123' ,
@@ -125,10 +110,6 @@ describe('Device Flow Authentication', () => {
125110 } ,
126111 } ) ;
127112
128- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
129- post : mockPost ,
130- } ) ) ;
131-
132113 const result = await pollDeviceToken ( 'https://api.splitmark.app' , 'device_123' ) ;
133114
134115 expect ( result ) . toEqual ( {
@@ -140,33 +121,25 @@ describe('Device Flow Authentication', () => {
140121 } ,
141122 } ) ;
142123
143- expect ( saveToken ) . toHaveBeenCalledWith ( 'jwt_token_123' , expect . any ( Object ) ) ;
124+ expect ( mockSaveToken ) . toHaveBeenCalledWith ( 'jwt_token_123' , expect . any ( Object ) ) ;
144125 } ) ;
145126
146127 it ( 'should throw error on expired token' , async ( ) => {
147- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
148- const mockPost = jest . fn ( ) . mockResolvedValue ( {
128+ mockPost . mockResolvedValue ( {
149129 error : 'expired_token' ,
150130 } ) ;
151131
152- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
153- post : mockPost ,
154- } ) ) ;
155132
156133 await expect (
157134 pollDeviceToken ( 'https://api.splitmark.app' , 'device_123' )
158135 ) . rejects . toThrow ( 'Device code expired' ) ;
159136 } ) ;
160137
161138 it ( 'should throw error on access denied' , async ( ) => {
162- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
163- const mockPost = jest . fn ( ) . mockResolvedValue ( {
139+ mockPost . mockResolvedValue ( {
164140 error : 'access_denied' ,
165141 } ) ;
166142
167- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
168- post : mockPost ,
169- } ) ) ;
170143
171144 await expect (
172145 pollDeviceToken ( 'https://api.splitmark.app' , 'device_123' )
@@ -176,10 +149,10 @@ describe('Device Flow Authentication', () => {
176149
177150 describe ( 'completeDeviceFlow' , ( ) => {
178151 it ( 'should poll until success' , async ( ) => {
179- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
152+ const { default : SplitmarkAPIClient } = await import ( '../../../src/cloud/api/client.js' ) ;
180153 let callCount = 0 ;
181154
182- const mockPost = jest . fn ( ) . mockImplementation ( ( ) => {
155+ mockPost . mockImplementation ( ( ) => {
183156 callCount ++ ;
184157 if ( callCount < 3 ) {
185158 return Promise . resolve ( { error : 'authorization_pending' } ) ;
@@ -190,9 +163,6 @@ describe('Device Flow Authentication', () => {
190163 } ) ;
191164 } ) ;
192165
193- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
194- post : mockPost ,
195- } ) ) ;
196166
197167 const progressUpdates = [ ] ;
198168 const onProgress = ( progress ) => progressUpdates . push ( progress ) ;
@@ -212,14 +182,10 @@ describe('Device Flow Authentication', () => {
212182 } ) ;
213183
214184 it ( 'should timeout after expiration' , async ( ) => {
215- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
216- const mockPost = jest . fn ( ) . mockResolvedValue ( {
185+ mockPost . mockResolvedValue ( {
217186 error : 'authorization_pending' ,
218187 } ) ;
219188
220- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
221- post : mockPost ,
222- } ) ) ;
223189
224190 await expect (
225191 completeDeviceFlow (
@@ -232,15 +198,11 @@ describe('Device Flow Authentication', () => {
232198 } , 10000 ) ;
233199
234200 it ( 'should call onProgress with correct status' , async ( ) => {
235- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
236- const mockPost = jest . fn ( ) . mockResolvedValue ( {
201+ mockPost . mockResolvedValue ( {
237202 token : 'jwt_token_123' ,
238203 user : { username : 'testuser' } ,
239204 } ) ;
240205
241- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
242- post : mockPost ,
243- } ) ) ;
244206
245207 const progressUpdates = [ ] ;
246208 const onProgress = ( progress ) => progressUpdates . push ( progress ) ;
@@ -264,19 +226,18 @@ describe('Device Flow Authentication', () => {
264226
265227 describe ( 'loginWithDeviceFlow' , ( ) => {
266228 it ( 'should complete full device flow' , async ( ) => {
267- const SplitmarkAPIClient = require ( '../../../src/cloud/api/client.js' ) . default ;
268229 let requestCount = 0 ;
269230
270- const mockPost = jest . fn ( ) . mockImplementation ( ( endpoint ) => {
271- if ( endpoint === '/cli/device-code' ) {
231+ mockPost . mockImplementation ( ( endpoint ) => {
232+ if ( endpoint === '/auth/ cli/device-code' ) {
272233 return Promise . resolve ( {
273- deviceCode : 'device_123' ,
274- userCode : 'ABCD-1234' ,
275- verificationUrl : 'https://splitmark.app/cli-auth' ,
276- expiresIn : 600 ,
234+ device_code : 'device_123' ,
235+ user_code : 'ABCD-1234' ,
236+ verification_uri : 'https://splitmark.app/cli-auth' ,
237+ expires_in : 600 ,
277238 interval : 0.1 ,
278239 } ) ;
279- } else if ( endpoint === '/cli/token' ) {
240+ } else if ( endpoint === '/auth/ cli/token' ) {
280241 requestCount ++ ;
281242 if ( requestCount < 2 ) {
282243 return Promise . resolve ( { error : 'authorization_pending' } ) ;
@@ -288,9 +249,6 @@ describe('Device Flow Authentication', () => {
288249 }
289250 } ) ;
290251
291- SplitmarkAPIClient . mockImplementation ( ( ) => ( {
292- post : mockPost ,
293- } ) ) ;
294252
295253 const progressUpdates = [ ] ;
296254 const onProgress = ( progress ) => progressUpdates . push ( progress ) ;
0 commit comments