55 * Claude Assistant
66 */
77
8- import { Test , TestingModule } from '@nestjs/testing' ;
98import { ConfigService } from '@nestjs/config' ;
9+ import { Test , TestingModule } from '@nestjs/testing' ;
10+ import * as fs from 'fs' ;
11+ import * as path from 'path' ;
1012import { OAuthService } from './oauth.service' ;
1113import {
1214 OAuthProvider ,
1315 OAuthProviderConfig ,
1416 OAuthUserInfo ,
1517} from './oauth.types' ;
16- import * as fs from 'fs' ;
17- import * as path from 'path' ;
1818
1919// Mock provider for testing
2020class MockOAuthProvider implements OAuthProvider {
@@ -72,17 +72,17 @@ describe('OAuthService', () => {
7272 useValue : {
7373 get : jest . fn ( ( key : string ) => {
7474 switch ( key ) {
75- case 'oauth.enabledProviders ' :
76- return [ 'test' ] ;
77- case 'oauth.pluginPaths ' :
78- return [ './test-plugins' ] ;
79- case 'oauth.allowNpmLoading ' :
75+ case 'OAUTH_ENABLED_PROVIDERS ' :
76+ return 'test' ;
77+ case 'OAUTH_PLUGIN_PATHS ' :
78+ return './test-plugins' ;
79+ case 'OAUTH_ALLOW_NPM_LOADING ' :
8080 return false ;
81- case 'oauth.test.clientId ' :
81+ case 'OAUTH_TEST_CLIENT_ID ' :
8282 return 'test-client-id' ;
83- case 'oauth.test.clientSecret ' :
83+ case 'OAUTH_TEST_CLIENT_SECRET ' :
8484 return 'test-client-secret' ;
85- case 'oauth.test.redirectUrl ' :
85+ case 'OAUTH_TEST_REDIRECT_URL ' :
8686 return 'http://localhost:3000/callback' ;
8787 default :
8888 return undefined ;
@@ -104,30 +104,32 @@ describe('OAuthService', () => {
104104 describe ( 'initialize' , ( ) => {
105105 it ( 'should initialize without providers when none are enabled' , async ( ) => {
106106 jest . spyOn ( configService , 'get' ) . mockImplementation ( ( key : string ) => {
107- if ( key === 'oauth.enabledProviders ' ) return [ ] ;
107+ if ( key === 'OAUTH_ENABLED_PROVIDERS ' ) return '' ;
108108 return undefined ;
109109 } ) ;
110110
111111 await service . initialize ( ) ;
112- expect ( service . getAvailableProviders ( ) ) . toEqual ( [ ] ) ;
112+ expect (
113+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
114+ ) . toEqual ( [ ] ) ;
113115 } ) ;
114116
115117 it ( 'should skip provider loading when config is missing' , async ( ) => {
116118 jest . spyOn ( configService , 'get' ) . mockImplementation ( ( key : string ) => {
117- if ( key === 'oauth.enabledProviders ' ) return [ 'test' ] ;
118- if ( key === 'oauth.test.clientId ' ) return undefined ;
119+ if ( key === 'OAUTH_ENABLED_PROVIDERS ' ) return 'test' ;
120+ if ( key === 'OAUTH_TEST_CLIENT_ID ' ) return undefined ;
119121 return undefined ;
120122 } ) ;
121123
122124 const consoleSpy = jest . spyOn ( console , 'warn' ) . mockImplementation ( ) ;
123125 await service . initialize ( ) ;
124126
125127 expect ( consoleSpy ) . toHaveBeenCalledWith (
126- expect . stringContaining (
127- 'OAuth provider test configuration is incomplete' ,
128- ) ,
128+ expect . stringContaining ( 'Missing configuration for OAuth provider' ) ,
129129 ) ;
130- expect ( service . getAvailableProviders ( ) ) . toEqual ( [ ] ) ;
130+ expect (
131+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
132+ ) . toEqual ( [ ] ) ;
131133 } ) ;
132134
133135 it ( 'should load provider from plugin file' , async ( ) => {
@@ -136,6 +138,8 @@ describe('OAuthService', () => {
136138
137139 const mockCreateProvider = jest . fn ( ) . mockReturnValue (
138140 new MockOAuthProvider ( {
141+ id : 'test' ,
142+ name : 'Test Provider' ,
139143 clientId : 'test-client-id' ,
140144 clientSecret : 'test-client-secret' ,
141145 redirectUrl : 'http://localhost:3000/callback' ,
@@ -155,11 +159,18 @@ describe('OAuthService', () => {
155159
156160 await service . initialize ( ) ;
157161
158- expect ( service . getAvailableProviders ( ) ) . toContain ( 'test' ) ;
162+ expect (
163+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
164+ ) . toContain ( 'test' ) ;
159165 expect ( mockCreateProvider ) . toHaveBeenCalledWith ( {
166+ id : 'test' ,
167+ name : 'test' ,
160168 clientId : 'test-client-id' ,
161169 clientSecret : 'test-client-secret' ,
170+ authorizationUrl : '' ,
171+ tokenUrl : '' ,
162172 redirectUrl : 'http://localhost:3000/callback' ,
173+ scope : [ ] ,
163174 } ) ;
164175 } ) ;
165176
@@ -181,12 +192,14 @@ describe('OAuthService', () => {
181192 expect ( consoleSpy ) . toHaveBeenCalledWith (
182193 expect . stringContaining ( 'Failed to load OAuth provider test' ) ,
183194 ) ;
184- expect ( service . getAvailableProviders ( ) ) . toEqual ( [ ] ) ;
195+ expect (
196+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
197+ ) . toEqual ( [ ] ) ;
185198 } ) ;
186199
187200 it ( 'should validate provider ID format' , async ( ) => {
188201 jest . spyOn ( configService , 'get' ) . mockImplementation ( ( key : string ) => {
189- if ( key === 'oauth.enabledProviders ' ) return [ 'invalid-provider!' ] ;
202+ if ( key === 'OAUTH_ENABLED_PROVIDERS ' ) return 'invalid-provider!' ;
190203 return undefined ;
191204 } ) ;
192205
@@ -196,7 +209,9 @@ describe('OAuthService', () => {
196209 expect ( consoleSpy ) . toHaveBeenCalledWith (
197210 expect . stringContaining ( 'Invalid provider ID: invalid-provider!' ) ,
198211 ) ;
199- expect ( service . getAvailableProviders ( ) ) . toEqual ( [ ] ) ;
212+ expect (
213+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
214+ ) . toEqual ( [ ] ) ;
200215 } ) ;
201216 } ) ;
202217
@@ -207,6 +222,8 @@ describe('OAuthService', () => {
207222
208223 const mockCreateProvider = jest . fn ( ) . mockReturnValue (
209224 new MockOAuthProvider ( {
225+ id : 'test' ,
226+ name : 'Test Provider' ,
210227 clientId : 'test-client-id' ,
211228 clientSecret : 'test-client-secret' ,
212229 redirectUrl : 'http://localhost:3000/callback' ,
@@ -227,15 +244,14 @@ describe('OAuthService', () => {
227244 await service . initialize ( ) ;
228245 } ) ;
229246
230- it ( 'should return provider when it exists' , ( ) => {
231- const provider = service . getProvider ( 'test' ) ;
247+ it ( 'should return provider when it exists' , async ( ) => {
248+ const provider = await service . getProvider ( 'test' ) ;
232249 expect ( provider ) . toBeInstanceOf ( MockOAuthProvider ) ;
233250 } ) ;
234251
235- it ( 'should throw error when provider does not exist' , ( ) => {
236- expect ( ( ) => service . getProvider ( 'nonexistent' ) ) . toThrow (
237- 'OAuth provider not found: nonexistent' ,
238- ) ;
252+ it ( 'should return undefined when provider does not exist' , async ( ) => {
253+ const provider = await service . getProvider ( 'nonexistent' ) ;
254+ expect ( provider ) . toBeUndefined ( ) ;
239255 } ) ;
240256 } ) ;
241257
@@ -246,6 +262,8 @@ describe('OAuthService', () => {
246262
247263 const mockCreateProvider = jest . fn ( ) . mockReturnValue (
248264 new MockOAuthProvider ( {
265+ id : 'test' ,
266+ name : 'Test Provider' ,
249267 clientId : 'test-client-id' ,
250268 clientSecret : 'test-client-secret' ,
251269 redirectUrl : 'http://localhost:3000/callback' ,
@@ -266,17 +284,17 @@ describe('OAuthService', () => {
266284 await service . initialize ( ) ;
267285 } ) ;
268286
269- it ( 'should generate authorization URL' , ( ) => {
270- const url = service . generateAuthorizationUrl ( 'test' , 'state123' ) ;
287+ it ( 'should generate authorization URL' , async ( ) => {
288+ const url = await service . generateAuthorizationUrl ( 'test' , 'state123' ) ;
271289 expect ( url ) . toContain ( 'https://test.com/oauth/authorize' ) ;
272290 expect ( url ) . toContain ( 'client_id=test-client-id' ) ;
273291 expect ( url ) . toContain ( 'state=state123' ) ;
274292 } ) ;
275293
276- it ( 'should throw error for nonexistent provider' , ( ) => {
277- expect ( ( ) => service . generateAuthorizationUrl ( 'nonexistent' ) ) . toThrow (
278- 'OAuth provider not found: nonexistent',
279- ) ;
294+ it ( 'should throw error for nonexistent provider' , async ( ) => {
295+ await expect (
296+ service . generateAuthorizationUrl ( ' nonexistent') ,
297+ ) . rejects . toThrow ( "OAuth provider 'nonexistent' not found" ) ;
280298 } ) ;
281299 } ) ;
282300
@@ -287,6 +305,8 @@ describe('OAuthService', () => {
287305
288306 const mockCreateProvider = jest . fn ( ) . mockReturnValue (
289307 new MockOAuthProvider ( {
308+ id : 'test' ,
309+ name : 'Test Provider' ,
290310 clientId : 'test-client-id' ,
291311 clientSecret : 'test-client-secret' ,
292312 redirectUrl : 'http://localhost:3000/callback' ,
@@ -308,14 +328,8 @@ describe('OAuthService', () => {
308328 } ) ;
309329
310330 it ( 'should handle callback successfully' , async ( ) => {
311- const userInfo = await service . handleCallback ( 'test' , 'valid_code' ) ;
312- expect ( userInfo ) . toEqual ( {
313- id : '12345' ,
314- email : 'test@example.com' ,
315- name : 'Test User' ,
316- username : 'testuser' ,
317- preferredUsername : 'testuser' ,
318- } ) ;
331+ const accessToken = await service . handleCallback ( 'test' , 'valid_code' ) ;
332+ expect ( accessToken ) . toBe ( 'mock_access_token' ) ;
319333 } ) ;
320334
321335 it ( 'should throw error for invalid code' , async ( ) => {
@@ -327,19 +341,21 @@ describe('OAuthService', () => {
327341 it ( 'should throw error for nonexistent provider' , async ( ) => {
328342 await expect (
329343 service . handleCallback ( 'nonexistent' , 'code' ) ,
330- ) . rejects . toThrow ( ' OAuth provider not found: nonexistent' ) ;
344+ ) . rejects . toThrow ( " OAuth provider 'nonexistent' not found" ) ;
331345 } ) ;
332346 } ) ;
333347
334- describe ( 'getAvailableProviders ' , ( ) => {
348+ describe ( 'getAllProviders ' , ( ) => {
335349 it ( 'should return empty array when no providers are loaded' , async ( ) => {
336350 jest . spyOn ( configService , 'get' ) . mockImplementation ( ( key : string ) => {
337- if ( key === 'oauth.enabledProviders ' ) return [ ] ;
351+ if ( key === 'OAUTH_ENABLED_PROVIDERS ' ) return '' ;
338352 return undefined ;
339353 } ) ;
340354
341355 await service . initialize ( ) ;
342- expect ( service . getAvailableProviders ( ) ) . toEqual ( [ ] ) ;
356+ expect (
357+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
358+ ) . toEqual ( [ ] ) ;
343359 } ) ;
344360
345361 it ( 'should return provider IDs when providers are loaded' , async ( ) => {
@@ -348,6 +364,8 @@ describe('OAuthService', () => {
348364
349365 const mockCreateProvider = jest . fn ( ) . mockReturnValue (
350366 new MockOAuthProvider ( {
367+ id : 'test' ,
368+ name : 'Test Provider' ,
351369 clientId : 'test-client-id' ,
352370 clientSecret : 'test-client-secret' ,
353371 redirectUrl : 'http://localhost:3000/callback' ,
@@ -366,15 +384,17 @@ describe('OAuthService', () => {
366384 ) ;
367385
368386 await service . initialize ( ) ;
369- expect ( service . getAvailableProviders ( ) ) . toEqual ( [ 'test' ] ) ;
387+ expect (
388+ ( await service . getAllProviders ( ) ) . map ( ( p ) => p . getConfig ( ) . id ) ,
389+ ) . toEqual ( [ 'test' ] ) ;
370390 } ) ;
371391 } ) ;
372392
373393 describe ( 'security validations' , ( ) => {
374394 it ( 'should prevent path traversal in plugin loading' , async ( ) => {
375395 jest . spyOn ( configService , 'get' ) . mockImplementation ( ( key : string ) => {
376- if ( key === 'oauth.enabledProviders ' ) return [ '../../../malicious' ] ;
377- if ( key === 'oauth.pluginPaths ' ) return [ './plugins' ] ;
396+ if ( key === 'OAUTH_ENABLED_PROVIDERS ' ) return '../../../malicious' ;
397+ if ( key === 'OAUTH_PLUGIN_PATHS ' ) return './plugins' ;
378398 return undefined ;
379399 } ) ;
380400
@@ -388,7 +408,7 @@ describe('OAuthService', () => {
388408
389409 it ( 'should validate provider ID contains only safe characters' , async ( ) => {
390410 jest . spyOn ( configService , 'get' ) . mockImplementation ( ( key : string ) => {
391- if ( key === 'oauth.enabledProviders ' ) return [ 'test<script>' ] ;
411+ if ( key === 'OAUTH_ENABLED_PROVIDERS ' ) return 'test<script>' ;
392412 return undefined ;
393413 } ) ;
394414
0 commit comments