Skip to content

Commit b545183

Browse files
committed
integration test.
1 parent 534505c commit b545183

16 files changed

+772
-9
lines changed

integration_test/README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,30 @@ Run the integration test as follows:
88
./run_tests.sh <project_id> [<project_id2>]
99
```
1010

11-
Test runs cycles of testing, once for Node.js 14 and another for Node.js 16.
11+
Test runs cycles of testing, once for Node.js 18 and another for Node.js 20.
1212

1313
Test uses locally installed firebase to invoke commands for deploying function. The test also requires that you have
1414
gcloud CLI installed and authenticated (`gcloud auth login`).
1515

1616
Integration test is triggered by invoking HTTP function integrationTest which in turns invokes each function trigger
1717
by issuing actions necessary to trigger it (e.g. write to storage bucket).
1818

19+
### Tested v2 Triggers
20+
21+
The integration tests now cover all v2 SDK triggers:
22+
- **HTTPS**: onCall (including streaming), onRequest
23+
- **Database**: onValueWritten, onValueCreated, onValueDeleted, onValueUpdated
24+
- **Firestore**: onDocumentWritten, onDocumentCreated, onDocumentDeleted, onDocumentUpdated
25+
- **Storage**: onObjectFinalized, onObjectDeleted, onObjectArchived, onObjectMetadataUpdated
26+
- **Pub/Sub**: onMessagePublished (with retry support)
27+
- **Scheduled**: onSchedule
28+
- **Tasks**: onTaskDispatched (with retry support)
29+
- **Remote Config**: onConfigUpdated
30+
- **Test Lab**: onTestMatrixCompleted
31+
- **Identity**: beforeUserCreated, beforeUserSignedIn
32+
- **Eventarc**: onCustomEventPublished
33+
- **Alerts**: Crashlytics, Billing, App Distribution, Performance alerts
34+
1935
### Debugging
2036

2137
The status and result of each test is stored in RTDB of the project used for testing. You can also inspect Cloud Logging

integration_test/functions/src/index.ts

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,77 @@ function v1Tests(testId: string, accessToken: string): Array<Promise<unknown>> {
169169
function v2Tests(testId: string, accessToken: string): Array<Promise<void>> {
170170
return [
171171
// Invoke a callable HTTPS trigger.
172-
// TODO: Temporarily disable - doesn't work unless running on projects w/ permission to create public functions.
173-
// callV2HttpsTrigger("v2-callabletests", { foo: "bar", testId }, accessToken),
172+
callV2HttpsTrigger("v2-callabletests", { foo: "bar", testId }, accessToken),
173+
// Test streaming callable function
174+
callV2HttpsTrigger("v2-callabletestsstreaming", { testId, streaming: true }, accessToken),
175+
// Test onRequest endpoints
176+
fetch(
177+
`https://${REGION}-${firebaseConfig.projectId}.cloudfunctions.net/v2-httpstests?testId=${testId}`
178+
).then((r) => r.text()),
174179
// Invoke a scheduled trigger.
175180
callV2ScheduleTrigger("v2-schedule", "us-central1", accessToken),
181+
// Database triggers
182+
admin.database().ref(`v2tests/${testId}/db/written`).set("world"),
183+
admin.database().ref(`v2tests/${testId}/db/created`).set("created"),
184+
admin
185+
.database()
186+
.ref(`v2tests/${testId}/db/updated`)
187+
.set("original")
188+
.then(() => admin.database().ref(`v2tests/${testId}/db/updated`).set("updated")),
189+
admin
190+
.database()
191+
.ref(`v2tests/${testId}/db/toDelete`)
192+
.set("willBeDeleted")
193+
.then(() => admin.database().ref(`v2tests/${testId}/db/toDelete`).remove()),
194+
// Storage triggers
195+
admin
196+
.storage()
197+
.bucket()
198+
.upload(`/tmp/${testId}.txt`, { destination: `v2tests/${testId}/test.txt` }),
199+
// Firestore triggers
200+
admin
201+
.firestore()
202+
.collection(`v2tests/${testId}/firestore`)
203+
.doc("test")
204+
.set({ value: "written" }),
205+
admin
206+
.firestore()
207+
.collection(`v2tests/${testId}/firestore-created`)
208+
.doc("test")
209+
.set({ value: "created" }),
210+
admin
211+
.firestore()
212+
.collection(`v2tests/${testId}/firestore-updated`)
213+
.doc("test")
214+
.set({ value: "original" })
215+
.then(() =>
216+
admin
217+
.firestore()
218+
.collection(`v2tests/${testId}/firestore-updated`)
219+
.doc("test")
220+
.update({ value: "updated" })
221+
),
222+
admin
223+
.firestore()
224+
.collection(`v2tests/${testId}/firestore-to-delete`)
225+
.doc("deleteMe")
226+
.set({ value: "toBeDeleted" })
227+
.then(() =>
228+
admin
229+
.firestore()
230+
.collection(`v2tests/${testId}/firestore-to-delete`)
231+
.doc("deleteMe")
232+
.delete()
233+
),
234+
// Pub/Sub triggers
235+
new PubSub()
236+
.topic("v2-pubsub-test-topic")
237+
.publish(Buffer.from(JSON.stringify({ testId })), { testId }),
238+
new PubSub()
239+
.topic("v2-pubsub-test-retry-topic")
240+
.publish(Buffer.from(JSON.stringify({ testRetry: true })), { testId, attempt: "1" }),
241+
// Remote Config trigger
242+
updateRemoteConfig(`v2-${testId}`, accessToken),
176243
];
177244
}
178245

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import {
2+
onAlertPublished,
3+
crashlytics,
4+
billing,
5+
appDistribution,
6+
performance,
7+
} from "firebase-functions/v2/alerts";
8+
import { expectEq, TestSuite } from "../testing";
9+
10+
export const alertstestsCrashlytics = onAlertPublished(
11+
{ alertType: "crashlytics.newFatalIssue" },
12+
(event) => {
13+
const testId = event.appId || "unknown";
14+
15+
return new TestSuite("alerts crashlytics onAlertPublished")
16+
.it("should have alert type", () => {
17+
expectEq(event.alertType, "crashlytics.newFatalIssue");
18+
})
19+
.it("should have app id", () => {
20+
expectEq(typeof event.appId, "string");
21+
})
22+
.run(testId, event);
23+
}
24+
);
25+
26+
export const alertstestsBilling = onAlertPublished({ alertType: "billing.planUpdate" }, (event) => {
27+
const testId = event.appId || "unknown";
28+
29+
return new TestSuite("alerts billing onAlertPublished")
30+
.it("should have alert type", () => {
31+
expectEq(event.alertType, "billing.planUpdate");
32+
})
33+
.run(testId, event);
34+
});
35+
36+
export const alertstestsAppDistribution = appDistribution.onNewTesterIosDevicePublished((event) => {
37+
const testId = event.appId || "unknown";
38+
39+
return new TestSuite("alerts appDistribution onNewTesterIosDevicePublished")
40+
.it("should have app id", () => {
41+
expectEq(typeof event.appId, "string");
42+
})
43+
.it("should have tester name", () => {
44+
expectEq(typeof event.data.testerName, "string");
45+
})
46+
.it("should have device model", () => {
47+
expectEq(typeof event.data.testerDeviceModelName, "string");
48+
})
49+
.it("should have device identifier", () => {
50+
expectEq(typeof event.data.testerDeviceIdentifier, "string");
51+
})
52+
.run(testId, event);
53+
});
54+
55+
export const alertstestsPerformance = performance.onThresholdAlertPublished((event) => {
56+
const testId = event.appId || "unknown";
57+
58+
return new TestSuite("alerts performance onThresholdAlertPublished")
59+
.it("should have app id", () => {
60+
expectEq(typeof event.appId, "string");
61+
})
62+
.it("should have metric type", () => {
63+
expectEq(typeof event.data.metricType, "string");
64+
})
65+
.it("should have event name", () => {
66+
expectEq(typeof event.data.eventName, "string");
67+
})
68+
.it("should have threshold value", () => {
69+
expectEq(typeof event.data.thresholdValue, "number");
70+
})
71+
.it("should have threshold unit", () => {
72+
expectEq(typeof event.data.thresholdUnit, "string");
73+
})
74+
.run(testId, event);
75+
});
76+
77+
export const alertstestsCrashlyticsRegression = crashlytics.onRegressionAlertPublished((event) => {
78+
const testId = event.appId || "unknown";
79+
80+
return new TestSuite("alerts crashlytics onRegressionAlertPublished")
81+
.it("should have app id", () => {
82+
expectEq(typeof event.appId, "string");
83+
})
84+
.it("should have issue data", () => {
85+
expectEq(typeof event.data.issue, "object");
86+
})
87+
.it("should have issue id", () => {
88+
expectEq(typeof event.data.issue.id, "string");
89+
})
90+
.it("should have issue title", () => {
91+
expectEq(typeof event.data.issue.title, "string");
92+
})
93+
.run(testId, event);
94+
});
95+
96+
export const alertstestsBillingAutomated = billing.onPlanAutomatedUpdatePublished((event) => {
97+
const testId = event.appId || "unknown";
98+
99+
return new TestSuite("alerts billing onPlanAutomatedUpdatePublished")
100+
.it("should have billing plan", () => {
101+
expectEq(typeof event.data.billingPlan, "string");
102+
})
103+
.it("should have notification type", () => {
104+
expectEq(typeof event.data.notificationType, "string");
105+
})
106+
.run(testId, event);
107+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as admin from "firebase-admin";
2+
import {
3+
onValueWritten,
4+
onValueCreated,
5+
onValueDeleted,
6+
onValueUpdated,
7+
} from "firebase-functions/v2/database";
8+
import { expectEq, TestSuite } from "../testing";
9+
10+
export const databasetestsWritten = onValueWritten("/v2tests/{testId}/db/written", (event) => {
11+
return new TestSuite("database onValueWritten")
12+
.it("should have path", () => {
13+
expectEq(event.params.testId, event.data.after.ref.parent?.parent?.key);
14+
})
15+
.it("should have data", () => {
16+
expectEq(event.data.after.val(), "world");
17+
})
18+
.it("should have before data", () => {
19+
expectEq(event.data.before.val(), null);
20+
})
21+
.run(event.params.testId, event.data);
22+
});
23+
24+
export const databasetestsCreated = onValueCreated("/v2tests/{testId}/db/created", (event) => {
25+
return new TestSuite("database onValueCreated")
26+
.it("should have path", () => {
27+
expectEq(event.params.testId, event.data.ref.parent?.parent?.key);
28+
})
29+
.it("should have data", () => {
30+
expectEq(event.data.val(), "created");
31+
})
32+
.run(event.params.testId, event.data);
33+
});
34+
35+
export const databasetestsDeleted = onValueDeleted(
36+
"/v2tests/{testId}/db/deleted",
37+
async (event) => {
38+
// First write a value to be deleted
39+
const db = admin.database();
40+
await db.ref(`/v2tests/${event.params.testId}/db/toDelete`).set("willBeDeleted");
41+
42+
return new TestSuite("database onValueDeleted")
43+
.it("should have path", () => {
44+
expectEq(event.params.testId, event.data.ref.parent?.parent?.key);
45+
})
46+
.it("should have previous data", () => {
47+
expectEq(event.data.val(), "willBeDeleted");
48+
})
49+
.run(event.params.testId, event.data);
50+
}
51+
);
52+
53+
export const databasetestsUpdated = onValueUpdated("/v2tests/{testId}/db/updated", (event) => {
54+
return new TestSuite("database onValueUpdated")
55+
.it("should have path", () => {
56+
expectEq(event.params.testId, event.data.after.ref.parent?.parent?.key);
57+
})
58+
.it("should have new data", () => {
59+
expectEq(event.data.after.val(), "updated");
60+
})
61+
.it("should have old data", () => {
62+
expectEq(event.data.before.val(), "original");
63+
})
64+
.run(event.params.testId, event.data);
65+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { onCustomEventPublished } from "firebase-functions/v2/eventarc";
2+
import { expectEq, TestSuite } from "../testing";
3+
4+
export const eventarctests = onCustomEventPublished("test.v2.custom.event", (event) => {
5+
const testId = event.data?.testId || "unknown";
6+
7+
return new TestSuite("eventarc onCustomEventPublished")
8+
.it("should have event type", () => {
9+
expectEq(event.type, "test.v2.custom.event");
10+
})
11+
.it("should have event id", () => {
12+
expectEq(typeof event.id, "string");
13+
})
14+
.it("should have event source", () => {
15+
expectEq(typeof event.source, "string");
16+
})
17+
.it("should have event time", () => {
18+
expectEq(typeof event.time, "string");
19+
})
20+
.it("should have event data", () => {
21+
expectEq(event.data?.message, "Hello from Eventarc");
22+
})
23+
.it("should have custom attributes", () => {
24+
expectEq(event.data?.customAttribute, "customValue");
25+
})
26+
.run(testId, event);
27+
});
28+
29+
export const eventarctestsWithFilter = onCustomEventPublished(
30+
{
31+
eventType: "test.v2.filtered.event",
32+
channel: "locations/us-central1/channels/firebase",
33+
filters: {
34+
attribute1: "value1",
35+
},
36+
},
37+
(event) => {
38+
const testId = event.data?.testId || "unknown";
39+
40+
return new TestSuite("eventarc onCustomEventPublished with filters")
41+
.it("should have matching event type", () => {
42+
expectEq(event.type, "test.v2.filtered.event");
43+
})
44+
.it("should have filtered attribute", () => {
45+
expectEq(event.data?.attribute1, "value1");
46+
})
47+
.it("should have channel data", () => {
48+
expectEq(typeof event.source, "string");
49+
})
50+
.run(testId, event);
51+
}
52+
);

0 commit comments

Comments
 (0)