@@ -5,6 +5,7 @@ import { withMockApi } from '../../utils/mock-api.js'
55import { withSiteBuilder } from '../../utils/site-builder.js'
66import {
77 assertAIGatewayValue ,
8+ createAccountScopedAIGatewayTestData ,
89 createAIGatewayCheckFunction ,
910 createAIGatewayDisabledTestData ,
1011 createAIGatewayTestData ,
@@ -263,6 +264,92 @@ describe.concurrent('AI Gateway Integration', () => {
263264 } )
264265 } )
265266
267+ test ( 'should fall back to account-scoped token when site has no published deploy' , async ( t ) => {
268+ await withSiteBuilder ( t , async ( builder ) => {
269+ const { siteInfo, accountAIGatewayToken, routes } = createAccountScopedAIGatewayTestData ( )
270+ const checkFunction = createAIGatewayCheckFunction ( )
271+
272+ await builder
273+ . withContentFile ( {
274+ path : checkFunction . path ,
275+ content : checkFunction . content ,
276+ } )
277+ . build ( )
278+
279+ await withMockApi ( routes , async ( { apiUrl } ) => {
280+ await withDevServer (
281+ {
282+ cwd : builder . directory ,
283+ offline : false ,
284+ env : {
285+ NETLIFY_API_URL : apiUrl ,
286+ NETLIFY_SITE_ID : siteInfo . id ,
287+ NETLIFY_AUTH_TOKEN : 'fake-token' ,
288+ } ,
289+ } ,
290+ async ( server ) => {
291+ const response = await fetch ( `${ server . url } ${ checkFunction . urlPath } ` )
292+ const result = ( await response . json ( ) ) as { hasAIGateway : boolean ; aiGatewayValue : string | null }
293+
294+ t . expect ( response . status ) . toBe ( 200 )
295+ assertAIGatewayValue ( t , result , accountAIGatewayToken . token , accountAIGatewayToken . url )
296+ } ,
297+ )
298+ } )
299+ } )
300+ } )
301+
302+ test ( 'should fall back to account-scoped token when site-scoped token fails' , async ( t ) => {
303+ await withSiteBuilder ( t , async ( builder ) => {
304+ const { siteInfo : baseSiteInfo } = createAIGatewayTestData ( )
305+ const siteInfo = { ...baseSiteInfo }
306+ const checkFunction = createAIGatewayCheckFunction ( )
307+
308+ const accountAIGatewayToken = {
309+ token : 'account-fallback-token-789' ,
310+ url : 'https://ai.netlify.com/.netlify/ai/' ,
311+ }
312+
313+ const routes = [
314+ { path : 'sites/test-site-id' , response : siteInfo } ,
315+ { path : 'sites/test-site-id/service-instances' , response : [ ] } ,
316+ { path : 'accounts' , response : [ { id : 'account-id-123' , slug : siteInfo . account_slug } ] } ,
317+ { path : 'accounts/test-account/env' , response : [ ] } ,
318+ { path : 'sites/test-site-id/ai-gateway/token' , status : 404 , response : { message : 'Not Found' } } ,
319+ { path : 'accounts/account-id-123/ai-gateway/token' , response : accountAIGatewayToken } ,
320+ { path : 'ai-gateway/providers' , response : { providers : { } } } ,
321+ ]
322+
323+ await builder
324+ . withContentFile ( {
325+ path : checkFunction . path ,
326+ content : checkFunction . content ,
327+ } )
328+ . build ( )
329+
330+ await withMockApi ( routes , async ( { apiUrl } ) => {
331+ await withDevServer (
332+ {
333+ cwd : builder . directory ,
334+ offline : false ,
335+ env : {
336+ NETLIFY_API_URL : apiUrl ,
337+ NETLIFY_SITE_ID : siteInfo . id ,
338+ NETLIFY_AUTH_TOKEN : 'fake-token' ,
339+ } ,
340+ } ,
341+ async ( server ) => {
342+ const response = await fetch ( `${ server . url } ${ checkFunction . urlPath } ` )
343+ const result = ( await response . json ( ) ) as { hasAIGateway : boolean ; aiGatewayValue : string | null }
344+
345+ t . expect ( response . status ) . toBe ( 200 )
346+ assertAIGatewayValue ( t , result , accountAIGatewayToken . token , accountAIGatewayToken . url )
347+ } ,
348+ )
349+ } )
350+ } )
351+ } )
352+
266353 test ( 'should work with staging environment URLs' , async ( t ) => {
267354 await withSiteBuilder ( t , async ( builder ) => {
268355 const { siteInfo : baseSiteInfo , aiGatewayToken : baseToken } = createAIGatewayTestData ( )
0 commit comments