diff --git a/integration_test/README.md b/integration_test/README.md index c6a0ca32d..3b0f5413f 100644 --- a/integration_test/README.md +++ b/integration_test/README.md @@ -8,6 +8,15 @@ Run the integration test as follows: ./run_tests.sh [] ``` -If just one project_id is provided, the both the node6 and node8 tests will be run on that project, in series. If two project_ids are provided, the node6 tests will be run on the first project and the node8 tests will be run on the second one, in parallel. +Test runs cycles of testing, once for Node.js 14 and another for Node.js 16. -The tests run fully automatically, and will print the result on standard out. The integration test for HTTPS is that it properly kicks off other integration tests and returns a result. From there the other integration test suites will write their results back to the database, where you can check the detailed results if you'd like. +Test uses locally installed firebase to invoke commands for deploying function. The test also requires that you have +gcloud CLI installed and authenticated (`gcloud auth login`). + +Integration test is triggered by invoking HTTP function integrationTest which in turns invokes each function trigger +by issuing actions necessary to trigger it (e.g. write to storage bucket). + +### Debugging + +The status and result of each test is stored in RTDB of the project used for testing. You can also inspect Cloud Logging +for more clues. diff --git a/integration_test/firebase.json b/integration_test/firebase.json index ce496e265..382d79c84 100644 --- a/integration_test/firebase.json +++ b/integration_test/firebase.json @@ -5,5 +5,8 @@ "firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json" + }, + "functions": { + "predeploy": ["npm --prefix \"$RESOURCE_DIR\" run build"] } } diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index e8c6918a5..3a5fd8474 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -1,4 +1,5 @@ import { PubSub } from '@google-cloud/pubsub'; +import { GoogleAuth } from 'google-auth-library'; import { Request, Response } from 'express'; import * as admin from 'firebase-admin'; import * as functions from 'firebase-functions'; @@ -22,17 +23,19 @@ const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG); admin.initializeApp(); async function callHttpsTrigger(name: string, data: any) { - const resp = await fetch( - `https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/${name}`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ data }), - } + const url = `https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/${name}`; + const client = await new GoogleAuth().getIdTokenClient( + '32555940559.apps.googleusercontent.com' ); - if (!resp.ok) { + const resp = await client.request({ + url, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ data }), + }); + if (resp.status > 200) { throw Error(resp.statusText); } } @@ -42,7 +45,7 @@ async function callV2HttpsTrigger( data: any, accessToken: string ) { - let resp = await fetch( + const getFnResp = await fetch( `https://cloudfunctions.googleapis.com/v2beta/projects/${firebaseConfig.projectId}/locations/${REGION}/functions/${name}`, { headers: { @@ -50,23 +53,28 @@ async function callV2HttpsTrigger( }, } ); - if (!resp.ok) { - throw new Error(resp.statusText); + if (!getFnResp.ok) { + throw new Error(getFnResp.statusText); } - const fn = await resp.json(); + const fn = await getFnResp.json(); const uri = fn.serviceConfig?.uri; if (!uri) { throw new Error(`Cannot call v2 https trigger ${name} - no uri found`); } - resp = await fetch(uri, { + + const client = await new GoogleAuth().getIdTokenClient( + '32555940559.apps.googleusercontent.com' + ); + const invokeFnREsp = await client.request({ + url: uri, method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ data }), }); - if (!resp.ok) { - throw new Error(resp.statusText); + if (invokeFnREsp.status > 200) { + throw Error(invokeFnREsp.statusText); } } @@ -170,6 +178,7 @@ export const integrationTests: any = functions .region(REGION) .runWith({ timeoutSeconds: 540, + invoker: 'private', }) .https.onRequest(async (req: Request, resp: Response) => { const testId = admin diff --git a/integration_test/functions/src/v1/https-tests.ts b/integration_test/functions/src/v1/https-tests.ts index 8acf8932f..bce05f341 100644 --- a/integration_test/functions/src/v1/https-tests.ts +++ b/integration_test/functions/src/v1/https-tests.ts @@ -2,10 +2,13 @@ import * as functions from 'firebase-functions'; import { REGION } from '../region'; import { expectEq, TestSuite } from '../testing'; -export const callableTests: any = functions.region(REGION).https.onCall((d) => { - return new TestSuite('https onCall') - .it('should have the correct data', (data: any) => - expectEq(data?.foo, 'bar') - ) - .run(d.testId, d); -}); +export const callableTests: any = functions + .runWith({ invoker: 'private' }) + .region(REGION) + .https.onCall((d) => { + return new TestSuite('https onCall') + .it('should have the correct data', (data: any) => + expectEq(data?.foo, 'bar') + ) + .run(d.testId, d); + }); diff --git a/integration_test/functions/src/v2/https-tests.ts b/integration_test/functions/src/v2/https-tests.ts index 4936c48ea..448464ef0 100644 --- a/integration_test/functions/src/v2/https-tests.ts +++ b/integration_test/functions/src/v2/https-tests.ts @@ -1,7 +1,7 @@ import { onCall } from 'firebase-functions/v2/https'; import { expectEq, TestSuite } from '../testing'; -export const callabletests = onCall((req) => { +export const callabletests = onCall({ invoker: 'private' }, (req) => { return new TestSuite('v2 https onCall') .it('should have the correct data', (data: any) => expectEq(data?.foo, 'bar') diff --git a/integration_test/functions/tsconfig.json b/integration_test/functions/tsconfig.json index a5bd57033..77fb279d5 100644 --- a/integration_test/functions/tsconfig.json +++ b/integration_test/functions/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "lib": ["es6", "dom"], "module": "commonjs", - "target": "es6", + "target": "es2020", "noImplicitAny": false, "outDir": "lib", "declaration": true, diff --git a/integration_test/run_tests.sh b/integration_test/run_tests.sh index 814ddc492..9098c912c 100755 --- a/integration_test/run_tests.sh +++ b/integration_test/run_tests.sh @@ -74,8 +74,6 @@ function delete_all_functions { } function deploy { - cd "${DIR}" - ./functions/node_modules/.bin/tsc -p functions/ # Deploy functions, and security rules for database and Firestore. If the deploy fails, retry twice if [[ "${TOKEN}" == "" ]]; then for i in 1 2 3; do firebase deploy --project="${PROJECT_ID}" --only functions,database,firestore && break; done @@ -97,7 +95,7 @@ function run_tests { TEST_URL="https://${FIREBASE_FUNCTIONS_TEST_REGION}-${PROJECT_ID}.${TEST_DOMAIN}/integrationTests" echo "${TEST_URL}" - curl --fail "${TEST_URL}" + curl --fail -H "Authorization: Bearer $(gcloud auth print-identity-token)" "${TEST_URL}" } function cleanup { diff --git a/package.json b/package.json index 000d85096..e6c277745 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ "docgen:v2:toc": "ts-node docgen/toc.ts --input docgen/v2/markdown --output docgen/v2/markdown/toc --path /docs/functions/beta/reference", "docgen:v2:gen": "api-documenter-fire markdown -i docgen/v2 -o docgen/v2/markdown && npm run docgen:v2:toc", "docgen:v2": "npm run build && npm run docgen:v2:extract && npm run docgen:v2:gen", - "build:pack": "rm -rf lib && npm install --production && tsc -p tsconfig.release.json && npm pack", + "build:pack": "rm -rf lib && npm install && tsc -p tsconfig.release.json && npm pack", "build:release": "npm ci --production && npm install --no-save typescript firebase-admin && tsc -p tsconfig.release.json", "build": "tsc -p tsconfig.release.json", "build:watch": "npm run build -- -w",