@@ -13,6 +13,7 @@ import {
1313 isGenerateImageAssetEnabled ,
1414 parseImageGenerationUpdate ,
1515 resolveImageGenerationConfig ,
16+ resolveImageGenerationTestCredentials ,
1617 updateImageGenerationSettings ,
1718} from './image-generation-settings' ;
1819
@@ -406,3 +407,89 @@ describe('image generation enablement', () => {
406407 } ) ;
407408 } ) ;
408409} ) ;
410+
411+ describe ( 'resolveImageGenerationTestCredentials' , ( ) => {
412+ afterEach ( ( ) => {
413+ mocks . cachedConfig = null ;
414+ getApiKeyForProviderMock . mockReset ( ) ;
415+ mocks . codexGetValidAccessToken . mockReset ( ) ;
416+ } ) ;
417+
418+ it ( 'returns IMAGE_GEN_DISABLED when no imageGeneration config exists' , async ( ) => {
419+ const result = await resolveImageGenerationTestCredentials ( null ) ;
420+ expect ( result ) . toMatchObject ( { ok : false , code : 'IMAGE_GEN_DISABLED' } ) ;
421+ } ) ;
422+
423+ it ( 'resolves inherited credentials even when image generation is disabled' , async ( ) => {
424+ getApiKeyForProviderMock . mockReturnValue ( 'sk-openai' ) ;
425+ const cfg = makeConfig ( false ) ;
426+ const result = await resolveImageGenerationTestCredentials ( cfg ) ;
427+ expect ( result ) . toMatchObject ( {
428+ ok : true ,
429+ provider : 'openai' ,
430+ apiKey : 'sk-openai' ,
431+ baseUrl : 'https://api.openai.com/v1' ,
432+ } ) ;
433+ } ) ;
434+
435+ it ( 'returns PROVIDER_KEY_MISSING when inherited credential cannot be read' , async ( ) => {
436+ getApiKeyForProviderMock . mockImplementation ( ( ) => {
437+ throw new CodesignError ( 'missing key' , ERROR_CODES . PROVIDER_KEY_MISSING ) ;
438+ } ) ;
439+ const cfg = makeConfig ( true ) ;
440+ const result = await resolveImageGenerationTestCredentials ( cfg ) ;
441+ expect ( result ) . toMatchObject ( { ok : false , code : 'PROVIDER_KEY_MISSING' } ) ;
442+ } ) ;
443+
444+ it ( 'decrypts custom-mode key when one is stored' , async ( ) => {
445+ const baseCfg = makeConfig ( true ) ;
446+ const cfg = hydrateConfig ( {
447+ version : 3 ,
448+ activeProvider : baseCfg . activeProvider ,
449+ activeModel : baseCfg . activeModel ,
450+ providers : baseCfg . providers ,
451+ secrets : baseCfg . secrets ,
452+ imageGeneration : {
453+ schemaVersion : IMAGE_GENERATION_SCHEMA_VERSION ,
454+ enabled : true ,
455+ provider : 'openai' ,
456+ credentialMode : 'custom' ,
457+ model : 'gpt-image-2' ,
458+ quality : 'high' ,
459+ size : '1536x1024' ,
460+ outputFormat : 'png' ,
461+ apiKey : { ciphertext : 'sk-custom-image' , mask : 'sk-…age' } ,
462+ } ,
463+ } ) ;
464+ const result = await resolveImageGenerationTestCredentials ( cfg ) ;
465+ expect ( result ) . toMatchObject ( { ok : true , provider : 'openai' , apiKey : 'sk-custom-image' } ) ;
466+ } ) ;
467+
468+ it ( 'uses ChatGPT OAuth access token for chatgpt-codex provider' , async ( ) => {
469+ mocks . codexGetValidAccessToken . mockResolvedValue ( 'codex-token-abc' ) ;
470+ const baseCfg = makeConfig ( true ) ;
471+ const cfg = hydrateConfig ( {
472+ version : 3 ,
473+ activeProvider : baseCfg . activeProvider ,
474+ activeModel : baseCfg . activeModel ,
475+ providers : baseCfg . providers ,
476+ secrets : baseCfg . secrets ,
477+ imageGeneration : {
478+ schemaVersion : IMAGE_GENERATION_SCHEMA_VERSION ,
479+ enabled : true ,
480+ provider : 'chatgpt-codex' ,
481+ credentialMode : 'inherit' ,
482+ model : 'gpt-5.5' ,
483+ quality : 'high' ,
484+ size : '1536x1024' ,
485+ outputFormat : 'png' ,
486+ } ,
487+ } ) ;
488+ const result = await resolveImageGenerationTestCredentials ( cfg ) ;
489+ expect ( result ) . toMatchObject ( {
490+ ok : true ,
491+ provider : 'chatgpt-codex' ,
492+ apiKey : 'codex-token-abc' ,
493+ } ) ;
494+ } ) ;
495+ } ) ;
0 commit comments