Skip to content

Commit 75df024

Browse files
committed
add testing hooks for integration testing of persistent_cache_index_manager.ts
1 parent 12221dd commit 75df024

8 files changed

+596
-46
lines changed

packages/firestore/externs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"packages/util/dist/src/environment.d.ts",
3232
"packages/util/dist/src/compat.d.ts",
3333
"packages/util/dist/src/obj.d.ts",
34+
"packages/firestore/src/api/persistent_cache_index_manager.ts",
3435
"packages/firestore/src/protos/firestore_bundle_proto.ts",
3536
"packages/firestore/src/protos/firestore_proto_api.ts",
3637
"packages/firestore/src/util/error.ts",

packages/firestore/src/api/persistent_cache_index_manager.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
import {
1919
firestoreClientSetPersistentCacheIndexAutoCreationEnabled,
20-
FirestoreClient
20+
FirestoreClient,
21+
TestingHooks as FirestoreClientTestingHooks
2122
} from '../core/firestore_client';
2223
import { cast } from '../util/input_validation';
2324
import { logDebug, logWarn } from '../util/log';
25+
import { testingHooksSpi } from '../util/testing_hooks_spi';
2426

2527
import { ensureFirestoreConfigured, Firestore } from './database';
2628

@@ -124,6 +126,8 @@ function setPersistentCacheIndexAutoCreationEnabled(
124126
error
125127
)
126128
);
129+
130+
testingHooksSpi?.notifyPersistentCacheIndexAutoCreationToggle(promise);
127131
}
128132

129133
/**
@@ -138,3 +142,25 @@ const persistentCacheIndexManagerByFirestore = new WeakMap<
138142
Firestore,
139143
PersistentCacheIndexManager
140144
>();
145+
146+
/**
147+
* Test-only hooks into the SDK for use exclusively by tests.
148+
*/
149+
export class TestingHooks {
150+
private constructor() {
151+
throw new Error('creating instances is not supported');
152+
}
153+
154+
static setIndexAutoCreationSettings(
155+
indexManager: PersistentCacheIndexManager,
156+
settings: {
157+
indexAutoCreationMinCollectionSize?: number;
158+
relativeIndexReadCostPerDocument?: number;
159+
}
160+
): Promise<void> {
161+
return FirestoreClientTestingHooks.setPersistentCacheIndexAutoCreationSettings(
162+
indexManager._client,
163+
settings
164+
);
165+
}
166+
}

packages/firestore/src/core/firestore_client.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ import {
2323
CredentialsProvider
2424
} from '../api/credentials';
2525
import { User } from '../auth/user';
26+
import { IndexType } from '../local/index_manager';
2627
import { LocalStore } from '../local/local_store';
2728
import {
2829
localStoreConfigureFieldIndexes,
2930
localStoreExecuteQuery,
3031
localStoreGetNamedQuery,
3132
localStoreHandleUserChange,
3233
localStoreReadDocument,
33-
localStoreSetIndexAutoCreationEnabled
34+
localStoreSetIndexAutoCreationEnabled,
35+
TestingHooks as LocalStoreTestingHooks
3436
} from '../local/local_store_impl';
3537
import { Persistence } from '../local/persistence';
3638
import { Document } from '../model/document';
@@ -841,3 +843,39 @@ export function firestoreClientSetPersistentCacheIndexAutoCreationEnabled(
841843
);
842844
});
843845
}
846+
847+
/**
848+
* Test-only hooks into the SDK for use exclusively by tests.
849+
*/
850+
export class TestingHooks {
851+
private constructor() {
852+
throw new Error('creating instances is not supported');
853+
}
854+
855+
static getQueryIndexType(
856+
client: FirestoreClient,
857+
query: Query
858+
): Promise<IndexType> {
859+
return client.asyncQueue.enqueue(async () => {
860+
const localStore = await getLocalStore(client);
861+
return LocalStoreTestingHooks.getQueryIndexType(localStore, query);
862+
});
863+
}
864+
865+
static setPersistentCacheIndexAutoCreationSettings(
866+
client: FirestoreClient,
867+
settings: {
868+
indexAutoCreationMinCollectionSize?: number;
869+
relativeIndexReadCostPerDocument?: number;
870+
}
871+
): Promise<void> {
872+
const settingsCopy = { ...settings };
873+
return client.asyncQueue.enqueue(async () => {
874+
const localStore = await getLocalStore(client);
875+
LocalStoreTestingHooks.setIndexAutoCreationSettings(
876+
localStore,
877+
settingsCopy
878+
);
879+
});
880+
}
881+
}

packages/firestore/src/local/local_store_impl.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import { BATCHID_UNKNOWN } from '../util/types';
7070

7171
import { BundleCache } from './bundle_cache';
7272
import { DocumentOverlayCache } from './document_overlay_cache';
73-
import { IndexManager } from './index_manager';
73+
import { IndexManager, IndexType } from './index_manager';
7474
import { IndexedDbMutationQueue } from './indexeddb_mutation_queue';
7575
import { IndexedDbPersistence } from './indexeddb_persistence';
7676
import { IndexedDbTargetCache } from './indexeddb_target_cache';
@@ -1560,4 +1560,17 @@ export class TestingHooks {
15601560
settings.relativeIndexReadCostPerDocument;
15611561
}
15621562
}
1563+
1564+
static getQueryIndexType(
1565+
localStore: LocalStore,
1566+
query: Query
1567+
): Promise<IndexType> {
1568+
const localStoreImpl = debugCast(localStore, LocalStoreImpl);
1569+
const target = queryToTarget(query);
1570+
return localStoreImpl.persistence.runTransaction(
1571+
'local_store_impl TestingHooks getQueryIndexType',
1572+
'readonly',
1573+
txn => localStoreImpl.indexManager.getIndexType(txn, target)
1574+
);
1575+
}
15631576
}

packages/firestore/src/util/testing_hooks.ts

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,17 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { ensureFirestoreConfigured, Firestore } from '../api/database';
19+
import {
20+
PersistentCacheIndexManager,
21+
TestingHooks as PersistentCacheIndexManagerTestingHooks
22+
} from '../api/persistent_cache_index_manager';
1823
import { Unsubscribe } from '../api/reference_impl';
24+
import { TestingHooks as FirestoreClientTestingHooks } from '../core/firestore_client';
25+
import { Query } from '../lite-api/reference';
26+
import { IndexType } from '../local/index_manager';
1927

28+
import { cast } from './input_validation';
2029
import {
2130
setTestingHooksSpi,
2231
ExistenceFilterMismatchInfo,
@@ -54,6 +63,83 @@ export class TestingHooks {
5463
): Unsubscribe {
5564
return TestingHooksSpiImpl.instance.onExistenceFilterMismatch(callback);
5665
}
66+
67+
/**
68+
* Registers a callback to be notified when
69+
* `enablePersistentCacheIndexAutoCreation()` or
70+
* `disablePersistentCacheIndexAutoCreation()` is invoked.
71+
*
72+
* The relative order in which callbacks are notified is unspecified; do not
73+
* rely on any particular ordering. If a given callback is registered multiple
74+
* times then it will be notified multiple times, once per registration.
75+
*
76+
* @param callback the callback to invoke when
77+
* `enablePersistentCacheIndexAutoCreation()` or
78+
* `disablePersistentCacheIndexAutoCreation()` is invoked.
79+
*
80+
* @return a function that, when called, unregisters the given callback; only
81+
* the first invocation of the returned function does anything; all subsequent
82+
* invocations do nothing.
83+
*/
84+
static onPersistentCacheIndexAutoCreationToggle(
85+
callback: PersistentCacheIndexAutoCreationToggleCallback
86+
): Unsubscribe {
87+
return TestingHooksSpiImpl.instance.onPersistentCacheIndexAutoCreationToggle(
88+
callback
89+
);
90+
}
91+
92+
/**
93+
* Determines the type of client-side index that will be used when executing the
94+
* given query against the local cache.
95+
*
96+
* @param query The query whose client-side index type to get; it is typed as
97+
* `unknown` so that it is usable in the minified, bundled code, but it should
98+
* be a `Query` object.
99+
*/
100+
static async getQueryIndexType(
101+
query: unknown
102+
): Promise<'full' | 'partial' | 'none'> {
103+
const query_ = cast<Query>(query as Query, Query);
104+
const firestore = cast(query_.firestore, Firestore);
105+
const client = ensureFirestoreConfigured(firestore);
106+
107+
const indexType = await FirestoreClientTestingHooks.getQueryIndexType(
108+
client,
109+
query_._query
110+
);
111+
112+
switch (indexType) {
113+
case IndexType.NONE:
114+
return 'none';
115+
case IndexType.PARTIAL:
116+
return 'partial';
117+
case IndexType.FULL:
118+
return 'full';
119+
default:
120+
throw new Error(`unrecognized IndexType: ${indexType}`);
121+
}
122+
}
123+
124+
/**
125+
* Sets the persistent cache index auto-creation settings for the given
126+
* Firestore instance.
127+
*
128+
* @return a Promise that is fulfilled when the settings are successfully
129+
* applied, or rejected if applying the settings fails.
130+
*/
131+
static setPersistentCacheIndexAutoCreationSettings(
132+
indexManager: PersistentCacheIndexManager,
133+
settings: {
134+
indexAutoCreationMinCollectionSize?: number;
135+
relativeIndexReadCostPerDocument?: number;
136+
}
137+
): Promise<void> {
138+
return PersistentCacheIndexManagerTestingHooks.setIndexAutoCreationSettings(
139+
indexManager,
140+
settings
141+
);
142+
}
57143
}
58144

59145
/**
@@ -68,6 +154,23 @@ export type ExistenceFilterMismatchCallback = (
68154
info: ExistenceFilterMismatchInfo
69155
) => unknown;
70156

157+
/**
158+
* The signature of callbacks registered with
159+
* `TestingHooks.onPersistentCacheIndexAutoCreationToggle()`.
160+
*
161+
* The `promise` argument will be fulfilled when the asynchronous work started
162+
* by the call to `enablePersistentCacheIndexAutoCreation()` or
163+
* `disablePersistentCacheIndexAutoCreation()` completes successfully, or will
164+
* be rejected if it fails.
165+
*
166+
* The return value, if any, is ignored.
167+
*
168+
* @internal
169+
*/
170+
export type PersistentCacheIndexAutoCreationToggleCallback = (
171+
promise: Promise<void>
172+
) => unknown;
173+
71174
/**
72175
* The implementation of `TestingHooksSpi`.
73176
*/
@@ -77,6 +180,9 @@ class TestingHooksSpiImpl implements TestingHooksSpi {
77180
ExistenceFilterMismatchCallback
78181
>();
79182

183+
private readonly persistentCacheIndexAutoCreationToggleCallbacksById =
184+
new Map<Symbol, PersistentCacheIndexAutoCreationToggleCallback>();
185+
80186
private constructor() {}
81187

82188
static get instance(): TestingHooksSpiImpl {
@@ -96,11 +202,35 @@ class TestingHooksSpiImpl implements TestingHooksSpi {
96202
onExistenceFilterMismatch(
97203
callback: ExistenceFilterMismatchCallback
98204
): Unsubscribe {
99-
const id = Symbol();
100-
const callbacks = this.existenceFilterMismatchCallbacksById;
101-
callbacks.set(id, callback);
102-
return () => callbacks.delete(id);
205+
return registerCallback(
206+
callback,
207+
this.existenceFilterMismatchCallbacksById
208+
);
209+
}
210+
211+
notifyPersistentCacheIndexAutoCreationToggle(promise: Promise<void>): void {
212+
this.persistentCacheIndexAutoCreationToggleCallbacksById.forEach(callback =>
213+
callback(promise)
214+
);
103215
}
216+
217+
onPersistentCacheIndexAutoCreationToggle(
218+
callback: PersistentCacheIndexAutoCreationToggleCallback
219+
): Unsubscribe {
220+
return registerCallback(
221+
callback,
222+
this.persistentCacheIndexAutoCreationToggleCallbacksById
223+
);
224+
}
225+
}
226+
227+
function registerCallback<T>(
228+
callback: T,
229+
callbacks: Map<Symbol, T>
230+
): Unsubscribe {
231+
const id = Symbol();
232+
callbacks.set(id, callback);
233+
return () => callbacks.delete(id);
104234
}
105235

106236
let testingHooksSpiImplInstance: TestingHooksSpiImpl | null = null;

packages/firestore/src/util/testing_hooks_spi.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ export interface TestingHooksSpi {
5050
* `TestingHooks.onExistenceFilterMismatch()` with the given info.
5151
*/
5252
notifyOnExistenceFilterMismatch(info: ExistenceFilterMismatchInfo): void;
53+
54+
/**
55+
* Invokes all callbacks registered with
56+
* `TestingHooks.onPersistentCacheIndexAutoCreationToggle()` with the given
57+
* promise.
58+
*/
59+
notifyPersistentCacheIndexAutoCreationToggle(promise: Promise<void>): void;
5360
}
5461

5562
/**

0 commit comments

Comments
 (0)