@@ -6,6 +6,8 @@ import * as args from "./args";
66import * as backend from "./backend" ;
77import * as gcf from "../../gcp/cloudfunctions" ;
88import * as gcfV2 from "../../gcp/cloudfunctionsv2" ;
9+ import * as run from "../../gcp/runv2" ;
10+ import * as experiments from "../../experiments" ;
911import * as utils from "../../utils" ;
1012import * as projectConfig from "../../functions/projectConfig" ;
1113
@@ -67,6 +69,41 @@ describe("Backend", () => {
6769 updateTime : new Date ( ) ,
6870 } ;
6971
72+ const RUN_SERVICE : run . Service = {
73+ name : "projects/project/locations/region/services/id" ,
74+ labels : {
75+ "goog-managed-by" : "cloud-functions" ,
76+ "goog-cloudfunctions-runtime" : "nodejs16" ,
77+ "firebase-functions-codebase" : "default" ,
78+ } ,
79+ annotations : {
80+ "cloudfunctions.googleapis.com/function-id" : "id" ,
81+ "cloudfunctions.googleapis.com/trigger-type" : "HTTP_TRIGGER" ,
82+ } ,
83+ template : {
84+ containers : [
85+ {
86+ name : "worker" ,
87+ image : "image" ,
88+ env : [ { name : "FUNCTION_TARGET" , value : "function" } ] ,
89+ resources : {
90+ limits : {
91+ cpu : "1" ,
92+ memory : "256Mi" ,
93+ } ,
94+ } ,
95+ } ,
96+ ] ,
97+ containerConcurrency : 80 ,
98+ } ,
99+ generation : 1 ,
100+ createTime : "2023-01-01T00:00:00Z" ,
101+ updateTime : "2023-01-01T00:00:00Z" ,
102+ creator : "user" ,
103+ lastModifier : "user" ,
104+ etag : "etag" ,
105+ } ;
106+
70107 const HAVE_CLOUD_FUNCTION : gcf . CloudFunction = {
71108 ...CLOUD_FUNCTION ,
72109 buildId : "buildId" ,
@@ -126,18 +163,24 @@ describe("Backend", () => {
126163 describe ( "existing backend" , ( ) => {
127164 let listAllFunctions : sinon . SinonStub ;
128165 let listAllFunctionsV2 : sinon . SinonStub ;
166+ let listServices : sinon . SinonStub ;
129167 let logLabeledWarning : sinon . SinonSpy ;
168+ let isEnabled : sinon . SinonStub ;
130169
131170 beforeEach ( ( ) => {
132171 listAllFunctions = sinon . stub ( gcf , "listAllFunctions" ) . rejects ( "Unexpected call" ) ;
133172 listAllFunctionsV2 = sinon . stub ( gcfV2 , "listAllFunctions" ) . rejects ( "Unexpected v2 call" ) ;
173+ listServices = sinon . stub ( run , "listServices" ) . rejects ( "Unexpected run call" ) ;
134174 logLabeledWarning = sinon . spy ( utils , "logLabeledWarning" ) ;
175+ isEnabled = sinon . stub ( experiments , "isEnabled" ) . returns ( false ) ;
135176 } ) ;
136177
137178 afterEach ( ( ) => {
138179 listAllFunctions . restore ( ) ;
139180 listAllFunctionsV2 . restore ( ) ;
181+ listServices . restore ( ) ;
140182 logLabeledWarning . restore ( ) ;
183+ isEnabled . restore ( ) ;
141184 } ) ;
142185
143186 function newContext ( ) : args . Context {
@@ -210,58 +253,6 @@ describe("Backend", () => {
210253 ) ;
211254 } ) ;
212255
213- it ( "should read v1 functions only when user is not allowlisted for v2" , async ( ) => {
214- listAllFunctions . onFirstCall ( ) . resolves ( {
215- functions : [
216- {
217- ...HAVE_CLOUD_FUNCTION ,
218- httpsTrigger : { } ,
219- } ,
220- ] ,
221- unreachable : [ ] ,
222- } ) ;
223- listAllFunctionsV2 . throws (
224- new FirebaseError ( "HTTP Error: 404, Method not found" , { status : 404 } ) ,
225- ) ;
226-
227- const have = await backend . existingBackend ( newContext ( ) ) ;
228-
229- expect ( have ) . to . deep . equal ( backend . of ( { ...ENDPOINT , httpsTrigger : { } } ) ) ;
230- } ) ;
231-
232- it ( "should throw an error if v2 list api throws an error" , async ( ) => {
233- listAllFunctions . onFirstCall ( ) . resolves ( {
234- functions : [ ] ,
235- unreachable : [ ] ,
236- } ) ;
237- listAllFunctionsV2 . throws (
238- new FirebaseError ( "HTTP Error: 500, Internal Error" , { status : 500 } ) ,
239- ) ;
240-
241- await expect ( backend . existingBackend ( newContext ( ) ) ) . to . be . rejectedWith (
242- "HTTP Error: 500, Internal Error" ,
243- ) ;
244- } ) ;
245-
246- it ( "should read v1 functions only when user is not allowlisted for v2" , async ( ) => {
247- listAllFunctions . onFirstCall ( ) . resolves ( {
248- functions : [
249- {
250- ...HAVE_CLOUD_FUNCTION ,
251- httpsTrigger : { } ,
252- } ,
253- ] ,
254- unreachable : [ ] ,
255- } ) ;
256- listAllFunctionsV2 . throws (
257- new FirebaseError ( "HTTP Error: 404, Method not found" , { status : 404 } ) ,
258- ) ;
259-
260- const have = await backend . existingBackend ( newContext ( ) ) ;
261-
262- expect ( have ) . to . deep . equal ( backend . of ( { ...ENDPOINT , httpsTrigger : { } } ) ) ;
263- } ) ;
264-
265256 it ( "should read v2 functions when enabled" , async ( ) => {
266257 listAllFunctions . onFirstCall ( ) . resolves ( {
267258 functions : [ ] ,
@@ -318,6 +309,53 @@ describe("Backend", () => {
318309
319310 expect ( have ) . to . deep . equal ( want ) ;
320311 } ) ;
312+
313+ it ( "should read v2 functions from Cloud Run when experiment is enabled" , async ( ) => {
314+ isEnabled . withArgs ( "functionsrunapionly" ) . returns ( true ) ;
315+ listAllFunctions . onFirstCall ( ) . resolves ( {
316+ functions : [ ] ,
317+ unreachable : [ ] ,
318+ } ) ;
319+ listServices . onFirstCall ( ) . resolves ( [ RUN_SERVICE ] ) ;
320+
321+ const have = await backend . existingBackend ( newContext ( ) ) ;
322+
323+ const wantEndpoint = {
324+ ...ENDPOINT ,
325+ platform : "gcfv2" as const ,
326+ concurrency : 80 ,
327+ cpu : 1 ,
328+ httpsTrigger : { } ,
329+ availableMemoryMb : 256 as const ,
330+ environmentVariables : {
331+ FUNCTION_TARGET : "function" ,
332+ } ,
333+ labels : {
334+ "goog-managed-by" : "cloud-functions" ,
335+ "goog-cloudfunctions-runtime" : "nodejs16" ,
336+ "firebase-functions-codebase" : "default" ,
337+ } ,
338+ secretEnvironmentVariables : [ ] ,
339+ } ;
340+ delete wantEndpoint . state ;
341+
342+ expect ( have ) . to . deep . equal ( backend . of ( wantEndpoint ) ) ;
343+ expect ( listAllFunctionsV2 ) . to . not . have . been . called ;
344+ } ) ;
345+
346+ it ( "should handle Cloud Run list errors gracefully when experiment is enabled" , async ( ) => {
347+ isEnabled . withArgs ( "functionsrunapionly" ) . returns ( true ) ;
348+ listAllFunctions . onFirstCall ( ) . resolves ( {
349+ functions : [ ] ,
350+ unreachable : [ ] ,
351+ } ) ;
352+ listServices . rejects ( new Error ( "Random error" ) ) ;
353+
354+ const context = newContext ( ) ;
355+ await backend . existingBackend ( context ) ;
356+
357+ expect ( context . unreachableRegions ?. run ) . to . deep . equal ( [ "unknown" ] ) ;
358+ } ) ;
321359 } ) ;
322360
323361 describe ( "checkAvailability" , ( ) => {
0 commit comments