Skip to content

Commit de643b1

Browse files
yuchenshiFeiyang1
authored andcommitted
Add new types and function stubs for RUT vNext. (#5316)
* Add new types and function stubs. * Fix types for testEnv.emulators. * Add util functions. * Add withFunctionTriggersDisabled overloads. * Improve typing for EmulatorConfig. * Fix tests. * Rename test_environment.ts to initialize.ts. * Add a dummy test to make CI pass.
1 parent 68eb7a0 commit de643b1

File tree

9 files changed

+857
-13
lines changed

9 files changed

+857
-13
lines changed

packages/rules-unit-testing/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@
2121
* creating a dependency on @firebase/rules-unit-testing.
2222
*/
2323

24-
export const TODO = 'TODO';
24+
export * from './src/public_types';
25+
export * from './src/initialize';
26+
export * from './src/util';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
const base = require('../../config/mocharc.node.js');
19+
20+
module.exports = {
21+
...base,
22+
require: [base.require, 'test/setup.ts']
23+
};

packages/rules-unit-testing/package.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
"build": "rollup -c",
1515
"build:deps": "lerna run --scope @firebase/rules-unit-testing --include-dependencies build",
1616
"dev": "rollup -c -w",
17-
"test:nyc": "TS_NODE_CACHE=NO TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'test/{,!(browser)/**/}*.test.ts' --config ../../config/mocharc.node.js",
18-
"test": "FIREBASE_CLI_PREVIEWS=storageemulator STORAGE_EMULATOR_HOST=http://localhost:9199 firebase --project=foo --debug emulators:exec 'yarn test:nyc'",
17+
"test:nyc": "TS_NODE_CACHE=NO TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'test/{,!(browser)/**/}*.test.ts' --config ./mocharc.node.js",
18+
"test": "firebase --project=demo-foo --debug emulators:exec 'yarn test:nyc'",
1919
"test:ci": "node ../../scripts/run_tests_in_ci.js -s test"
2020
},
2121
"license": "Apache-2.0",
2222
"devDependencies": {
23+
"firebase-admin": "9.11.1",
24+
"firebase-functions": "3.15.4",
2325
"rollup": "2.52.2",
2426
"rollup-plugin-typescript2": "0.30.0"
2527
},
@@ -34,5 +36,8 @@
3436
"typings": "dist/index.d.ts",
3537
"bugs": {
3638
"url": "https://github.com/firebase/firebase-js-sdk/issues"
39+
},
40+
"dependencies": {
41+
"node-fetch": "2.6.1"
3742
}
3843
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { RulesTestEnvironment, TestEnvironmentConfig } from './public_types';
19+
20+
/**
21+
* Initializes a test environment for rules unit testing. Call this function first for test setup.
22+
*
23+
* Requires emulators to be running. This function tries to discover those emulators via environment
24+
* variables or through the Firebase Emulator hub if hosts and ports are unspecified. It is strongly
25+
* recommended to specify security rules for emulators used for testing. See minimal example below.
26+
*
27+
* @param config the configuration for emulators. most fields are optional if they can be discovered
28+
* @returns a promise that resolves with an environment ready for testing, or rejects on error.
29+
* @public
30+
* @example
31+
* ```javascript
32+
* const testEnv = await initializeTestEnvironment({
33+
* firestore: {
34+
* rules: fs.readFileSync("/path/to/firestore.rules", "utf8"), // Load rules from file
35+
* // host and port can be omitted if they can be discovered from the hub.
36+
* },
37+
* // ...
38+
* });
39+
* ```
40+
*/
41+
export async function initializeTestEnvironment(): Promise<RulesTestEnvironment> {
42+
throw new Error('unimplemented');
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { FirebaseSignInProvider } from '@firebase/util';
19+
import { Firestore, FirestoreSettings } from '@firebase/firestore/exp';
20+
import { Database } from '@firebase/database/exp';
21+
import { FirebaseStorage } from '@firebase/storage/exp';
22+
23+
/**
24+
* More options for the mock user token to be used for testing, including developer-specfied custom
25+
* claims or optional overrides for Firebase Auth token payloads.
26+
* @public
27+
*/
28+
export type TokenOptions = {
29+
/** The token issue time, in seconds since epoch */
30+
iat?: number;
31+
32+
/** The token expiry time, normally 'iat' + 3600 */
33+
exp?: number;
34+
35+
/** The time the user authenticated, normally 'iat' */
36+
auth_time?: number;
37+
38+
/** The sign in provider, only set when the provider is 'anonymous' */
39+
provider_id?: 'anonymous';
40+
41+
/** The user's primary email */
42+
email?: string;
43+
44+
/** The user's email verification status */
45+
email_verified?: boolean;
46+
47+
/** The user's primary phone number */
48+
phone_number?: string;
49+
50+
/** The user's display name */
51+
name?: string;
52+
53+
/** The user's profile photo URL */
54+
picture?: string;
55+
56+
/** Information on all identities linked to this user */
57+
firebase?: {
58+
/** The primary sign-in provider */
59+
sign_in_provider: FirebaseSignInProvider;
60+
61+
/** A map of providers to the user's list of unique identifiers from each provider */
62+
identities?: { [provider in FirebaseSignInProvider]?: string[] };
63+
};
64+
65+
/** Set to PROJECT_ID by default. In rare cases, you may want to specify an override. */
66+
aud?: string;
67+
68+
/** Set to https://securetoken.google.com/PROJECT_ID by default. In rare cases, you may want to specify an override. */
69+
iss?: string;
70+
71+
/** Custom claims set by the developer */
72+
[claim: string]: unknown;
73+
74+
// The fields below needs to be explicitly excluded from the index signature above.
75+
76+
/** DO NOT USE. The user ID MUST be specified as the first param in authenticatedContext(uid, options) instead. */
77+
uid?: never;
78+
/** DO NOT USE. The user ID MUST be specified as the first param in authenticatedContext(uid, options) instead. */
79+
sub?: never;
80+
/** DO NOT USE. The user ID MUST be specified as the first param in authenticatedContext(uid, options) instead. */
81+
user_id?: never;
82+
};
83+
84+
/**
85+
* Configuration of the unit testing environment, including emulators.
86+
* @public
87+
*/
88+
export interface TestEnvironmentConfig {
89+
/**
90+
* The project ID of the test environment. Can also be specified via the environment variable GCLOUD_PROJECT.
91+
*
92+
* A "demo-*" project ID is strongly recommended, especially for unit testing.
93+
* See: https://firebase.google.com/docs/emulator-suite/connect_firestore#choose_a_firebase_project
94+
*/
95+
projectId?: string;
96+
97+
/**
98+
* The Firebase Emulator hub. Can also be specified via the environment variable FIREBASE_EMULATOR_HUB.
99+
* If specified either way, other running emulators can be automatically discovered, and thus do
100+
* not to be explicity specified.
101+
*/
102+
hub?: HostAndPort;
103+
104+
/**
105+
* The Database emulator. Its host and port can also be discovered automatically through the hub
106+
* (see field "hub") or specified via the environment variable FIREBASE_DATABASE_EMULATOR_HOST.
107+
*/
108+
database?: EmulatorConfig;
109+
110+
/**
111+
* The Firestore emulator. Its host and port can also be discovered automatically through the hub
112+
* (see field "hub") or specified via the environment variable FIRESTORE_EMULATOR_HOST.
113+
*/
114+
firestore?: EmulatorConfig;
115+
116+
/**
117+
* The Storage emulator. Its host and port can also be discovered automatically through the hub
118+
* (see field "hub") or specified via the environment variable FIREBASE_STORAGE_EMULATOR_HOST.
119+
*/
120+
storage?: EmulatorConfig;
121+
}
122+
123+
/**
124+
* An object containing the hostname and port number of an emulator.
125+
* @public
126+
*/
127+
export interface HostAndPort {
128+
/**
129+
* The host of the emulator. Can be omitted if discovered automatically through the hub or
130+
* specified via environment variables. See {@code TestEnvironmentConfig} for details.
131+
*/
132+
host: string;
133+
134+
/**
135+
* The port of the emulator. Can be omitted if discovered automatically through the hub or
136+
* specified via environment variables. See {@code TestEnvironmentConfig} for details.
137+
*/
138+
port: number;
139+
}
140+
141+
/**
142+
* Configuration for a given emulator.
143+
* @public
144+
*/
145+
export type EmulatorConfig = {
146+
/** The security rules source code under test for this emulator. Strongly recommended. */
147+
rules?: string;
148+
} & (HostAndPort | {}); // Both or none of host and port should be specified.
149+
150+
/**
151+
* An object used to control the rules unit test environment. Can be used to create RulesTestContext
152+
* for different authentication situations.
153+
* @public
154+
*/
155+
export interface RulesTestEnvironment {
156+
/** The project ID specified or discovered at test environment creation. */
157+
readonly projectId: string;
158+
159+
/**
160+
* A readonly copy of the emulator config specified or discovered at test environment creation.
161+
*/
162+
readonly emulators: {
163+
database?: HostAndPort;
164+
firestore?: HostAndPort;
165+
storage?: HostAndPort;
166+
};
167+
168+
/**
169+
* Create a {@code RulesTestContext} which behaves like an authenticated Firebase Auth user.
170+
*
171+
* Requests created via the returned context will have a mock Firebase Auth token attached.
172+
*
173+
* @param user_id the User ID of the user. Specifies the value of "user_id" and "sub" on the token
174+
* @param tokenOptions custom claims or overrides for Firebase Auth token payloads
175+
*
176+
* @example
177+
* ```javascript
178+
* const alice = testEnv.authenticatedContext('alice');
179+
* await assertSucceeds(get(doc(alice.firestore(), '/doc/readable/by/alice'), { ... });
180+
* ```
181+
*/
182+
authenticatedContext(
183+
user_id: string,
184+
tokenOptions?: TokenOptions
185+
): RulesTestContext;
186+
187+
/**
188+
* Create a {@code RulesTestContext} which behaves like client that is NOT logged in via Firebase
189+
* Auth.
190+
*
191+
* Requests created via the returned context will not have Firebase Auth tokens attached.
192+
*
193+
* @example
194+
* ```javascript
195+
* const unauthed = testEnv.unauthenticatedContext();
196+
* await assertFails(get(doc(unauthed.firestore(), '/private/doc'), { ... });
197+
* ```
198+
*/
199+
unauthenticatedContext(): RulesTestContext;
200+
201+
/*
202+
* Run a setup function with a context that behaves as if Security Rules were disabled.
203+
*
204+
* This can be used for test setup by importing data into emulators without blocked by Rules.
205+
* ONLY requests issued through the context passed into the callback will bypass Security Rules.
206+
* Requests issued through other contexts will go through Security Rules as normal.
207+
*
208+
* @param callback a function which takes the Security-Rules-bypassing context and returns a promise.
209+
* The context will be destroyed once the promise resolves / rejects.
210+
*/
211+
withSecurityRulesDisabled(
212+
callback: (context: RulesTestContext) => Promise<void>
213+
): Promise<void>;
214+
215+
/**
216+
* Clear all data in the Realtime Database emulator namespace.
217+
*/
218+
clearDatabase(): Promise<void>;
219+
220+
/**
221+
* Clear data in the Firestore that belongs to the {@code projectId} in the Firestore emulator.
222+
*/
223+
clearFirestore(): Promise<void>;
224+
225+
/**
226+
* Clear Storage files and metadata in all buckets in the Storage emulator.
227+
*/
228+
clearStorage(): Promise<void>;
229+
230+
/**
231+
* At the very end of your test code, call the cleanup function. Destroy all RulesTestContexts
232+
* created in test environment and clean up the underlying resources, allowing a clean exit.
233+
*
234+
* This method does not change the state in emulators in any way. To reset data between tests,
235+
* see {@code clearDatabase()}, {@code clearFirestore()} and {@code clearStorage()}.
236+
*/
237+
cleanup(): Promise<void>;
238+
}
239+
240+
/**
241+
* A test context that represents a client. Can be used to access emulators for rules unit testing.
242+
* @public
243+
*/
244+
export interface RulesTestContext {
245+
/**
246+
* Get a Firestore instance for this test context. The returned Firebase JS Client SDK instance
247+
* can be used with the client SDK APIs (v9 modular or v9 compat).
248+
*
249+
* See: https://firebase.google.com/docs/reference/js/v9/firestore_
250+
* @param settings a settings object to configure the {@code Firestore} instance
251+
* @returns a Firestore instance configured to connect to the emulator
252+
* @public
253+
*/
254+
firestore(settings?: FirestoreSettings): Firestore;
255+
256+
/**
257+
* Get a Firestore instance for this test context. The returned Firebase JS Client SDK instance
258+
* can be used with the client SDK APIs (v9 modular or v9 compat).
259+
*
260+
* See: https://firebase.google.com/docs/reference/js/v9/firestore_
261+
* @param databaseURL the URL of the Realtime Database instance. If specified, returns an instance
262+
* for an emulated version of the namespace with parameters extracted from URL
263+
* @returns a Database instance configured to connect to the emulator. It never connects to
264+
* production even if a production databaseURL is specified
265+
*/
266+
database(databaseURL?: string): Database;
267+
268+
/**
269+
* Get a Storage instance for this test context. The returned Firebase JS Client SDK instance
270+
* can be used with the client SDK APIs (v9 modular or v9 compat).
271+
*
272+
* See: https://firebase.google.com/docs/reference/js/v9/firestore_
273+
* @param settings the gs:// url to the Firebase Storage Bucket for testing. If specified,
274+
* returns a Storage instance for an emulated version of the bucket name
275+
* @returns a Storage instance configured to connect to the emulator
276+
*/
277+
storage(bucketUrl?: string): FirebaseStorage;
278+
}

0 commit comments

Comments
 (0)