@luvio/adapter-test-library 0.104.0 → 0.106.0

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.
@@ -0,0 +1 @@
1
+ module.exports = require('../../../babel.config');
@@ -1,14 +1,16 @@
1
- import type { DurableStore, DurableStoreEntries, OnDurableStoreChangedListener, DurableStoreOperation, DurableStoreChange } from '@luvio/environments';
1
+ import type { DurableStore, DurableStoreEntries, OnDurableStoreChangedListener, DurableStoreOperation } from '@luvio/environments';
2
2
  import type { DurableStorePersistence } from './durableStorePersistence';
3
3
  export declare class MockDurableStore implements DurableStore {
4
+ private writePromises;
4
5
  listeners: Set<OnDurableStoreChangedListener>;
5
6
  persistence: DurableStorePersistence;
6
7
  constructor(persistence?: DurableStorePersistence);
7
8
  getEntries<T>(entryIds: string[], segment: string): Promise<DurableStoreEntries<T> | undefined>;
8
- getAllEntries<T>(segment: string): Promise<DurableStoreEntries<T>>;
9
+ getAllEntries<T>(segment: string): Promise<DurableStoreEntries<T> | undefined>;
9
10
  setEntries<T>(entries: DurableStoreEntries<T>, segment: string): Promise<void>;
10
11
  evictEntries(ids: string[], segment: string): Promise<void>;
11
12
  registerOnChangedListener(listener: OnDurableStoreChangedListener): () => Promise<void>;
12
13
  batchOperations<T>(operations: DurableStoreOperation<T>[]): Promise<void>;
13
- performOperation<T>(operation: DurableStoreOperation<T>): Promise<DurableStoreChange>;
14
+ private performOperation;
15
+ flushPendingOperations(): Promise<void>;
14
16
  }
@@ -2,10 +2,12 @@ export interface DurableStorePersistence {
2
2
  get<T>(key: string): Promise<T | undefined>;
3
3
  set<T>(key: string, value: T): Promise<void>;
4
4
  delete(key: string): Promise<void>;
5
+ flushPendingWork(): Promise<void>;
5
6
  }
6
7
  export declare class MemoryDurableStorePersistence implements DurableStorePersistence {
7
8
  private store;
8
9
  get<T>(key: string): Promise<T | undefined>;
9
10
  set<T>(key: string, value: T): Promise<void>;
10
11
  delete(key: string): Promise<void>;
12
+ flushPendingWork(): Promise<void>;
11
13
  }
@@ -1,6 +1,6 @@
1
- export { MockPayload, ConnectivityState, buildMockNetworkAdapter, resetMockNetworkAdapter, getMockNetworkAdapterCallCount, buildSuccessMockPayload, buildErrorMockPayload, setMockNetworkPayloads, setNetworkConnectivity, buildFetchResponse, overrideMockNetworkResponses, } from './network';
1
+ export { MockPayload, ConnectivityState, buildMockNetworkAdapter, resetMockNetworkAdapter, getMockNetworkAdapterCallCount, buildSuccessMockPayload, buildErrorMockPayload, setMockNetworkPayloads, setNetworkConnectivity, buildFetchResponse, overrideMockNetworkResponses, flushPendingNetworkRequests, } from './network';
2
2
  export { verifyImmutable, isImmutable } from './verification';
3
3
  export { getMockLuvioWithFulfilledSnapshot, getMockFulfilledSnapshot } from './mocks';
4
4
  export { stripProperties } from './utils';
5
- export { MockDurableStore } from './durableStore';
5
+ export { MockDurableStore } from './MockDurableStore';
6
6
  export { MemoryDurableStorePersistence, DurableStorePersistence } from './durableStorePersistence';
@@ -7,6 +7,13 @@ export interface MockPayload {
7
7
  networkArgs: Partial<ResourceRequest>;
8
8
  response: FetchResponse<any>;
9
9
  }
10
+ /**
11
+ * Flushes any pending network requests. Useful for tests that need to ensure all
12
+ * un-awaited background refreshes are complete
13
+ *
14
+ * @param _mockNetworkAdapter {NetworkAdapter} The network adapter instance to flush
15
+ */
16
+ export declare function flushPendingNetworkRequests(_mockNetworkAdapter: NetworkAdapter): Promise<void>;
10
17
  export declare function buildMockNetworkAdapter(mockPayloads: MockPayload[]): NetworkAdapter;
11
18
  export declare function setNetworkConnectivity(mockNetworkAdapter: NetworkAdapter, connectivityState: ConnectivityState): void;
12
19
  export declare function setMockNetworkPayloads(mockNetworkAdapter: NetworkAdapter, mockPayloads: MockPayload[]): void;
@@ -1,5 +1,4 @@
1
1
  import sinon from 'sinon';
2
- import { DurableStoreOperationType } from '@luvio/environments';
3
2
 
4
3
  /**
5
4
  * Clone an object
@@ -49,6 +48,9 @@ function doesThrow(predicate) {
49
48
  return true;
50
49
  }
51
50
  return false;
51
+ }
52
+ function flushPromises() {
53
+ return new Promise((resolve) => setTimeout(resolve, 0));
52
54
  }
53
55
 
54
56
  const networkConnectivityStateMap = new WeakMap();
@@ -94,6 +96,18 @@ function sortPayloads(a, b) {
94
96
  function isOkResponse(status) {
95
97
  return status >= 200 && status <= 299;
96
98
  }
99
+ /**
100
+ * Flushes any pending network requests. Useful for tests that need to ensure all
101
+ * un-awaited background refreshes are complete
102
+ *
103
+ * @param _mockNetworkAdapter {NetworkAdapter} The network adapter instance to flush
104
+ */
105
+ async function flushPendingNetworkRequests(_mockNetworkAdapter) {
106
+ // since the network mock is actually synchronous (just returns things wrapped
107
+ // in Promise.resolve/reject) the only thing necessary to flush any pending
108
+ // network activity is to flush the pending microtask queue
109
+ await flushPromises();
110
+ }
97
111
  function buildMockNetworkAdapter(mockPayloads) {
98
112
  // any endpoints not setup with a fake will return a rejected promise
99
113
  const networkAdapter = sinon.stub().rejects(buildMockSetupError());
@@ -281,53 +295,66 @@ class MemoryDurableStorePersistence {
281
295
  return this.store[key];
282
296
  }
283
297
  async set(key, value) {
298
+ // simulate a more realistic durable store by making the write wait a
299
+ // tick before actually setting the value
300
+ await flushPromises();
284
301
  this.store[key] = value;
285
302
  }
286
303
  async delete(key) {
287
304
  delete this.store[key];
288
305
  }
306
+ async flushPendingWork() {
307
+ // since this implementation does actual "IO" synchronously the only
308
+ // thing necessary to await any pending IO is to flush the current
309
+ // microtask queue
310
+ await flushPromises();
311
+ }
289
312
  }
290
313
 
314
+ function waitForPromiseSet(set) {
315
+ // NOTE: we are building an array from the Set at this point in time. If
316
+ // more Promises are added to the Set while this is awaiting it won't
317
+ // await the newly-added Promise. That's what we want.
318
+ return Promise.all(Array.from(set)).then();
319
+ }
291
320
  class MockDurableStore {
292
321
  constructor(persistence) {
293
- // NOTE: This mock class doesn't enforce read/write synchronization
322
+ // for read/write synchronization
323
+ this.writePromises = new Set();
294
324
  this.listeners = new Set();
295
325
  this.persistence = persistence || new MemoryDurableStorePersistence();
296
326
  }
297
- getEntries(entryIds, segment) {
327
+ async getEntries(entryIds, segment) {
328
+ // await any current write operations
329
+ if (this.writePromises.size > 0) {
330
+ await waitForPromiseSet(this.writePromises);
331
+ }
332
+ const entries = await this.persistence.get(segment);
333
+ if (entries === undefined) {
334
+ return undefined;
335
+ }
298
336
  const returnSource = Object.create(null);
299
- return this.persistence.get(segment).then((entries) => {
300
- if (entries === undefined) {
301
- return undefined;
337
+ for (const entryId of entryIds) {
338
+ const entry = entries[entryId];
339
+ if (entry !== undefined) {
340
+ returnSource[entryId] = clone(entry);
302
341
  }
303
- for (const entryId of entryIds) {
304
- const entry = entries[entryId];
305
- if (entry !== undefined) {
306
- returnSource[entryId] = clone(entry);
307
- }
308
- }
309
- return returnSource;
310
- });
342
+ }
343
+ return returnSource;
311
344
  }
312
- getAllEntries(segment) {
313
- const returnSource = Object.create(null);
314
- return this.persistence.get(segment).then((rawEntries) => {
315
- const entries = rawEntries === undefined ? {} : rawEntries;
316
- for (const key of Object.keys(entries)) {
317
- returnSource[key] = clone(entries[key]);
318
- }
319
- return returnSource;
320
- });
345
+ async getAllEntries(segment) {
346
+ // await any current write operations
347
+ if (this.writePromises.size > 0) {
348
+ await waitForPromiseSet(this.writePromises);
349
+ }
350
+ const entries = await this.persistence.get(segment);
351
+ return entries;
321
352
  }
322
353
  setEntries(entries, segment) {
323
- return this.batchOperations([
324
- { entries, segment, type: DurableStoreOperationType.SetEntries },
325
- ]);
354
+ return this.batchOperations([{ entries, segment, type: 'setEntries' }]);
326
355
  }
327
356
  evictEntries(ids, segment) {
328
- return this.batchOperations([
329
- { ids, segment, type: DurableStoreOperationType.EvictEntries },
330
- ]);
357
+ return this.batchOperations([{ ids, segment, type: 'evictEntries' }]);
331
358
  }
332
359
  registerOnChangedListener(listener) {
333
360
  this.listeners.add(listener);
@@ -337,9 +364,22 @@ class MockDurableStore {
337
364
  };
338
365
  }
339
366
  async batchOperations(operations) {
367
+ // await any current write operations
368
+ if (this.writePromises.size > 0) {
369
+ await waitForPromiseSet(this.writePromises);
370
+ }
340
371
  const changes = [];
341
- for (let i = 0; i < operations.length; i++) {
342
- changes.push(await this.performOperation(operations[i]));
372
+ const writePromise = (async () => {
373
+ for (const operation of operations) {
374
+ changes.push(await this.performOperation(operation));
375
+ }
376
+ })();
377
+ this.writePromises.add(writePromise);
378
+ try {
379
+ await writePromise;
380
+ }
381
+ finally {
382
+ this.writePromises.delete(writePromise);
343
383
  }
344
384
  this.listeners.forEach((listener) => {
345
385
  listener(changes);
@@ -351,13 +391,13 @@ class MockDurableStore {
351
391
  const entries = rawEntries === undefined ? {} : rawEntries;
352
392
  let ids = [];
353
393
  switch (operation.type) {
354
- case DurableStoreOperationType.SetEntries:
394
+ case 'setEntries':
355
395
  ids = Object.keys(operation.entries);
356
396
  ids.forEach((id) => {
357
397
  entries[id] = clone(operation.entries[id]);
358
398
  });
359
399
  break;
360
- case DurableStoreOperationType.EvictEntries:
400
+ case 'evictEntries':
361
401
  ids = operation.ids;
362
402
  ids.forEach((id) => {
363
403
  delete entries[id];
@@ -366,6 +406,14 @@ class MockDurableStore {
366
406
  await this.persistence.set(operation.segment, entries);
367
407
  return { ids, segment, type: operation.type };
368
408
  }
409
+ async flushPendingOperations() {
410
+ // flush any pending read operations
411
+ await this.persistence.flushPendingWork();
412
+ // wait for any pending writes to finish
413
+ while (this.writePromises.size > 0) {
414
+ await waitForPromiseSet(this.writePromises);
415
+ }
416
+ }
369
417
  }
370
418
 
371
- export { ConnectivityState, MemoryDurableStorePersistence, MockDurableStore, buildErrorMockPayload, buildFetchResponse, buildMockNetworkAdapter, buildSuccessMockPayload, getMockFulfilledSnapshot, getMockLuvioWithFulfilledSnapshot, getMockNetworkAdapterCallCount, isImmutable, overrideMockNetworkResponses, resetMockNetworkAdapter, setMockNetworkPayloads, setNetworkConnectivity, stripProperties, verifyImmutable };
419
+ export { ConnectivityState, MemoryDurableStorePersistence, MockDurableStore, buildErrorMockPayload, buildFetchResponse, buildMockNetworkAdapter, buildSuccessMockPayload, flushPendingNetworkRequests, getMockFulfilledSnapshot, getMockLuvioWithFulfilledSnapshot, getMockNetworkAdapterCallCount, isImmutable, overrideMockNetworkResponses, resetMockNetworkAdapter, setMockNetworkPayloads, setNetworkConnectivity, stripProperties, verifyImmutable };
@@ -19,3 +19,4 @@ export declare function stripProperties(obj: {
19
19
  [s: string]: any;
20
20
  }, props: string[]): any;
21
21
  export declare function doesThrow(predicate: () => void): boolean;
22
+ export declare function flushPromises(): Promise<unknown>;
@@ -1,14 +1,16 @@
1
- import type { DurableStore, DurableStoreEntries, OnDurableStoreChangedListener, DurableStoreOperation, DurableStoreChange } from '@luvio/environments';
1
+ import type { DurableStore, DurableStoreEntries, OnDurableStoreChangedListener, DurableStoreOperation } from '@luvio/environments';
2
2
  import type { DurableStorePersistence } from './durableStorePersistence';
3
3
  export declare class MockDurableStore implements DurableStore {
4
+ private writePromises;
4
5
  listeners: Set<OnDurableStoreChangedListener>;
5
6
  persistence: DurableStorePersistence;
6
7
  constructor(persistence?: DurableStorePersistence);
7
8
  getEntries<T>(entryIds: string[], segment: string): Promise<DurableStoreEntries<T> | undefined>;
8
- getAllEntries<T>(segment: string): Promise<DurableStoreEntries<T>>;
9
+ getAllEntries<T>(segment: string): Promise<DurableStoreEntries<T> | undefined>;
9
10
  setEntries<T>(entries: DurableStoreEntries<T>, segment: string): Promise<void>;
10
11
  evictEntries(ids: string[], segment: string): Promise<void>;
11
12
  registerOnChangedListener(listener: OnDurableStoreChangedListener): () => Promise<void>;
12
13
  batchOperations<T>(operations: DurableStoreOperation<T>[]): Promise<void>;
13
- performOperation<T>(operation: DurableStoreOperation<T>): Promise<DurableStoreChange>;
14
+ private performOperation;
15
+ flushPendingOperations(): Promise<void>;
14
16
  }
@@ -2,10 +2,12 @@ export interface DurableStorePersistence {
2
2
  get<T>(key: string): Promise<T | undefined>;
3
3
  set<T>(key: string, value: T): Promise<void>;
4
4
  delete(key: string): Promise<void>;
5
+ flushPendingWork(): Promise<void>;
5
6
  }
6
7
  export declare class MemoryDurableStorePersistence implements DurableStorePersistence {
7
8
  private store;
8
9
  get<T>(key: string): Promise<T | undefined>;
9
10
  set<T>(key: string, value: T): Promise<void>;
10
11
  delete(key: string): Promise<void>;
12
+ flushPendingWork(): Promise<void>;
11
13
  }
@@ -1,6 +1,6 @@
1
- export { MockPayload, ConnectivityState, buildMockNetworkAdapter, resetMockNetworkAdapter, getMockNetworkAdapterCallCount, buildSuccessMockPayload, buildErrorMockPayload, setMockNetworkPayloads, setNetworkConnectivity, buildFetchResponse, overrideMockNetworkResponses, } from './network';
1
+ export { MockPayload, ConnectivityState, buildMockNetworkAdapter, resetMockNetworkAdapter, getMockNetworkAdapterCallCount, buildSuccessMockPayload, buildErrorMockPayload, setMockNetworkPayloads, setNetworkConnectivity, buildFetchResponse, overrideMockNetworkResponses, flushPendingNetworkRequests, } from './network';
2
2
  export { verifyImmutable, isImmutable } from './verification';
3
3
  export { getMockLuvioWithFulfilledSnapshot, getMockFulfilledSnapshot } from './mocks';
4
4
  export { stripProperties } from './utils';
5
- export { MockDurableStore } from './durableStore';
5
+ export { MockDurableStore } from './MockDurableStore';
6
6
  export { MemoryDurableStorePersistence, DurableStorePersistence } from './durableStorePersistence';
@@ -7,6 +7,13 @@ export interface MockPayload {
7
7
  networkArgs: Partial<ResourceRequest>;
8
8
  response: FetchResponse<any>;
9
9
  }
10
+ /**
11
+ * Flushes any pending network requests. Useful for tests that need to ensure all
12
+ * un-awaited background refreshes are complete
13
+ *
14
+ * @param _mockNetworkAdapter {NetworkAdapter} The network adapter instance to flush
15
+ */
16
+ export declare function flushPendingNetworkRequests(_mockNetworkAdapter: NetworkAdapter): Promise<void>;
10
17
  export declare function buildMockNetworkAdapter(mockPayloads: MockPayload[]): NetworkAdapter;
11
18
  export declare function setNetworkConnectivity(mockNetworkAdapter: NetworkAdapter, connectivityState: ConnectivityState): void;
12
19
  export declare function setMockNetworkPayloads(mockNetworkAdapter: NetworkAdapter, mockPayloads: MockPayload[]): void;
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('sinon'), require('@luvio/environments')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'sinon', '@luvio/environments'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.luvioAdapterTestLibrary = {}, global.sinon, global.environments));
5
- })(this, (function (exports, sinon, environments) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('sinon')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'sinon'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.luvioAdapterTestLibrary = {}, global.sinon));
5
+ })(this, (function (exports, sinon) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -56,6 +56,9 @@
56
56
  return true;
57
57
  }
58
58
  return false;
59
+ }
60
+ function flushPromises() {
61
+ return new Promise((resolve) => setTimeout(resolve, 0));
59
62
  }
60
63
 
61
64
  const networkConnectivityStateMap = new WeakMap();
@@ -101,6 +104,18 @@
101
104
  function isOkResponse(status) {
102
105
  return status >= 200 && status <= 299;
103
106
  }
107
+ /**
108
+ * Flushes any pending network requests. Useful for tests that need to ensure all
109
+ * un-awaited background refreshes are complete
110
+ *
111
+ * @param _mockNetworkAdapter {NetworkAdapter} The network adapter instance to flush
112
+ */
113
+ async function flushPendingNetworkRequests(_mockNetworkAdapter) {
114
+ // since the network mock is actually synchronous (just returns things wrapped
115
+ // in Promise.resolve/reject) the only thing necessary to flush any pending
116
+ // network activity is to flush the pending microtask queue
117
+ await flushPromises();
118
+ }
104
119
  function buildMockNetworkAdapter(mockPayloads) {
105
120
  // any endpoints not setup with a fake will return a rejected promise
106
121
  const networkAdapter = sinon__default["default"].stub().rejects(buildMockSetupError());
@@ -288,53 +303,66 @@
288
303
  return this.store[key];
289
304
  }
290
305
  async set(key, value) {
306
+ // simulate a more realistic durable store by making the write wait a
307
+ // tick before actually setting the value
308
+ await flushPromises();
291
309
  this.store[key] = value;
292
310
  }
293
311
  async delete(key) {
294
312
  delete this.store[key];
295
313
  }
314
+ async flushPendingWork() {
315
+ // since this implementation does actual "IO" synchronously the only
316
+ // thing necessary to await any pending IO is to flush the current
317
+ // microtask queue
318
+ await flushPromises();
319
+ }
296
320
  }
297
321
 
322
+ function waitForPromiseSet(set) {
323
+ // NOTE: we are building an array from the Set at this point in time. If
324
+ // more Promises are added to the Set while this is awaiting it won't
325
+ // await the newly-added Promise. That's what we want.
326
+ return Promise.all(Array.from(set)).then();
327
+ }
298
328
  class MockDurableStore {
299
329
  constructor(persistence) {
300
- // NOTE: This mock class doesn't enforce read/write synchronization
330
+ // for read/write synchronization
331
+ this.writePromises = new Set();
301
332
  this.listeners = new Set();
302
333
  this.persistence = persistence || new MemoryDurableStorePersistence();
303
334
  }
304
- getEntries(entryIds, segment) {
335
+ async getEntries(entryIds, segment) {
336
+ // await any current write operations
337
+ if (this.writePromises.size > 0) {
338
+ await waitForPromiseSet(this.writePromises);
339
+ }
340
+ const entries = await this.persistence.get(segment);
341
+ if (entries === undefined) {
342
+ return undefined;
343
+ }
305
344
  const returnSource = Object.create(null);
306
- return this.persistence.get(segment).then((entries) => {
307
- if (entries === undefined) {
308
- return undefined;
345
+ for (const entryId of entryIds) {
346
+ const entry = entries[entryId];
347
+ if (entry !== undefined) {
348
+ returnSource[entryId] = clone(entry);
309
349
  }
310
- for (const entryId of entryIds) {
311
- const entry = entries[entryId];
312
- if (entry !== undefined) {
313
- returnSource[entryId] = clone(entry);
314
- }
315
- }
316
- return returnSource;
317
- });
350
+ }
351
+ return returnSource;
318
352
  }
319
- getAllEntries(segment) {
320
- const returnSource = Object.create(null);
321
- return this.persistence.get(segment).then((rawEntries) => {
322
- const entries = rawEntries === undefined ? {} : rawEntries;
323
- for (const key of Object.keys(entries)) {
324
- returnSource[key] = clone(entries[key]);
325
- }
326
- return returnSource;
327
- });
353
+ async getAllEntries(segment) {
354
+ // await any current write operations
355
+ if (this.writePromises.size > 0) {
356
+ await waitForPromiseSet(this.writePromises);
357
+ }
358
+ const entries = await this.persistence.get(segment);
359
+ return entries;
328
360
  }
329
361
  setEntries(entries, segment) {
330
- return this.batchOperations([
331
- { entries, segment, type: environments.DurableStoreOperationType.SetEntries },
332
- ]);
362
+ return this.batchOperations([{ entries, segment, type: 'setEntries' }]);
333
363
  }
334
364
  evictEntries(ids, segment) {
335
- return this.batchOperations([
336
- { ids, segment, type: environments.DurableStoreOperationType.EvictEntries },
337
- ]);
365
+ return this.batchOperations([{ ids, segment, type: 'evictEntries' }]);
338
366
  }
339
367
  registerOnChangedListener(listener) {
340
368
  this.listeners.add(listener);
@@ -344,9 +372,22 @@
344
372
  };
345
373
  }
346
374
  async batchOperations(operations) {
375
+ // await any current write operations
376
+ if (this.writePromises.size > 0) {
377
+ await waitForPromiseSet(this.writePromises);
378
+ }
347
379
  const changes = [];
348
- for (let i = 0; i < operations.length; i++) {
349
- changes.push(await this.performOperation(operations[i]));
380
+ const writePromise = (async () => {
381
+ for (const operation of operations) {
382
+ changes.push(await this.performOperation(operation));
383
+ }
384
+ })();
385
+ this.writePromises.add(writePromise);
386
+ try {
387
+ await writePromise;
388
+ }
389
+ finally {
390
+ this.writePromises.delete(writePromise);
350
391
  }
351
392
  this.listeners.forEach((listener) => {
352
393
  listener(changes);
@@ -358,13 +399,13 @@
358
399
  const entries = rawEntries === undefined ? {} : rawEntries;
359
400
  let ids = [];
360
401
  switch (operation.type) {
361
- case environments.DurableStoreOperationType.SetEntries:
402
+ case 'setEntries':
362
403
  ids = Object.keys(operation.entries);
363
404
  ids.forEach((id) => {
364
405
  entries[id] = clone(operation.entries[id]);
365
406
  });
366
407
  break;
367
- case environments.DurableStoreOperationType.EvictEntries:
408
+ case 'evictEntries':
368
409
  ids = operation.ids;
369
410
  ids.forEach((id) => {
370
411
  delete entries[id];
@@ -373,6 +414,14 @@
373
414
  await this.persistence.set(operation.segment, entries);
374
415
  return { ids, segment, type: operation.type };
375
416
  }
417
+ async flushPendingOperations() {
418
+ // flush any pending read operations
419
+ await this.persistence.flushPendingWork();
420
+ // wait for any pending writes to finish
421
+ while (this.writePromises.size > 0) {
422
+ await waitForPromiseSet(this.writePromises);
423
+ }
424
+ }
376
425
  }
377
426
 
378
427
  exports.MemoryDurableStorePersistence = MemoryDurableStorePersistence;
@@ -381,6 +430,7 @@
381
430
  exports.buildFetchResponse = buildFetchResponse;
382
431
  exports.buildMockNetworkAdapter = buildMockNetworkAdapter;
383
432
  exports.buildSuccessMockPayload = buildSuccessMockPayload;
433
+ exports.flushPendingNetworkRequests = flushPendingNetworkRequests;
384
434
  exports.getMockFulfilledSnapshot = getMockFulfilledSnapshot;
385
435
  exports.getMockLuvioWithFulfilledSnapshot = getMockLuvioWithFulfilledSnapshot;
386
436
  exports.getMockNetworkAdapterCallCount = getMockNetworkAdapterCallCount;
@@ -19,3 +19,4 @@ export declare function stripProperties(obj: {
19
19
  [s: string]: any;
20
20
  }, props: string[]): any;
21
21
  export declare function doesThrow(predicate: () => void): boolean;
22
+ export declare function flushPromises(): Promise<unknown>;
@@ -1,14 +1,16 @@
1
- import type { DurableStore, DurableStoreEntries, OnDurableStoreChangedListener, DurableStoreOperation, DurableStoreChange } from '@luvio/environments';
1
+ import type { DurableStore, DurableStoreEntries, OnDurableStoreChangedListener, DurableStoreOperation } from '@luvio/environments';
2
2
  import type { DurableStorePersistence } from './durableStorePersistence';
3
3
  export declare class MockDurableStore implements DurableStore {
4
+ private writePromises;
4
5
  listeners: Set<OnDurableStoreChangedListener>;
5
6
  persistence: DurableStorePersistence;
6
7
  constructor(persistence?: DurableStorePersistence);
7
8
  getEntries<T>(entryIds: string[], segment: string): Promise<DurableStoreEntries<T> | undefined>;
8
- getAllEntries<T>(segment: string): Promise<DurableStoreEntries<T>>;
9
+ getAllEntries<T>(segment: string): Promise<DurableStoreEntries<T> | undefined>;
9
10
  setEntries<T>(entries: DurableStoreEntries<T>, segment: string): Promise<void>;
10
11
  evictEntries(ids: string[], segment: string): Promise<void>;
11
12
  registerOnChangedListener(listener: OnDurableStoreChangedListener): () => Promise<void>;
12
13
  batchOperations<T>(operations: DurableStoreOperation<T>[]): Promise<void>;
13
- performOperation<T>(operation: DurableStoreOperation<T>): Promise<DurableStoreChange>;
14
+ private performOperation;
15
+ flushPendingOperations(): Promise<void>;
14
16
  }
@@ -2,10 +2,12 @@ export interface DurableStorePersistence {
2
2
  get<T>(key: string): Promise<T | undefined>;
3
3
  set<T>(key: string, value: T): Promise<void>;
4
4
  delete(key: string): Promise<void>;
5
+ flushPendingWork(): Promise<void>;
5
6
  }
6
7
  export declare class MemoryDurableStorePersistence implements DurableStorePersistence {
7
8
  private store;
8
9
  get<T>(key: string): Promise<T | undefined>;
9
10
  set<T>(key: string, value: T): Promise<void>;
10
11
  delete(key: string): Promise<void>;
12
+ flushPendingWork(): Promise<void>;
11
13
  }
@@ -1,6 +1,6 @@
1
- export { MockPayload, ConnectivityState, buildMockNetworkAdapter, resetMockNetworkAdapter, getMockNetworkAdapterCallCount, buildSuccessMockPayload, buildErrorMockPayload, setMockNetworkPayloads, setNetworkConnectivity, buildFetchResponse, overrideMockNetworkResponses, } from './network';
1
+ export { MockPayload, ConnectivityState, buildMockNetworkAdapter, resetMockNetworkAdapter, getMockNetworkAdapterCallCount, buildSuccessMockPayload, buildErrorMockPayload, setMockNetworkPayloads, setNetworkConnectivity, buildFetchResponse, overrideMockNetworkResponses, flushPendingNetworkRequests, } from './network';
2
2
  export { verifyImmutable, isImmutable } from './verification';
3
3
  export { getMockLuvioWithFulfilledSnapshot, getMockFulfilledSnapshot } from './mocks';
4
4
  export { stripProperties } from './utils';
5
- export { MockDurableStore } from './durableStore';
5
+ export { MockDurableStore } from './MockDurableStore';
6
6
  export { MemoryDurableStorePersistence, DurableStorePersistence } from './durableStorePersistence';
@@ -7,6 +7,13 @@ export interface MockPayload {
7
7
  networkArgs: Partial<ResourceRequest>;
8
8
  response: FetchResponse<any>;
9
9
  }
10
+ /**
11
+ * Flushes any pending network requests. Useful for tests that need to ensure all
12
+ * un-awaited background refreshes are complete
13
+ *
14
+ * @param _mockNetworkAdapter {NetworkAdapter} The network adapter instance to flush
15
+ */
16
+ export declare function flushPendingNetworkRequests(_mockNetworkAdapter: NetworkAdapter): Promise<void>;
10
17
  export declare function buildMockNetworkAdapter(mockPayloads: MockPayload[]): NetworkAdapter;
11
18
  export declare function setNetworkConnectivity(mockNetworkAdapter: NetworkAdapter, connectivityState: ConnectivityState): void;
12
19
  export declare function setMockNetworkPayloads(mockNetworkAdapter: NetworkAdapter, mockPayloads: MockPayload[]): void;