@causa/runtime-google 0.40.0 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +7 -14
  2. package/dist/app-check/testing.d.ts +7 -3
  3. package/dist/app-check/testing.js +9 -6
  4. package/dist/firebase/testing.d.ts +7 -4
  5. package/dist/firebase/testing.js +12 -7
  6. package/dist/firestore/testing.d.ts +32 -17
  7. package/dist/firestore/testing.js +45 -29
  8. package/dist/identity-platform/testing.d.ts +21 -6
  9. package/dist/identity-platform/testing.js +34 -9
  10. package/dist/pubsub/publisher.module.d.ts +1 -1
  11. package/dist/pubsub/{testing/fixture.d.ts → testing.d.ts} +64 -48
  12. package/dist/pubsub/{testing/fixture.js → testing.js} +106 -73
  13. package/dist/spanner/column.decorator.d.ts +1 -11
  14. package/dist/spanner/column.decorator.js +2 -7
  15. package/dist/spanner/conversion.d.ts +5 -18
  16. package/dist/spanner/conversion.js +45 -125
  17. package/dist/spanner/entity-manager.d.ts +19 -23
  18. package/dist/spanner/entity-manager.js +26 -60
  19. package/dist/spanner/table-cache.js +0 -3
  20. package/dist/spanner/testing.d.ts +37 -18
  21. package/dist/spanner/testing.js +75 -10
  22. package/dist/spanner/types.d.ts +0 -6
  23. package/dist/testing.d.ts +36 -2
  24. package/dist/testing.js +33 -2
  25. package/dist/transaction/firestore-pubsub/index.d.ts +3 -2
  26. package/dist/transaction/firestore-pubsub/index.js +2 -1
  27. package/dist/transaction/firestore-pubsub/nestjs-collection-resolver.d.ts +1 -1
  28. package/dist/transaction/firestore-pubsub/nestjs-collection-resolver.js +0 -1
  29. package/dist/transaction/firestore-pubsub/readonly-state-transaction.d.ts +37 -0
  30. package/dist/transaction/firestore-pubsub/readonly-state-transaction.js +46 -0
  31. package/dist/transaction/firestore-pubsub/runner.d.ts +6 -4
  32. package/dist/transaction/firestore-pubsub/runner.js +16 -7
  33. package/dist/transaction/firestore-pubsub/state-transaction.d.ts +11 -49
  34. package/dist/transaction/firestore-pubsub/state-transaction.js +19 -42
  35. package/dist/transaction/firestore-pubsub/transaction.d.ts +15 -3
  36. package/dist/transaction/firestore-pubsub/transaction.js +21 -3
  37. package/dist/transaction/firestore-pubsub/types.d.ts +36 -0
  38. package/dist/transaction/firestore-pubsub/types.js +1 -0
  39. package/dist/transaction/index.d.ts +0 -3
  40. package/dist/transaction/index.js +0 -3
  41. package/dist/transaction/spanner-outbox/index.d.ts +3 -1
  42. package/dist/transaction/spanner-outbox/index.js +3 -0
  43. package/dist/transaction/spanner-outbox/readonly-transaction.d.ts +22 -0
  44. package/dist/transaction/spanner-outbox/readonly-transaction.js +25 -0
  45. package/dist/transaction/spanner-outbox/runner.d.ts +7 -18
  46. package/dist/transaction/spanner-outbox/runner.js +13 -6
  47. package/dist/transaction/{spanner-utils.js → spanner-outbox/spanner-utils.js} +1 -1
  48. package/dist/transaction/spanner-outbox/state-transaction.d.ts +20 -0
  49. package/dist/transaction/spanner-outbox/state-transaction.js +34 -0
  50. package/dist/transaction/spanner-outbox/transaction.d.ts +28 -0
  51. package/dist/transaction/spanner-outbox/transaction.js +38 -0
  52. package/package.json +24 -21
  53. package/dist/pubsub/testing/index.d.ts +0 -4
  54. package/dist/pubsub/testing/index.js +0 -2
  55. package/dist/pubsub/testing/requester.d.ts +0 -50
  56. package/dist/pubsub/testing/requester.js +0 -53
  57. package/dist/testing/google-app-fixture.d.ts +0 -191
  58. package/dist/testing/google-app-fixture.js +0 -200
  59. package/dist/testing/index.d.ts +0 -1
  60. package/dist/testing/index.js +0 -1
  61. package/dist/transaction/spanner-pubsub/index.d.ts +0 -2
  62. package/dist/transaction/spanner-pubsub/index.js +0 -2
  63. package/dist/transaction/spanner-pubsub/module.d.ts +0 -14
  64. package/dist/transaction/spanner-pubsub/module.js +0 -21
  65. package/dist/transaction/spanner-pubsub/runner.d.ts +0 -23
  66. package/dist/transaction/spanner-pubsub/runner.js +0 -69
  67. package/dist/transaction/spanner-state-transaction.d.ts +0 -20
  68. package/dist/transaction/spanner-state-transaction.js +0 -35
  69. package/dist/transaction/spanner-transaction.d.ts +0 -16
  70. package/dist/transaction/spanner-transaction.js +0 -20
  71. /package/dist/transaction/{spanner-utils.d.ts → spanner-outbox/spanner-utils.d.ts} +0 -0
@@ -0,0 +1,38 @@
1
+ import { OutboxEventTransaction, Transaction, } from '@causa/runtime';
2
+ import { SpannerEntityManager, } from '../../spanner/index.js';
3
+ /**
4
+ * A {@link Transaction} that uses Spanner for state (and outbox) storage, and Pub/Sub for event publishing.
5
+ */
6
+ export class SpannerOutboxTransaction extends Transaction {
7
+ stateTransaction;
8
+ eventTransaction;
9
+ constructor(stateTransaction, eventTransaction, publishOptions = {}) {
10
+ super(publishOptions);
11
+ this.stateTransaction = stateTransaction;
12
+ this.eventTransaction = eventTransaction;
13
+ }
14
+ /**
15
+ * The underlying {@link SpannerTransaction} used by the state transaction.
16
+ */
17
+ get spannerTransaction() {
18
+ return this.stateTransaction.spannerTransaction;
19
+ }
20
+ /**
21
+ * The underlying {@link SpannerEntityManager} used by the state transaction.
22
+ */
23
+ get entityManager() {
24
+ return this.stateTransaction.entityManager;
25
+ }
26
+ set(entity) {
27
+ return this.stateTransaction.set(entity);
28
+ }
29
+ delete(type, key) {
30
+ return this.stateTransaction.delete(type, key);
31
+ }
32
+ get(type, entity) {
33
+ return this.stateTransaction.get(type, entity);
34
+ }
35
+ publish(topic, event, options) {
36
+ return this.eventTransaction.publish(topic, event, options);
37
+ }
38
+ }
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@causa/runtime-google",
3
- "version": "0.40.0",
3
+ "version": "1.0.0-rc.1",
4
4
  "description": "An extension to the Causa runtime SDK (`@causa/runtime`), providing Google-specific features.",
5
- "repository": "github:causa-io/runtime-typescript-google",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/causa-io/runtime-typescript-google.git"
8
+ },
6
9
  "license": "ISC",
7
10
  "type": "module",
8
11
  "engines": {
9
- "node": ">=16"
12
+ "node": ">=20"
10
13
  },
11
14
  "main": "dist/index.js",
12
15
  "types": "dist/index.d.ts",
@@ -29,15 +32,15 @@
29
32
  "test:cov": "npm run test -- --coverage"
30
33
  },
31
34
  "dependencies": {
32
- "@causa/runtime": ">= 0.27.2 < 1.0.0",
35
+ "@causa/runtime": ">= 1.0.0-rc",
33
36
  "@google-cloud/precise-date": "^5.0.0",
34
- "@google-cloud/pubsub": "^5.0.0",
37
+ "@google-cloud/pubsub": "^5.1.0",
35
38
  "@google-cloud/spanner": "^8.0.0",
36
39
  "@google-cloud/tasks": "^6.1.0",
37
40
  "@grpc/grpc-js": "^1.13.4",
38
- "@nestjs/common": "^11.1.2",
41
+ "@nestjs/common": "^11.1.3",
39
42
  "@nestjs/config": "^4.0.2",
40
- "@nestjs/core": "^11.1.2",
43
+ "@nestjs/core": "^11.1.3",
41
44
  "@nestjs/passport": "^11.0.5",
42
45
  "@nestjs/terminus": "^11.0.0",
43
46
  "class-transformer": "^0.5.1",
@@ -50,28 +53,28 @@
50
53
  "reflect-metadata": "^0.2.2"
51
54
  },
52
55
  "devDependencies": {
53
- "@nestjs/testing": "^11.1.2",
54
- "@swc/core": "^1.11.29",
55
- "@swc/jest": "^0.2.38",
56
+ "@nestjs/testing": "^11.1.3",
57
+ "@swc/core": "^1.12.11",
58
+ "@swc/jest": "^0.2.39",
56
59
  "@tsconfig/node22": "^22.0.2",
57
- "@types/jest": "^29.5.14",
58
- "@types/jsonwebtoken": "^9.0.9",
59
- "@types/node": "^22.15.23",
60
+ "@types/jest": "^30.0.0",
61
+ "@types/jsonwebtoken": "^9.0.10",
62
+ "@types/node": "^22.16.2",
60
63
  "@types/passport-http-bearer": "^1.0.41",
61
64
  "@types/supertest": "^6.0.3",
62
65
  "@types/uuid": "^10.0.0",
63
- "dotenv": "^16.5.0",
64
- "eslint": "^9.27.0",
66
+ "dotenv": "^17.1.0",
67
+ "eslint": "^9.30.1",
65
68
  "eslint-config-prettier": "^10.1.5",
66
- "eslint-plugin-prettier": "^5.4.0",
67
- "jest": "^29.7.0",
68
- "jest-extended": "^5.0.3",
69
+ "eslint-plugin-prettier": "^5.5.1",
70
+ "jest": "^30.0.4",
71
+ "jest-extended": "^6.0.0",
69
72
  "rimraf": "^6.0.1",
70
- "supertest": "^7.1.1",
71
- "ts-jest": "^29.3.4",
73
+ "supertest": "^7.1.3",
74
+ "ts-jest": "^29.4.0",
72
75
  "ts-node": "^10.9.2",
73
76
  "typescript": "^5.8.3",
74
- "typescript-eslint": "^8.33.0",
77
+ "typescript-eslint": "^8.36.0",
75
78
  "uuid": "^11.1.0"
76
79
  }
77
80
  }
@@ -1,4 +0,0 @@
1
- export { PubSubFixture } from './fixture.js';
2
- export type { ReceivedPubSubEvent } from './fixture.js';
3
- export { makePubSubRequester } from './requester.js';
4
- export type { EventRequester, EventRequesterOptions } from './requester.js';
@@ -1,2 +0,0 @@
1
- export { PubSubFixture } from './fixture.js';
2
- export { makePubSubRequester } from './requester.js';
@@ -1,50 +0,0 @@
1
- import { type ObjectSerializer } from '@causa/runtime';
2
- import { type INestApplication } from '@nestjs/common';
3
- /**
4
- * Options when making a request to an endpoint handling Pub/Sub events using an {@link EventRequester}.
5
- */
6
- export type EventRequesterOptions = {
7
- /**
8
- * The attributes to add to the Pub/Sub message.
9
- * Using `undefined` values allows removing the attributes set by default.
10
- */
11
- attributes?: Record<string, string | undefined>;
12
- /**
13
- * The expected status code when making the request.
14
- * Default is `200`.
15
- */
16
- expectedStatus?: number;
17
- /**
18
- * The time to set as the publication time of the Pub/Sub message.
19
- */
20
- publishTime?: Date;
21
- };
22
- /**
23
- * A function that makes a query to an endpoint handling Pub/Sub events and tests the response.
24
- */
25
- export type EventRequester = (endpoint: string, event: object, options?: EventRequesterOptions) => Promise<void>;
26
- /**
27
- * Creates an {@link EventRequester} for a NestJS HTTP application handling Pub/Sub messages.
28
- * If the `event` passed to the {@link EventRequester} conforms to the `Event` interface (if it has `producedAt`, `name`
29
- * and / or `id` properties), the default attributes are set in the Pub/Sub message. Default attributes can be
30
- * overridden (or removed by passing `undefined`) using {@link EventRequesterOptions.attributes}.
31
- *
32
- * @param app The NestJS application handling events.
33
- * @param options Options when creating the requester.
34
- * @returns The {@link EventRequester}.
35
- */
36
- export declare function makePubSubRequester(app: INestApplication, options?: {
37
- /**
38
- * The prefix added to all routes handling events.
39
- * Used when constructing the request to the HTTP application.
40
- */
41
- routePrefix?: string;
42
- /**
43
- * The serializer to use to serialize events.
44
- */
45
- serializer?: ObjectSerializer;
46
- /**
47
- * The default expected status code when making a request.
48
- */
49
- expectedStatus?: number;
50
- }): EventRequester;
@@ -1,53 +0,0 @@
1
- import { JsonObjectSerializer } from '@causa/runtime';
2
- import { HttpStatus } from '@nestjs/common';
3
- import supertest from 'supertest';
4
- import * as uuid from 'uuid';
5
- /**
6
- * Creates an {@link EventRequester} for a NestJS HTTP application handling Pub/Sub messages.
7
- * If the `event` passed to the {@link EventRequester} conforms to the `Event` interface (if it has `producedAt`, `name`
8
- * and / or `id` properties), the default attributes are set in the Pub/Sub message. Default attributes can be
9
- * overridden (or removed by passing `undefined`) using {@link EventRequesterOptions.attributes}.
10
- *
11
- * @param app The NestJS application handling events.
12
- * @param options Options when creating the requester.
13
- * @returns The {@link EventRequester}.
14
- */
15
- export function makePubSubRequester(app, options = {}) {
16
- const request = supertest(app.getHttpServer());
17
- const routePrefix = options.routePrefix ?? '';
18
- const serializer = options.serializer ?? new JsonObjectSerializer();
19
- return async (endpoint, event, requestOptions) => {
20
- const messageId = uuid.v4();
21
- const publishTime = (requestOptions?.publishTime ?? new Date()).toISOString();
22
- const buffer = await serializer.serialize(event);
23
- const data = buffer.toString('base64');
24
- // Default attributes if the event conforms to the `Event` interface.
25
- const defaultAttributes = {};
26
- if ('producedAt' in event && event.producedAt instanceof Date) {
27
- defaultAttributes.producedAt = event.producedAt.toISOString();
28
- }
29
- if ('name' in event && typeof event.name === 'string') {
30
- defaultAttributes.eventName = event.name;
31
- }
32
- if ('id' in event && typeof event.id === 'string') {
33
- defaultAttributes.eventId = event.id;
34
- }
35
- const attributes = { ...defaultAttributes, ...requestOptions?.attributes };
36
- await request
37
- .post(`${routePrefix}${endpoint}`)
38
- .send({
39
- message: {
40
- messageId,
41
- message_id: messageId,
42
- publishTime,
43
- publish_time: publishTime,
44
- attributes,
45
- data,
46
- },
47
- subscription: 'subscription',
48
- })
49
- .expect(requestOptions?.expectedStatus ??
50
- options.expectedStatus ??
51
- HttpStatus.OK);
52
- };
53
- }
@@ -1,191 +0,0 @@
1
- import type { VersionedEntity } from '@causa/runtime';
2
- import { type MakeTestAppFactoryOptions } from '@causa/runtime/nestjs/testing';
3
- import { Database, Spanner } from '@google-cloud/spanner';
4
- import type { INestApplication, NestApplicationOptions, Type } from '@nestjs/common';
5
- import { CollectionReference } from 'firebase-admin/firestore';
6
- import { Test } from 'supertest';
7
- import TestAgent from 'supertest/lib/agent.js';
8
- import { AuthUsersFixture } from '../identity-platform/testing.js';
9
- import { type EventRequester, PubSubFixture } from '../pubsub/testing/index.js';
10
- import { SpannerEntityManager, type SpannerKey } from '../spanner/index.js';
11
- /**
12
- * Describes an entity to fetch using the {@link SpannerEntityManager}.
13
- * Used by expect methods in {@link GoogleAppFixture}.
14
- */
15
- type EntityToFetch<T extends object> = {
16
- /**
17
- * The type of entity to fetch.
18
- */
19
- type: Type<T>;
20
- /**
21
- * The ID of the entity to fetch.
22
- */
23
- id: string | SpannerKey;
24
- /**
25
- * Whether to include soft-deleted results when fetching the entity.
26
- * See {@link SpannerEntityManager.findOneByKeyOrFail} options.
27
- */
28
- includeSoftDeletes?: boolean;
29
- };
30
- /**
31
- * Describes tests to run on a versioned entity stored in Spanner.
32
- */
33
- type VersionedEntityTests<T extends object> = {
34
- /**
35
- * The expected entity after mutation.
36
- * It is checked against the actual entity in the database using `toEqual`, which means it can contain matchers.
37
- * A function can be provided to generate the expected entity from the actual entity.
38
- */
39
- expectedEntity: ((actual: T) => T) | T;
40
- /**
41
- * If set, checks that an event has been published to the corresponding Pub/Sub topic with the entity as `data`.
42
- */
43
- expectedEvent?: {
44
- /**
45
- * The name of the topic.
46
- */
47
- topic: string;
48
- /**
49
- * The `name` of the expected published event.
50
- */
51
- name: string;
52
- /**
53
- * Attributes expected to have been set on the published event.
54
- */
55
- attributes?: Record<string, string>;
56
- };
57
- /**
58
- * If set, the passed object is checked to be equal to a "plain object" (e.g. with `Date`s converted to `string`s)
59
- * version of the fetched entity.
60
- * This can contain a parsed HTTP response body which is expected to return the entity.
61
- *
62
- * {@link serializeAsJavaScriptObject} is used to serialize the fetched entity.
63
- */
64
- matchesHttpResponse?: object;
65
- };
66
- /**
67
- * A fixture for testing a NestJS application that uses Google Cloud Platform services.
68
- *
69
- * It manages the setup and teardown of resources in GCP emulators, and provides utilities for testing versioned
70
- * entities.
71
- */
72
- export declare class GoogleAppFixture {
73
- readonly app: INestApplication;
74
- readonly spanner: Spanner;
75
- readonly database: Database;
76
- readonly entityManager: SpannerEntityManager;
77
- readonly pubSub: PubSubFixture;
78
- readonly users: AuthUsersFixture;
79
- readonly request: TestAgent<Test>;
80
- readonly pubSubRequest: EventRequester;
81
- readonly entities: Type[];
82
- readonly firestoreDocuments: Type[];
83
- /**
84
- * Creates a new {@link GoogleAppFixture} instance.
85
- *
86
- * @param app The NestJS application.
87
- * @param spanner The {@link Spanner} client managed by the fixture.
88
- * @param database The temporary Spanner database.
89
- * @param entityManager The {@link SpannerEntityManager} for the temporary database.
90
- * @param pubSub The {@link PubSubFixture} managing temporary topics.
91
- * @param users The {@link AuthUsersFixture}.
92
- * @param request The {@link SuperTest} instance for the NestJS application.
93
- * @param pubSubRequest The {@link EventRequester} to make requests to Pub/Sub push endpoints in the application.
94
- * @param entities The entities to clear from the database when calling {@link GoogleAppFixture.clear}.
95
- * @param firestoreDocuments The Firestore documents to clear when calling {@link GoogleAppFixture.clear}.
96
- */
97
- private constructor();
98
- /**
99
- * Returns the temporary Firestore collection (as set up by the fixture) for a given document type.
100
- *
101
- * @param document The type of document to get the collection for.
102
- * @returns The Firestore collection.
103
- */
104
- firestoreCollection<T>(document: Type<T>): CollectionReference<T>;
105
- /**
106
- * Runs a test on a versioned entity, checking that it has been mutated as expected.
107
- * Optionally, checks that an event has been published to the corresponding Pub/Sub topic.
108
- * Also, a serialized version of the entity (e.g. an HTTP response) can be checked against the expected entity.
109
- *
110
- * @param entity Describes the entity to fetch using the {@link SpannerEntityManager}.
111
- * @param tests The tests to run on the entity and its event.
112
- * @returns The entity fetched from the database.
113
- */
114
- expectMutatedVersionedEntity<T extends Pick<VersionedEntity, 'updatedAt'>>(entity: EntityToFetch<T>, tests: VersionedEntityTests<T>): Promise<T>;
115
- /**
116
- * Ensures the specified entity has not been mutated.
117
- * Optionally, checks that no event has been published to the corresponding Pub/Sub topic.
118
- * Also, a serialized version of the entity (e.g. an HTTP response) can be checked against the expected entity.
119
- *
120
- * @param entity Describes the entity to fetch using the {@link SpannerEntityManager}.
121
- * @param tests The tests to run on the entity.
122
- */
123
- expectNonMutatedVersionedEntity<T extends Pick<VersionedEntity, 'updatedAt'>>(entity: EntityToFetch<T>, tests: Omit<VersionedEntityTests<T>, 'expectedEvent'> & {
124
- expectedEntity: object;
125
- /**
126
- * If set, checks that no event has been published to the corresponding Pub/Sub topic.
127
- */
128
- expectNoEventInTopic?: string;
129
- }): Promise<void>;
130
- /**
131
- * Clears all entities from the test database.
132
- *
133
- * The tables to clear should be specified in the `entities` property of the options passed to
134
- * {@link GoogleAppFixture.create}.
135
- */
136
- clearSpanner(): Promise<void>;
137
- /**
138
- * Clears all documents from the test Firestore collections.
139
- *
140
- * The collections to clear should be specified in the `firestoreDocuments` property of the options passed to
141
- * {@link GoogleAppFixture.create}.
142
- */
143
- clearFirestore(): Promise<void>;
144
- /**
145
- * Clears all entities from the test database and all documents from the test Firestore collections.
146
- * Also clears all messages read from the test Pub/Sub topics up to now.
147
- *
148
- * This does **not** delete Identity Platform users, which are only deleted when calling {
149
- * @link GoogleAppFixture.delete}.
150
- */
151
- clear(): Promise<void>;
152
- /**
153
- * Deletes all resources created by the fixture.
154
- */
155
- delete(): Promise<void>;
156
- /**
157
- * Creates a NestJS application using the specified module, and sets up the fixture.
158
- *
159
- * @param appModule The NestJS module to create the application from.
160
- * @param options Options when creating the GCP resources for the fixture.
161
- * @returns The {@link GoogleAppFixture}.
162
- */
163
- static create(appModule: any, options?: {
164
- /**
165
- * Temporary Pub/Sub topics to create using the {@link PubSubFixture}.
166
- */
167
- pubSubTopics?: Record<string, Type>;
168
- /**
169
- * Temporary Firestore collections to create and to clear during teardown.
170
- */
171
- firestoreDocuments?: Type[];
172
- /**
173
- * Spanner entities to clear during teardown.
174
- */
175
- entities?: Type[];
176
- /**
177
- * Options for the {@link makeTestAppFactory} function.
178
- */
179
- appFactoryOptions?: MakeTestAppFactoryOptions;
180
- /**
181
- * Options passed to the NestJS application factory.
182
- */
183
- nestApplicationOptions?: NestApplicationOptions;
184
- /**
185
- * Whether the `AppCheckGuard` should be disabled.
186
- * Defaults to `true`.
187
- */
188
- disableAppCheck?: boolean;
189
- }): Promise<GoogleAppFixture>;
190
- }
191
- export {};
@@ -1,200 +0,0 @@
1
- import { createApp } from '@causa/runtime/nestjs';
2
- import { makeTestAppFactory, } from '@causa/runtime/nestjs/testing';
3
- import { serializeAsJavaScriptObject } from '@causa/runtime/testing';
4
- import { Database, Spanner } from '@google-cloud/spanner';
5
- import { CollectionReference } from 'firebase-admin/firestore';
6
- import supertest, { Test } from 'supertest';
7
- import TestAgent from 'supertest/lib/agent.js';
8
- import { overrideAppCheck } from '../app-check/testing.js';
9
- import { overrideFirebaseApp } from '../firebase/testing.js';
10
- import { clearFirestoreCollection, getFirestoreCollectionFromModule, overrideFirestoreCollections, } from '../firestore/testing.js';
11
- import { AuthUsersFixture } from '../identity-platform/testing.js';
12
- import { PubSubFixture, makePubSubRequester, } from '../pubsub/testing/index.js';
13
- import { SpannerEntityManager } from '../spanner/index.js';
14
- import { createDatabase, overrideDatabase } from '../spanner/testing.js';
15
- /**
16
- * A fixture for testing a NestJS application that uses Google Cloud Platform services.
17
- *
18
- * It manages the setup and teardown of resources in GCP emulators, and provides utilities for testing versioned
19
- * entities.
20
- */
21
- export class GoogleAppFixture {
22
- app;
23
- spanner;
24
- database;
25
- entityManager;
26
- pubSub;
27
- users;
28
- request;
29
- pubSubRequest;
30
- entities;
31
- firestoreDocuments;
32
- /**
33
- * Creates a new {@link GoogleAppFixture} instance.
34
- *
35
- * @param app The NestJS application.
36
- * @param spanner The {@link Spanner} client managed by the fixture.
37
- * @param database The temporary Spanner database.
38
- * @param entityManager The {@link SpannerEntityManager} for the temporary database.
39
- * @param pubSub The {@link PubSubFixture} managing temporary topics.
40
- * @param users The {@link AuthUsersFixture}.
41
- * @param request The {@link SuperTest} instance for the NestJS application.
42
- * @param pubSubRequest The {@link EventRequester} to make requests to Pub/Sub push endpoints in the application.
43
- * @param entities The entities to clear from the database when calling {@link GoogleAppFixture.clear}.
44
- * @param firestoreDocuments The Firestore documents to clear when calling {@link GoogleAppFixture.clear}.
45
- */
46
- constructor(app, spanner, database, entityManager, pubSub, users, request, pubSubRequest, entities, firestoreDocuments) {
47
- this.app = app;
48
- this.spanner = spanner;
49
- this.database = database;
50
- this.entityManager = entityManager;
51
- this.pubSub = pubSub;
52
- this.users = users;
53
- this.request = request;
54
- this.pubSubRequest = pubSubRequest;
55
- this.entities = entities;
56
- this.firestoreDocuments = firestoreDocuments;
57
- }
58
- /**
59
- * Returns the temporary Firestore collection (as set up by the fixture) for a given document type.
60
- *
61
- * @param document The type of document to get the collection for.
62
- * @returns The Firestore collection.
63
- */
64
- firestoreCollection(document) {
65
- return getFirestoreCollectionFromModule(this.app, document);
66
- }
67
- /**
68
- * Runs a test on a versioned entity, checking that it has been mutated as expected.
69
- * Optionally, checks that an event has been published to the corresponding Pub/Sub topic.
70
- * Also, a serialized version of the entity (e.g. an HTTP response) can be checked against the expected entity.
71
- *
72
- * @param entity Describes the entity to fetch using the {@link SpannerEntityManager}.
73
- * @param tests The tests to run on the entity and its event.
74
- * @returns The entity fetched from the database.
75
- */
76
- async expectMutatedVersionedEntity(entity, tests) {
77
- const storedEntity = await this.entityManager.findOneByKeyOrFail(entity.type, entity.id, { includeSoftDeletes: entity.includeSoftDeletes });
78
- const expectedEntity = typeof tests.expectedEntity === 'function'
79
- ? tests.expectedEntity(storedEntity)
80
- : tests.expectedEntity;
81
- expect(storedEntity).toEqual(expectedEntity);
82
- const { expectedEvent } = tests;
83
- if (expectedEvent) {
84
- await this.pubSub.expectEventInTopic(expectedEvent.topic, {
85
- id: expect.any(String),
86
- name: expectedEvent.name,
87
- producedAt: storedEntity.updatedAt,
88
- data: storedEntity,
89
- }, { attributes: expectedEvent.attributes });
90
- }
91
- if (tests.matchesHttpResponse) {
92
- const expectedResponse = await serializeAsJavaScriptObject(storedEntity);
93
- expect(tests.matchesHttpResponse).toEqual(expectedResponse);
94
- }
95
- return storedEntity;
96
- }
97
- /**
98
- * Ensures the specified entity has not been mutated.
99
- * Optionally, checks that no event has been published to the corresponding Pub/Sub topic.
100
- * Also, a serialized version of the entity (e.g. an HTTP response) can be checked against the expected entity.
101
- *
102
- * @param entity Describes the entity to fetch using the {@link SpannerEntityManager}.
103
- * @param tests The tests to run on the entity.
104
- */
105
- async expectNonMutatedVersionedEntity(entity, tests) {
106
- const storedEntity = await this.entityManager.findOneByKeyOrFail(entity.type, entity.id, { includeSoftDeletes: entity.includeSoftDeletes });
107
- expect(storedEntity).toEqual(tests.expectedEntity);
108
- if (tests.expectNoEventInTopic) {
109
- await this.pubSub.expectNoMessageInTopic(tests.expectNoEventInTopic);
110
- }
111
- if (tests.matchesHttpResponse) {
112
- const expectedResponse = await serializeAsJavaScriptObject(storedEntity);
113
- expect(tests.matchesHttpResponse).toEqual(expectedResponse);
114
- }
115
- }
116
- /**
117
- * Clears all entities from the test database.
118
- *
119
- * The tables to clear should be specified in the `entities` property of the options passed to
120
- * {@link GoogleAppFixture.create}.
121
- */
122
- async clearSpanner() {
123
- await this.entityManager.transaction(async (transaction) => {
124
- for (const entity of this.entities) {
125
- await this.entityManager.clear(entity, { transaction });
126
- }
127
- });
128
- }
129
- /**
130
- * Clears all documents from the test Firestore collections.
131
- *
132
- * The collections to clear should be specified in the `firestoreDocuments` property of the options passed to
133
- * {@link GoogleAppFixture.create}.
134
- */
135
- async clearFirestore() {
136
- await Promise.all(this.firestoreDocuments.map((d) => clearFirestoreCollection(this.firestoreCollection(d))));
137
- }
138
- /**
139
- * Clears all entities from the test database and all documents from the test Firestore collections.
140
- * Also clears all messages read from the test Pub/Sub topics up to now.
141
- *
142
- * This does **not** delete Identity Platform users, which are only deleted when calling {
143
- * @link GoogleAppFixture.delete}.
144
- */
145
- async clear() {
146
- this.pubSub.clear();
147
- await Promise.all([this.clearSpanner(), this.clearFirestore()]);
148
- }
149
- /**
150
- * Deletes all resources created by the fixture.
151
- */
152
- async delete() {
153
- await this.app.close();
154
- await Promise.all([
155
- this.users.deleteAll(),
156
- this.pubSub.deleteAll(),
157
- this.database.delete(),
158
- ]);
159
- this.spanner.close();
160
- }
161
- /**
162
- * Creates a NestJS application using the specified module, and sets up the fixture.
163
- *
164
- * @param appModule The NestJS module to create the application from.
165
- * @param options Options when creating the GCP resources for the fixture.
166
- * @returns The {@link GoogleAppFixture}.
167
- */
168
- static async create(appModule, options = {}) {
169
- const entities = options.entities ?? [];
170
- const firestoreDocuments = options.firestoreDocuments ?? [];
171
- const disableAppCheck = options.disableAppCheck ?? true;
172
- const spanner = new Spanner();
173
- const database = await createDatabase({ spanner });
174
- const entityManager = new SpannerEntityManager(database);
175
- const pubSubFixture = new PubSubFixture();
176
- const overridePubSub = await pubSubFixture.createWithOverrider(options.pubSubTopics ?? {});
177
- const usersFixture = new AuthUsersFixture();
178
- const appFactoryOptions = options.appFactoryOptions ?? {};
179
- const additionalOverrides = appFactoryOptions.overrides && !Array.isArray(appFactoryOptions.overrides)
180
- ? [appFactoryOptions.overrides]
181
- : (appFactoryOptions.overrides ?? []);
182
- const app = await createApp(appModule, {
183
- nestApplicationOptions: options.nestApplicationOptions,
184
- appFactory: makeTestAppFactory({
185
- ...appFactoryOptions,
186
- overrides: [
187
- overrideDatabase(database),
188
- overridePubSub,
189
- overrideFirebaseApp,
190
- overrideFirestoreCollections(...firestoreDocuments),
191
- ...(disableAppCheck ? [overrideAppCheck] : []),
192
- ...additionalOverrides,
193
- ],
194
- }),
195
- });
196
- const request = supertest(app.getHttpServer());
197
- const pubSubRequest = makePubSubRequester(app);
198
- return new GoogleAppFixture(app, spanner, database, entityManager, pubSubFixture, usersFixture, request, pubSubRequest, entities, firestoreDocuments);
199
- }
200
- }
@@ -1 +0,0 @@
1
- export { GoogleAppFixture } from './google-app-fixture.js';
@@ -1 +0,0 @@
1
- export { GoogleAppFixture } from './google-app-fixture.js';
@@ -1,2 +0,0 @@
1
- export { SpannerPubSubTransactionModule } from './module.js';
2
- export { SpannerPubSubTransactionRunner } from './runner.js';
@@ -1,2 +0,0 @@
1
- export { SpannerPubSubTransactionModule } from './module.js';
2
- export { SpannerPubSubTransactionRunner } from './runner.js';
@@ -1,14 +0,0 @@
1
- import type { DynamicModule } from '@nestjs/common';
2
- /**
3
- * The module exposing the {@link SpannerPubSubTransactionRunner}.
4
- * This modules assumes that the `SpannerModule` and `PubSubPublisherModule` are available.
5
- */
6
- export declare class SpannerPubSubTransactionModule {
7
- /**
8
- * Create a global module that provides the {@link SpannerPubSubTransactionRunner}.
9
- * This modules assumes that the `SpannerModule` and `PubSubPublisherModule` are available.
10
- *
11
- * @returns The module.
12
- */
13
- static forRoot(): DynamicModule;
14
- }