From c2ca40867ba82317e19e5a258a831dc960e609cd Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 20 Oct 2022 14:39:27 -0400 Subject: [PATCH 1/6] adding a promise queue to handle parallel requests --- src/emulator/functionsEmulator.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 1896ef14bdf..613a126cfc3 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -183,6 +183,9 @@ export class FunctionsEmulator implements EmulatorInstance { private blockingFunctionsConfig: BlockingFunctionsConfig = {}; + // A queue to handle https requests that are parallel + private httpsTriggerQueue: Promise; + constructor(private args: FunctionsEmulatorArgs) { // TODO: Would prefer not to have static state but here we are! EmulatorLogger.verbosity = this.args.quiet ? Verbosity.QUIET : Verbosity.DEBUG; @@ -204,6 +207,7 @@ export class FunctionsEmulator implements EmulatorInstance { this.workerPools[backend.codebase] = pool; } this.workQueue = new WorkQueue(mode); + this.httpsTriggerQueue = Promise.resolve(); } private async getCredentialsEnvironment(): Promise> { @@ -1378,7 +1382,14 @@ export class FunctionsEmulator implements EmulatorInstance { } } - private async handleHttpsTrigger(req: express.Request, res: express.Response) { + private handleHttpsTrigger(req: express.Request, res: express.Response) { + this.httpsTriggerQueue = this.httpsTriggerQueue.then(() => + this.handleHttpsTriggerLocked(req, res) + ); + return this.httpsTriggerQueue; + } + + private async handleHttpsTriggerLocked(req: express.Request, res: express.Response) { const method = req.method; let triggerId: string = req.params.trigger_name; if (req.params.region) { From f6a59e71c0255553d9bbc5e5459233aa220aab8f Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 27 Oct 2022 14:43:49 -0400 Subject: [PATCH 2/6] adding create state to workers for concurrent requests --- src/emulator/functionsEmulator.ts | 13 +------- src/emulator/functionsRuntimeWorker.ts | 20 ++++++++++-- .../emulators/functionsRuntimeWorker.spec.ts | 31 ++++++++++++++++--- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 613a126cfc3..1896ef14bdf 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -183,9 +183,6 @@ export class FunctionsEmulator implements EmulatorInstance { private blockingFunctionsConfig: BlockingFunctionsConfig = {}; - // A queue to handle https requests that are parallel - private httpsTriggerQueue: Promise; - constructor(private args: FunctionsEmulatorArgs) { // TODO: Would prefer not to have static state but here we are! EmulatorLogger.verbosity = this.args.quiet ? Verbosity.QUIET : Verbosity.DEBUG; @@ -207,7 +204,6 @@ export class FunctionsEmulator implements EmulatorInstance { this.workerPools[backend.codebase] = pool; } this.workQueue = new WorkQueue(mode); - this.httpsTriggerQueue = Promise.resolve(); } private async getCredentialsEnvironment(): Promise> { @@ -1382,14 +1378,7 @@ export class FunctionsEmulator implements EmulatorInstance { } } - private handleHttpsTrigger(req: express.Request, res: express.Response) { - this.httpsTriggerQueue = this.httpsTriggerQueue.then(() => - this.handleHttpsTriggerLocked(req, res) - ); - return this.httpsTriggerQueue; - } - - private async handleHttpsTriggerLocked(req: express.Request, res: express.Response) { + private async handleHttpsTrigger(req: express.Request, res: express.Response) { const method = req.method; let triggerId: string = req.params.trigger_name; if (req.params.region) { diff --git a/src/emulator/functionsRuntimeWorker.ts b/src/emulator/functionsRuntimeWorker.ts index e40b51c98eb..654ebf1e3ea 100644 --- a/src/emulator/functionsRuntimeWorker.ts +++ b/src/emulator/functionsRuntimeWorker.ts @@ -12,6 +12,9 @@ import { Serializable } from "child_process"; type LogListener = (el: EmulatorLog) => any; export enum RuntimeWorkerState { + // Worker has been created but is not ready to accept work + CREATED = "CREATED", + // Worker is ready to accept new work IDLE = "IDLE", @@ -34,7 +37,7 @@ export class RuntimeWorker { stateEvents: EventEmitter = new EventEmitter(); private logListeners: Array = []; - private _state: RuntimeWorkerState = RuntimeWorkerState.IDLE; + private _state: RuntimeWorkerState = RuntimeWorkerState.CREATED; constructor(key: string, runtime: FunctionsRuntimeInstance) { this.id = uuid.v4(); @@ -86,6 +89,10 @@ export class RuntimeWorker { return lines[lines.length - 1]; } + readyForWork(): void { + this.state = RuntimeWorkerState.IDLE; + } + sendDebugMsg(debug: FunctionsRuntimeBundle["debug"]): Promise { return new Promise((resolve, reject) => { this.runtime.process.send(JSON.stringify(debug), (err) => { @@ -178,7 +185,11 @@ export class RuntimeWorker { path: "/__/health", socketPath: this.runtime.socketPath, }, - () => resolve() + () => { + // Set the worker state to IDLE for new work + this.readyForWork(); + resolve(); + } ) .end(); req.on("error", (error) => { @@ -323,6 +334,11 @@ export class RuntimeWorkerPool { return; } + /** + * Adds a worker to the pool + * + * Must call worker.readyForWork() or worker.waitForSocketReady() to set the worker to ready status + */ addWorker( triggerId: string | undefined, runtime: FunctionsRuntimeInstance, diff --git a/src/test/emulators/functionsRuntimeWorker.spec.ts b/src/test/emulators/functionsRuntimeWorker.spec.ts index cbb0015f7fe..ffd84c9d4c2 100644 --- a/src/test/emulators/functionsRuntimeWorker.spec.ts +++ b/src/test/emulators/functionsRuntimeWorker.spec.ts @@ -41,6 +41,7 @@ class MockRuntimeInstance implements FunctionsRuntimeInstance { */ class WorkerStateCounter { counts: { [state in RuntimeWorkerState]: number } = { + CREATED: 0, IDLE: 0, BUSY: 0, FINISHING: 0, @@ -49,6 +50,9 @@ class WorkerStateCounter { constructor(worker: RuntimeWorker) { this.increment(worker.state); + worker.stateEvents.on(RuntimeWorkerState.CREATED, () => { + this.increment(RuntimeWorkerState.CREATED); + }); worker.stateEvents.on(RuntimeWorkerState.IDLE, () => { this.increment(RuntimeWorkerState.IDLE); }); @@ -68,7 +72,13 @@ class WorkerStateCounter { } get total() { - return this.counts.IDLE + this.counts.BUSY + this.counts.FINISHING + this.counts.FINISHED; + return ( + this.counts.CREATED + + this.counts.IDLE + + this.counts.BUSY + + this.counts.FINISHING + + this.counts.FINISHED + ); } } @@ -82,15 +92,17 @@ describe("FunctionsRuntimeWorker", () => { const worker = new RuntimeWorker(workerPool.getKey("trigger"), new MockRuntimeInstance()); const counter = new WorkerStateCounter(worker); + worker.readyForWork(); await worker.request( { method: "GET", path: "/" }, httpMocks.createResponse({ eventEmitter: EventEmitter }) ); scope.done(); + expect(counter.counts.CREATED).to.eql(1); expect(counter.counts.BUSY).to.eql(1); expect(counter.counts.IDLE).to.eql(2); - expect(counter.total).to.eql(3); + expect(counter.total).to.eql(4); }); it("goes from idle --> busy --> finished when there's an error", async () => { @@ -99,16 +111,18 @@ describe("FunctionsRuntimeWorker", () => { const worker = new RuntimeWorker(workerPool.getKey("trigger"), new MockRuntimeInstance()); const counter = new WorkerStateCounter(worker); + worker.readyForWork(); await worker.request( { method: "GET", path: "/" }, httpMocks.createResponse({ eventEmitter: EventEmitter }) ); scope.done(); + expect(counter.counts.CREATED).to.eql(1); expect(counter.counts.IDLE).to.eql(1); expect(counter.counts.BUSY).to.eql(1); expect(counter.counts.FINISHED).to.eql(1); - expect(counter.total).to.eql(3); + expect(counter.total).to.eql(4); }); it("goes from busy --> finishing --> finished when marked", async () => { @@ -117,6 +131,7 @@ describe("FunctionsRuntimeWorker", () => { const worker = new RuntimeWorker(workerPool.getKey("trigger"), new MockRuntimeInstance()); const counter = new WorkerStateCounter(worker); + worker.readyForWork(); const resp = httpMocks.createResponse({ eventEmitter: EventEmitter }); resp.on("end", () => { worker.state = RuntimeWorkerState.FINISHING; @@ -124,11 +139,12 @@ describe("FunctionsRuntimeWorker", () => { await worker.request({ method: "GET", path: "/" }, resp); scope.done(); + expect(counter.counts.CREATED).to.eql(1); expect(counter.counts.IDLE).to.eql(1); expect(counter.counts.BUSY).to.eql(1); expect(counter.counts.FINISHING).to.eql(1); expect(counter.counts.FINISHED).to.eql(1); - expect(counter.total).to.eql(4); + expect(counter.total).to.eql(5); }); }); @@ -144,6 +160,7 @@ describe("FunctionsRuntimeWorker", () => { // Add a worker and make sure it's there const worker = pool.addWorker(trigger, new MockRuntimeInstance()); + worker.readyForWork(); const triggerWorkers = pool.getTriggerWorkers(trigger); expect(triggerWorkers.length).length.to.eq(1); expect(pool.getIdleWorker(trigger)).to.eql(worker); @@ -170,6 +187,7 @@ describe("FunctionsRuntimeWorker", () => { // Add a worker to the pool that's destined to fail. const scope = nock("http://localhost").get("/").replyWithError("boom"); const worker = pool.addWorker(trigger, new MockRuntimeInstance()); + worker.readyForWork(); expect(pool.getIdleWorker(trigger)).to.eql(worker); // Send request to the worker. Request should fail, killing the worker. @@ -188,9 +206,11 @@ describe("FunctionsRuntimeWorker", () => { const trigger = "trigger1"; const busyWorker = pool.addWorker(trigger, new MockRuntimeInstance()); + busyWorker.readyForWork(); const busyWorkerCounter = new WorkerStateCounter(busyWorker); const idleWorker = pool.addWorker(trigger, new MockRuntimeInstance()); + idleWorker.readyForWork(); const idleWorkerCounter = new WorkerStateCounter(idleWorker); // Add a worker to the pool that's destined to fail. @@ -217,9 +237,11 @@ describe("FunctionsRuntimeWorker", () => { const trigger = "trigger1"; const busyWorker = pool.addWorker(trigger, new MockRuntimeInstance()); + busyWorker.readyForWork(); const busyWorkerCounter = new WorkerStateCounter(busyWorker); const idleWorker = pool.addWorker(trigger, new MockRuntimeInstance()); + idleWorker.readyForWork(); const idleWorkerCounter = new WorkerStateCounter(idleWorker); // Add a worker to the pool that's destined to fail. @@ -248,6 +270,7 @@ describe("FunctionsRuntimeWorker", () => { const pool = new RuntimeWorkerPool(FunctionsExecutionMode.SEQUENTIAL); const worker = pool.addWorker(trigger1, new MockRuntimeInstance()); + worker.readyForWork(); const resp = httpMocks.createResponse({ eventEmitter: EventEmitter }); resp.on("end", () => { From b62c45dfbfd389c68a929d7de774950ee5fe5c0b Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 28 Oct 2022 11:20:29 -0400 Subject: [PATCH 3/6] update doc string --- src/emulator/functionsRuntimeWorker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emulator/functionsRuntimeWorker.ts b/src/emulator/functionsRuntimeWorker.ts index 654ebf1e3ea..3358ac68311 100644 --- a/src/emulator/functionsRuntimeWorker.ts +++ b/src/emulator/functionsRuntimeWorker.ts @@ -335,9 +335,9 @@ export class RuntimeWorkerPool { } /** - * Adds a worker to the pool - * - * Must call worker.readyForWork() or worker.waitForSocketReady() to set the worker to ready status + * Adds a worker to the pool. + * Caller must set the worker status to ready by calling + * `worker.readyForWork()` or `worker.waitForSocketReady()`. */ addWorker( triggerId: string | undefined, From 4e8336ea6d834607ad43a293037dc97432cb70fb Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 28 Oct 2022 11:22:08 -0400 Subject: [PATCH 4/6] increase timeout for parallel firestore --- scripts/triggers-end-to-end-tests/tests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/triggers-end-to-end-tests/tests.ts b/scripts/triggers-end-to-end-tests/tests.ts index 17a3aadb85c..dc69f0b8ded 100755 --- a/scripts/triggers-end-to-end-tests/tests.ts +++ b/scripts/triggers-end-to-end-tests/tests.ts @@ -131,7 +131,7 @@ describe("function triggers", () => { }); it("should write to the firestore emulator", async function (this) { - this.timeout(EMULATOR_TEST_TIMEOUT); + this.timeout(EMULATOR_TEST_TIMEOUT * 2); const response = await test.writeToFirestore(); expect(response.status).to.equal(200); @@ -143,7 +143,7 @@ describe("function triggers", () => { * fixture state handlers to complete before we check * that state in the next test. */ - await new Promise((resolve) => setTimeout(resolve, EMULATORS_WRITE_DELAY_MS)); + await new Promise((resolve) => setTimeout(resolve, EMULATORS_WRITE_DELAY_MS * 2)); }); it("should have have triggered cloud functions", () => { From 3eefbe925a1aae7bfd973d47beb58d99ebbe5e0e Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Fri, 28 Oct 2022 15:45:44 -0400 Subject: [PATCH 5/6] adding e2e test for parallel requests --- scripts/integration-helpers/framework.ts | 10 ++++++++ scripts/triggers-end-to-end-tests/tests.ts | 19 +++++++++++++++ .../triggers/index.js | 24 +++++++++++++++++++ scripts/triggers-end-to-end-tests/v2/index.js | 6 +++++ 4 files changed, 59 insertions(+) diff --git a/scripts/integration-helpers/framework.ts b/scripts/integration-helpers/framework.ts index a5aeb59df3f..2ad15fead77 100644 --- a/scripts/integration-helpers/framework.ts +++ b/scripts/integration-helpers/framework.ts @@ -45,6 +45,7 @@ const AUTH_BLOCKING_CREATE_V2_LOG = "========== AUTH BLOCKING CREATE V2 FUNCTION METADATA =========="; const AUTH_BLOCKING_SIGN_IN_V2_LOG = "========== AUTH BLOCKING SIGN IN V2 FUNCTION METADATA =========="; +const HTTPS_V2_FUNCTION_LOG = "========== HTTPS V2 FUNCTION =========="; interface ConnectionInfo { host: string; @@ -143,6 +144,7 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { authBlockingCreateV2TriggerCount = 0; authBlockingSignInV2TriggerCount = 0; rtdbV2TriggerCount = 0; + httpsV2TriggerCount = 0; rtdbFromFirestore = false; firestoreFromRtdb = false; @@ -179,6 +181,7 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { this.authBlockingCreateV2TriggerCount = 0; this.authBlockingSignInV2TriggerCount = 0; this.rtdbV2TriggerCount = 0; + this.httpsV2TriggerCount = 0; } /* @@ -274,6 +277,9 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { if (data.includes(RTDB_V2_FUNCTION_LOG)) { this.rtdbV2TriggerCount++; } + if (data.includes(HTTPS_V2_FUNCTION_LOG)) { + this.httpsV2TriggerCount++; + } }); return startEmulators; @@ -336,6 +342,10 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { }); } + triggerHttpsFunction(): Promise { + return this.invokeHttpFunction("triggerHttpsFunction"); + } + createUserFromAuth(): Promise { return this.invokeHttpFunction("createUserFromAuth"); } diff --git a/scripts/triggers-end-to-end-tests/tests.ts b/scripts/triggers-end-to-end-tests/tests.ts index dc69f0b8ded..d8fd0e74f3d 100755 --- a/scripts/triggers-end-to-end-tests/tests.ts +++ b/scripts/triggers-end-to-end-tests/tests.ts @@ -122,6 +122,25 @@ describe("function triggers", () => { await test.stopEmulators(); }); + describe("https triggers", () => { + it("should handle parallel requests", async function (this) { + this.timeout(EMULATOR_TEST_TIMEOUT); + + const [resp1, resp2] = await Promise.all([ + test.triggerHttpsFunction(), + test.triggerHttpsFunction(), + ]); + + expect(resp1.status).to.eq(200); + expect(resp2.status).to.eq(200); + await new Promise((resolve) => setTimeout(resolve, EMULATORS_WRITE_DELAY_MS)); + }); + + it("should have triggered the cloud functions", () => { + expect(test.httpsV2TriggerCount).to.eq(2); + }); + }); + describe("database and firestore emulator triggers", () => { it("should write to the database emulator", async function (this) { this.timeout(EMULATOR_TEST_TIMEOUT); diff --git a/scripts/triggers-end-to-end-tests/triggers/index.js b/scripts/triggers-end-to-end-tests/triggers/index.js index 35ffe4c73df..221622cf8fc 100644 --- a/scripts/triggers-end-to-end-tests/triggers/index.js +++ b/scripts/triggers-end-to-end-tests/triggers/index.js @@ -8,9 +8,21 @@ const { createUserWithEmailAndPassword, signInWithEmailAndPassword, } = require("firebase/auth"); +const fs = require("fs"); +const path = require("path"); +const fetch = require("node-fetch"); + +// load the config from the parent dir +const filename = path.join(__dirname, "/../firebase.json"); +const data = fs.readFileSync(filename, "utf8"); +const config = JSON.parse(data); const FIREBASE_PROJECT = process.env.FBTOOLS_TARGET_PROJECT || ""; +const FUNCTIONS_PORT = config.emulators.functions?.port || "9002"; + +const FUNCTIONS_REGION = "us-central1"; + /* * We install onWrite triggers for START_DOCUMENT_NAME in both the firestore and * database emulators. From each respective onWrite trigger, we write a document @@ -43,6 +55,18 @@ const app = initializeApp( const auth = getAuth(app); connectAuthEmulator(auth, `http://${process.env.FIREBASE_AUTH_EMULATOR_HOST}`); +function invokeHttpsFunction(name) { + const url = `http://localhost:${[FUNCTIONS_PORT, FIREBASE_PROJECT, FUNCTIONS_REGION, name].join( + "/" + )}`; + return fetch(url); +} + +exports.triggerHttpsFunction = functions.https.onRequest(async (req, res) => { + await invokeHttpsFunction("httpsv2reaction"); + res.json({ triggered: true }); +}); + exports.deleteFromFirestore = functions.https.onRequest(async (req, res) => { await admin.firestore().doc(START_DOCUMENT_NAME).delete(); res.json({ deleted: true }); diff --git a/scripts/triggers-end-to-end-tests/v2/index.js b/scripts/triggers-end-to-end-tests/v2/index.js index 7d1aeb89bff..c6a6d887cd3 100644 --- a/scripts/triggers-end-to-end-tests/v2/index.js +++ b/scripts/triggers-end-to-end-tests/v2/index.js @@ -23,12 +23,18 @@ const AUTH_BLOCKING_CREATE_V2_LOG = const AUTH_BLOCKING_SIGN_IN_V2_LOG = "========== AUTH BLOCKING SIGN IN V2 FUNCTION METADATA =========="; const RTDB_LOG = "========== RTDB V2 FUNCTION =========="; +const HTTPS_FUNCTION_LOG = "========== HTTPS V2 FUNCTION =========="; const PUBSUB_TOPIC = "test-topic"; const START_DOCUMENT_NAME = "test/start"; admin.initializeApp(); +exports.httpsv2reaction = functionsV2.https.onRequest((req, res) => { + console.log(HTTPS_FUNCTION_LOG); + res.send(); +}); + exports.pubsubv2reaction = functionsV2.pubsub.onMessagePublished(PUBSUB_TOPIC, (cloudevent) => { console.log(PUBSUB_FUNCTION_LOG); console.log("Message", JSON.stringify(cloudevent.data.message.json)); From 52b5d90f9dbd2d5d0452b62c8961e14c04530a72 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Tue, 1 Nov 2022 10:48:49 -0400 Subject: [PATCH 6/6] address pr comments --- CHANGELOG.md | 1 + scripts/integration-helpers/framework.ts | 10 -------- scripts/triggers-end-to-end-tests/tests.ts | 11 +++------ .../triggers/index.js | 24 ------------------- scripts/triggers-end-to-end-tests/v2/index.js | 4 +--- .../emulators/functionsRuntimeWorker.spec.ts | 6 ++--- 6 files changed, 8 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b0d5784d68..142ca90ffa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,3 +7,4 @@ - Adds `--disable-triggers` flag to RTDB write commands. - Default enables experiment to skip deploying unmodified functions (#5192) - Default enables experiment to allow parameterized functions codebases (#5192) +- Fixes parallel requests in the functions emulator (#5149). diff --git a/scripts/integration-helpers/framework.ts b/scripts/integration-helpers/framework.ts index 2ad15fead77..a5aeb59df3f 100644 --- a/scripts/integration-helpers/framework.ts +++ b/scripts/integration-helpers/framework.ts @@ -45,7 +45,6 @@ const AUTH_BLOCKING_CREATE_V2_LOG = "========== AUTH BLOCKING CREATE V2 FUNCTION METADATA =========="; const AUTH_BLOCKING_SIGN_IN_V2_LOG = "========== AUTH BLOCKING SIGN IN V2 FUNCTION METADATA =========="; -const HTTPS_V2_FUNCTION_LOG = "========== HTTPS V2 FUNCTION =========="; interface ConnectionInfo { host: string; @@ -144,7 +143,6 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { authBlockingCreateV2TriggerCount = 0; authBlockingSignInV2TriggerCount = 0; rtdbV2TriggerCount = 0; - httpsV2TriggerCount = 0; rtdbFromFirestore = false; firestoreFromRtdb = false; @@ -181,7 +179,6 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { this.authBlockingCreateV2TriggerCount = 0; this.authBlockingSignInV2TriggerCount = 0; this.rtdbV2TriggerCount = 0; - this.httpsV2TriggerCount = 0; } /* @@ -277,9 +274,6 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { if (data.includes(RTDB_V2_FUNCTION_LOG)) { this.rtdbV2TriggerCount++; } - if (data.includes(HTTPS_V2_FUNCTION_LOG)) { - this.httpsV2TriggerCount++; - } }); return startEmulators; @@ -342,10 +336,6 @@ export class TriggerEndToEndTest extends EmulatorEndToEndTest { }); } - triggerHttpsFunction(): Promise { - return this.invokeHttpFunction("triggerHttpsFunction"); - } - createUserFromAuth(): Promise { return this.invokeHttpFunction("createUserFromAuth"); } diff --git a/scripts/triggers-end-to-end-tests/tests.ts b/scripts/triggers-end-to-end-tests/tests.ts index d8fd0e74f3d..a0dc6928a12 100755 --- a/scripts/triggers-end-to-end-tests/tests.ts +++ b/scripts/triggers-end-to-end-tests/tests.ts @@ -124,20 +124,15 @@ describe("function triggers", () => { describe("https triggers", () => { it("should handle parallel requests", async function (this) { - this.timeout(EMULATOR_TEST_TIMEOUT); + this.timeout(TEST_SETUP_TIMEOUT); const [resp1, resp2] = await Promise.all([ - test.triggerHttpsFunction(), - test.triggerHttpsFunction(), + test.invokeHttpFunction("httpsv2reaction"), + test.invokeHttpFunction("httpsv2reaction"), ]); expect(resp1.status).to.eq(200); expect(resp2.status).to.eq(200); - await new Promise((resolve) => setTimeout(resolve, EMULATORS_WRITE_DELAY_MS)); - }); - - it("should have triggered the cloud functions", () => { - expect(test.httpsV2TriggerCount).to.eq(2); }); }); diff --git a/scripts/triggers-end-to-end-tests/triggers/index.js b/scripts/triggers-end-to-end-tests/triggers/index.js index 221622cf8fc..35ffe4c73df 100644 --- a/scripts/triggers-end-to-end-tests/triggers/index.js +++ b/scripts/triggers-end-to-end-tests/triggers/index.js @@ -8,21 +8,9 @@ const { createUserWithEmailAndPassword, signInWithEmailAndPassword, } = require("firebase/auth"); -const fs = require("fs"); -const path = require("path"); -const fetch = require("node-fetch"); - -// load the config from the parent dir -const filename = path.join(__dirname, "/../firebase.json"); -const data = fs.readFileSync(filename, "utf8"); -const config = JSON.parse(data); const FIREBASE_PROJECT = process.env.FBTOOLS_TARGET_PROJECT || ""; -const FUNCTIONS_PORT = config.emulators.functions?.port || "9002"; - -const FUNCTIONS_REGION = "us-central1"; - /* * We install onWrite triggers for START_DOCUMENT_NAME in both the firestore and * database emulators. From each respective onWrite trigger, we write a document @@ -55,18 +43,6 @@ const app = initializeApp( const auth = getAuth(app); connectAuthEmulator(auth, `http://${process.env.FIREBASE_AUTH_EMULATOR_HOST}`); -function invokeHttpsFunction(name) { - const url = `http://localhost:${[FUNCTIONS_PORT, FIREBASE_PROJECT, FUNCTIONS_REGION, name].join( - "/" - )}`; - return fetch(url); -} - -exports.triggerHttpsFunction = functions.https.onRequest(async (req, res) => { - await invokeHttpsFunction("httpsv2reaction"); - res.json({ triggered: true }); -}); - exports.deleteFromFirestore = functions.https.onRequest(async (req, res) => { await admin.firestore().doc(START_DOCUMENT_NAME).delete(); res.json({ deleted: true }); diff --git a/scripts/triggers-end-to-end-tests/v2/index.js b/scripts/triggers-end-to-end-tests/v2/index.js index c6a6d887cd3..3978bfad112 100644 --- a/scripts/triggers-end-to-end-tests/v2/index.js +++ b/scripts/triggers-end-to-end-tests/v2/index.js @@ -23,7 +23,6 @@ const AUTH_BLOCKING_CREATE_V2_LOG = const AUTH_BLOCKING_SIGN_IN_V2_LOG = "========== AUTH BLOCKING SIGN IN V2 FUNCTION METADATA =========="; const RTDB_LOG = "========== RTDB V2 FUNCTION =========="; -const HTTPS_FUNCTION_LOG = "========== HTTPS V2 FUNCTION =========="; const PUBSUB_TOPIC = "test-topic"; const START_DOCUMENT_NAME = "test/start"; @@ -31,8 +30,7 @@ const START_DOCUMENT_NAME = "test/start"; admin.initializeApp(); exports.httpsv2reaction = functionsV2.https.onRequest((req, res) => { - console.log(HTTPS_FUNCTION_LOG); - res.send(); + res.send("httpsv2reaction"); }); exports.pubsubv2reaction = functionsV2.pubsub.onMessagePublished(PUBSUB_TOPIC, (cloudevent) => { diff --git a/src/test/emulators/functionsRuntimeWorker.spec.ts b/src/test/emulators/functionsRuntimeWorker.spec.ts index ffd84c9d4c2..d0cb0ca557d 100644 --- a/src/test/emulators/functionsRuntimeWorker.spec.ts +++ b/src/test/emulators/functionsRuntimeWorker.spec.ts @@ -86,7 +86,7 @@ describe("FunctionsRuntimeWorker", () => { const workerPool = new RuntimeWorkerPool(); describe("RuntimeWorker", () => { - it("goes from idle --> busy --> idle in normal operation", async () => { + it("goes from created --> idle --> busy --> idle in normal operation", async () => { const scope = nock("http://localhost").get("/").reply(200); const worker = new RuntimeWorker(workerPool.getKey("trigger"), new MockRuntimeInstance()); @@ -105,7 +105,7 @@ describe("FunctionsRuntimeWorker", () => { expect(counter.total).to.eql(4); }); - it("goes from idle --> busy --> finished when there's an error", async () => { + it("goes from created --> idle --> busy --> finished when there's an error", async () => { const scope = nock("http://localhost").get("/").replyWithError("boom"); const worker = new RuntimeWorker(workerPool.getKey("trigger"), new MockRuntimeInstance()); @@ -125,7 +125,7 @@ describe("FunctionsRuntimeWorker", () => { expect(counter.total).to.eql(4); }); - it("goes from busy --> finishing --> finished when marked", async () => { + it("goes from created --> busy --> finishing --> finished when marked", async () => { const scope = nock("http://localhost").get("/").replyWithError("boom"); const worker = new RuntimeWorker(workerPool.getKey("trigger"), new MockRuntimeInstance());