Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions integration_test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ Run the integration test as follows:
./run_tests.sh <project_id> [<project_id2>]
```

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.
3 changes: 3 additions & 0 deletions integration_test/firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"functions": {
"predeploy": ["npm --prefix \"$RESOURCE_DIR\" run build"]
}
}
43 changes: 26 additions & 17 deletions integration_test/functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this hard-coded project-specific string?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing some research, this is the CLI appID? Whatever it is, can't we assume that the default compute service account has invoker permissions and use the ADC?

);
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);
}
}
Expand All @@ -42,31 +45,36 @@ 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: {
Authorization: `Bearer ${accessToken}`,
},
}
);
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);
}
}

Expand Down Expand Up @@ -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
Expand Down
17 changes: 10 additions & 7 deletions integration_test/functions/src/v1/https-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
2 changes: 1 addition & 1 deletion integration_test/functions/src/v2/https-tests.ts
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
2 changes: 1 addition & 1 deletion integration_test/functions/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"lib": ["es6", "dom"],
"module": "commonjs",
"target": "es6",
"target": "es2020",
"noImplicitAny": false,
"outDir": "lib",
"declaration": true,
Expand Down
4 changes: 1 addition & 3 deletions integration_test/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down