Skip to content

Commit 96dcbbc

Browse files
committed
feat: allow mocking FirebaseFunctionsRateLimiter
1 parent 3aa04ed commit 96dcbbc

5 files changed

+40
-16
lines changed

src/FirebaseFunctionsRateLimiter.integration.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ afterEach(async () => {
3434

3535
//
3636
describe("FirebaseFunctionsRateLimiter", () => {
37-
const backends: Array<"firestore" | "realtimedb"> = ["firestore", "realtimedb"];
38-
backends.forEach((backend: "firestore" | "realtimedb") =>
37+
const backends: Array<"firestore" | "realtimedb" | "mock"> = ["firestore", "realtimedb", "mock"];
38+
backends.forEach((backend: "firestore" | "realtimedb" | "mock") =>
3939
describe("Backend " + backend, () => {
4040
describe("isQuotaExceeded", () => {
4141
it("Uses qualifier to identify document in the collection", async () => {
42-
const { rateLimiter, uniqueCollectionName, qualifier, getDocument } = mock("firestore", {});
42+
const { rateLimiter, uniqueCollectionName, qualifier, getDocument } = mock(backend, {});
4343
await rateLimiter.isQuotaExceeded(qualifier);
4444

4545
const doc = await getDocument(uniqueCollectionName, qualifier);
@@ -51,7 +51,7 @@ describe("FirebaseFunctionsRateLimiter", () => {
5151
});
5252

5353
it("Increments counter when limit is not exceeded", async () => {
54-
const { rateLimiter, getDocument, uniqueCollectionName, qualifier } = mock("firestore", {
54+
const { rateLimiter, getDocument, uniqueCollectionName, qualifier } = mock(backend, {
5555
maxCalls: 10,
5656
});
5757

src/FirebaseFunctionsRateLimiter.mock.integration.test.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,42 @@ import * as uuid from "uuid/v4";
66

77
import { FirebaseFunctionsRateLimiter } from "./FirebaseFunctionsRateLimiter";
88
import { FirebaseFunctionsRateLimiterConfiguration } from "./FirebaseFunctionsRateLimiterConfiguration";
9+
import { PersistenceProviderMock } from "./persistence/PersistenceProviderMock";
910

10-
export function mock(backend: "firestore" | "realtimedb", configApply: FirebaseFunctionsRateLimiterConfiguration) {
11+
export function mock(
12+
backend: "firestore" | "realtimedb" | "mock",
13+
configApply: FirebaseFunctionsRateLimiterConfiguration,
14+
) {
1115
const app = firebase.initializeTestApp({ projectId: "unit-testing-" + Date.now(), databaseName: "db" });
1216
const uniqueCollectionName = uuid();
1317
const uniqueDocName = uuid();
1418
const qualifier = uuid();
1519
const firestore = app.firestore();
1620
const database = app.database();
21+
const persistenceProviderMock = new PersistenceProviderMock();
1722
async function getDocument(collection: string, doc: string): Promise<any> {
1823
if (backend === "firestore") {
1924
return (await firestore
2025
.collection(collection)
2126
.doc(doc)
2227
.get()).data();
23-
} else return (await database.ref(`${collection}/${doc}`).once("value")).val();
28+
} else if (backend === "realtimedb") {
29+
return (await database.ref(`${collection}/${doc}`).once("value")).val();
30+
} else if (backend === "mock") {
31+
return persistenceProviderMock.getRecord(collection, doc);
32+
} else throw new Error("Unknown backend " + backend);
2433
}
2534
const config: FirebaseFunctionsRateLimiterConfiguration = {
2635
name: uniqueCollectionName,
2736
debug: false,
2837
...configApply,
2938
};
30-
const rateLimiter =
31-
backend === "firestore"
32-
? FirebaseFunctionsRateLimiter.withFirestoreBackend(config, firestore)
33-
: FirebaseFunctionsRateLimiter.withRealtimeDbBackend(config, database);
39+
let rateLimiter: FirebaseFunctionsRateLimiter;
40+
if (backend === "firestore") rateLimiter = FirebaseFunctionsRateLimiter.withFirestoreBackend(config, firestore);
41+
else if (backend === "realtimedb") {
42+
rateLimiter = FirebaseFunctionsRateLimiter.withRealtimeDbBackend(config, database);
43+
} else if (backend === "mock") rateLimiter = FirebaseFunctionsRateLimiter.mock(config, persistenceProviderMock);
44+
else throw new Error("Unknown backend " + backend);
3445
return {
3546
app,
3647
firestore,

src/FirebaseFunctionsRateLimiter.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { FirebaseFunctionsRateLimiterConfiguration } from "./FirebaseFunctionsRa
77
import { GenericRateLimiter } from "./GenericRateLimiter";
88
import { FirestorePersistenceProvider } from "./persistence/FirestorePersistenceProvider";
99
import { PersistenceProvider } from "./persistence/PersistenceProvider";
10+
import { PersistenceProviderMock } from "./persistence/PersistenceProviderMock";
1011
import { RealtimeDbPersistenceProvider } from "./persistence/RealtimeDbPersistenceProvider";
1112
import { FirebaseTimestampProvider } from "./timestamp/FirebaseTimestampProvider";
1213
import { FirestoreEquivalent } from "./types/FirestoreEquivalent";
@@ -29,6 +30,18 @@ export class FirebaseFunctionsRateLimiter {
2930
return new FirebaseFunctionsRateLimiter(configuration, provider);
3031
}
3132

33+
public static mock(
34+
configuration?: FirebaseFunctionsRateLimiterConfiguration,
35+
persistenceProviderMock?: PersistenceProviderMock,
36+
): FirebaseFunctionsRateLimiter {
37+
const defaultConfig: FirebaseFunctionsRateLimiterConfiguration = {
38+
periodSeconds: 10,
39+
maxCalls: Number.MAX_SAFE_INTEGER,
40+
};
41+
const provider = persistenceProviderMock || new PersistenceProviderMock();
42+
return new FirebaseFunctionsRateLimiter(configuration || defaultConfig, provider);
43+
}
44+
3245
private configurationFull: FirebaseFunctionsRateLimiterConfiguration.ConfigurationFull;
3346
private genericRateLimiter: GenericRateLimiter;
3447
private debugFn: (msg: string) => void;

src/GenericRateLimiter.spec.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { SinonSpy, spy } from "sinon";
77

88
import { FirebaseFunctionsRateLimiterConfiguration } from "./FirebaseFunctionsRateLimiterConfiguration";
99
import { GenericRateLimiter } from "./GenericRateLimiter";
10-
import { PersistenceProviderMock } from "./persistence/PersistenceProviderMock.test";
10+
import { PersistenceProviderMock } from "./persistence/PersistenceProviderMock";
1111
import { TimestampProviderMock } from "./timestamp/TimestampProviderMock.test";
1212

1313
chaiUse(chaiAsPromised);

src/persistence/PersistenceProviderMock.test.ts renamed to src/persistence/PersistenceProviderMock.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ export class PersistenceProviderMock implements PersistenceProvider {
2626
//
2727
}
2828

29-
private async runTransaction(asyncTransactionFn: () => Promise<void>): Promise<void> {
30-
await asyncTransactionFn();
31-
}
32-
33-
private async getRecord(collectionName: string, recordName: string): Promise<PersistenceRecord> {
29+
public async getRecord(collectionName: string, recordName: string): Promise<PersistenceRecord> {
3430
await BluebirdPromise.delay(2);
3531
const key = this.getKey(collectionName, recordName);
3632
return this.persistenceObject[key] || this.createEmptyRecord();
3733
}
3834

35+
private async runTransaction(asyncTransactionFn: () => Promise<void>): Promise<void> {
36+
await asyncTransactionFn();
37+
}
38+
3939
private async saveRecord(collectionName: string, recordName: string, record: PersistenceRecord): Promise<void> {
4040
await BluebirdPromise.delay(2);
4141
const key = this.getKey(collectionName, recordName);

0 commit comments

Comments
 (0)