I am in the process of writing tests for Firestore rules, focusing on testing rules that control when actions can be executed based on a timestamp stored in the document.
It is important to note that the rules will utilize rules.Timestamp rather than JavaScript Date objects. Therefore, I need to generate Timestamp
objects within my test data.
However, upon importing @google-cloud/firestore
to access the Timestamp
data type, I encounter a severe error during test compilation:
functions/node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: DocumentData, UpdateData, Firestore, GeoPoint, Transaction, WriteBatch, WriteResult, DocumentReference, DocumentSnapshot, QueryDocumentSnapshot, OrderByDirection, WhereFilterOp, Query, QuerySnapshot, DocumentChangeType, CollectionReference, FieldValue, FieldPath, Timestamp, v1beta1, v1, OK, CANCELLED, UNKNOWN, INVALID_ARGUMENT, DEADLINE_EXCEEDED, NOT_FOUND, ALREADY_EXISTS, PERMISSION_DENIED, RESOURCE_EXHAUSTED, FAILED_PRECONDITION, ABORTED, OUT_OF_RANGE, UNIMPLEMENTED, INTERNAL, UNAVAILABLE, DATA_LOSS, UNAUTHENTICATED, FirebaseFirestore
23 declare namespace FirebaseFirestore {
~~~~~~~
node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1
23 declare namespace FirebaseFirestore {
~~~~~~~
Conflicts are in this file.
node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1 - error TS6200: Definitions of the following identifiers conflict with those in another file: DocumentData, UpdateData, Firestore, GeoPoint, Transaction, WriteBatch, WriteResult, DocumentReference, DocumentSnapshot, QueryDocumentSnapshot, OrderByDirection, WhereFilterOp, Query, QuerySnapshot, DocumentChangeType, CollectionReference, FieldValue, FieldPath, Timestamp, v1beta1, v1, OK, CANCELLED, UNKNOWN, INVALID_ARGUMENT, DEADLINE_EXCEEDED, NOT_FOUND, ALREADY_EXISTS, PERMISSION_DENIED, RESOURCE_EXHAUSTED, FAILED_PRECONDITION, ABORTED, OUT_OF_RANGE, UNIMPLEMENTED, INTERNAL, UNAVAILABLE, DATA_LOSS, UNAUTHENTICATED, FirebaseFirestore
23 declare namespace FirebaseFirestore {
~~~~~~~
functions/node_modules/@google-cloud/firestore/types/firestore.d.ts:23:1
23 declare namespace FirebaseFirestore {
~~~~~~~
Conflicts are in this file.
node_modules/@google-cloud/firestore/types/firestore.d.ts:147:5 - error TS2374: Duplicate string index signature.
147 [key: string]: any; // Accept other properties, such as GRPC settings.
~~~~~~~~~~~~~~~~~~~
Below is a complete test file highlighting this issue:
import * as firebase from '@firebase/testing';
import * as fs from "fs";
import {suite, test} from "mocha-typescript";
import * as chai from 'chai';
import {Timestamp} from "@google-cloud/firestore";
const projectId = 'stackoverflow-timestamp';
const coverageUrl = `http://localhost:8080/emulator/v1/projects/${projectId}:ruleCoverage.html`;
const rules = fs.readFileSync('firestore.rules', 'utf8');
const nowMillis = Date.now();
const yesterday = Timestamp.fromMillis(nowMillis - 86400000);
const tomorrow = Timestamp.fromMillis(nowMillis + 86400000);
const test_data = {
'timesamples/ts1': {
'createTime': yesterday,
'closeTime': tomorrow
}
};
/**
* Sets up a new app with authentication data based on the input.
*
* @param {object} auth the authentication object to use (usually {uid: some-uid})
* @return {object} the initialized app
*/
function authedApp(auth) {
return firebase.initializeTestApp({'projectId': projectId, 'auth': auth}).firestore();
}
/*
* ============
* Test Cases
* ============
*/
before(async () => {
await firebase.loadFirestoreRules({projectId, rules});
});
beforeEach(async () => {
// Clear the database between each test
await firebase.clearFirestoreData({projectId});
const db = firebase.initializeAdminApp({'projectId': projectId}).firestore();
// Load the test data
for (const key in test_data) {
const ref = db.doc(key);
await ref.set(test_data[key]);
}
});
after(async () => {
await Promise.all(firebase.apps().map(app => app.delete()));
console.log(`Visit rule coverage information at ${coverageUrl}\n`);
});
@suite
class TimestampRuleChecks {
@test
async 'modify when close is in future'() {
const unauthenticated = authedApp(null);
const record = unauthenticated.collection('timesamples').doc('ts1');
await firebase.assertSucceeds(
record.set({'data': 'modifiedData'}, {merge: true})
.catch(
(reason => {
chai.assert.fail("Problem in Firebase: " + reason);
}
)
)
);
}
}