Skip to content

Commit

Permalink
feat: use MemoryProvider in unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kirillzyusko committed Feb 29, 2024
1 parent 581bd53 commit 3e8ee71
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 107 deletions.
107 changes: 22 additions & 85 deletions lib/storage/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,27 @@
import utils from '../../utils';
import type {Key, KeyValuePairList, Value} from '../providers/types';
import type StorageProvider from '../providers/types';
import MemoryOnlyProvider, {mockStore, mockSet, setMockStore} from '../providers/MemoryOnlyProvider';

let storageMapInternal: Record<Key, Value> = {};
const init = jest.fn(MemoryOnlyProvider.init);

const set = jest.fn((key, value) => {
storageMapInternal[key] = value;
return Promise.resolve(value);
});
init();

const idbKeyvalMock: StorageProvider = {
name: 'KeyValMockProvider',
init: () => undefined,
setItem(key, value) {
return set(key, value);
},
multiSet(pairs) {
const setPromises = pairs.map(([key, value]) => this.setItem(key, value));
return new Promise((resolve) => Promise.all(setPromises).then(() => resolve(storageMapInternal)));
},
getItem(key) {
return Promise.resolve(storageMapInternal[key]);
},
multiGet(keys) {
const getPromises = keys.map((key) => new Promise((resolve) => this.getItem(key).then((value) => resolve([key, value]))));
return Promise.all(getPromises) as Promise<KeyValuePairList>;
},
multiMerge(pairs) {
pairs.forEach(([key, value]) => {
const existingValue = storageMapInternal[key];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newValue = utils.fastMerge(existingValue as any, value);

set(key, newValue);
});

return Promise.resolve(storageMapInternal);
},
mergeItem(key, _changes, modifiedData) {
return this.setItem(key, modifiedData);
},
removeItem(key) {
delete storageMapInternal[key];
return Promise.resolve();
},
removeItems(keys) {
keys.forEach((key) => {
delete storageMapInternal[key];
});
return Promise.resolve();
},
clear() {
storageMapInternal = {};
return Promise.resolve();
},
getAllKeys() {
return Promise.resolve(Object.keys(storageMapInternal));
},
getDatabaseSize() {
return Promise.resolve({bytesRemaining: 0, bytesUsed: 99999});
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
setMemoryOnlyKeys() {},
keepInstancesSync: () => undefined,
};

const idbKeyvalMockSpy = {
idbKeyvalSet: set,
init: jest.fn(idbKeyvalMock.init),
setItem: jest.fn(idbKeyvalMock.setItem),
getItem: jest.fn(idbKeyvalMock.getItem),
removeItem: jest.fn(idbKeyvalMock.removeItem),
removeItems: jest.fn(idbKeyvalMock.removeItems),
clear: jest.fn(idbKeyvalMock.clear),
getAllKeys: jest.fn(idbKeyvalMock.getAllKeys),
multiGet: jest.fn(idbKeyvalMock.multiGet),
multiSet: jest.fn(idbKeyvalMock.multiSet),
multiMerge: jest.fn(idbKeyvalMock.multiMerge),
mergeItem: jest.fn(idbKeyvalMock.mergeItem),
getStorageMap: jest.fn(() => storageMapInternal),
setInitialMockData: jest.fn((data) => {
storageMapInternal = data;
}),
getDatabaseSize: jest.fn(idbKeyvalMock.getDatabaseSize),
setMemoryOnlyKeys: jest.fn(idbKeyvalMock.setMemoryOnlyKeys),
keepInstancesSync: jest.fn(idbKeyvalMock.keepInstancesSync),
const StorageMock = {
init,
getItem: jest.fn(MemoryOnlyProvider.getItem),
multiGet: jest.fn(MemoryOnlyProvider.multiGet),
setItem: jest.fn(MemoryOnlyProvider.setItem),
multiSet: jest.fn(MemoryOnlyProvider.multiSet),
mergeItem: jest.fn(MemoryOnlyProvider.mergeItem),
multiMerge: jest.fn(MemoryOnlyProvider.multiMerge),
removeItem: jest.fn(MemoryOnlyProvider.removeItem),
removeItems: jest.fn(MemoryOnlyProvider.removeItems),
clear: jest.fn(MemoryOnlyProvider.clear),
setMemoryOnlyKeys: jest.fn(MemoryOnlyProvider.setMemoryOnlyKeys),
getAllKeys: jest.fn(MemoryOnlyProvider.getAllKeys),
getDatabaseSize: jest.fn(MemoryOnlyProvider.getDatabaseSize),
keepInstancesSync: jest.fn(),
mockSet,
getMockStore: jest.fn(() => mockStore),
setMockStore: jest.fn((data) => setMockStore(data)),
};

export default idbKeyvalMockSpy;
export default StorageMock;
36 changes: 18 additions & 18 deletions tests/unit/onyxMultiMergeWebStorageTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ describe('Onyx.mergeCollection() and WebStorage', () => {
afterEach(() => Onyx.clear());

it('merges two sets of data consecutively', () => {
StorageMock.setInitialMockData(initialData);
StorageMock.setMockStore(initialData);

// Given initial data in storage
expect(StorageMock.getStorageMap().test_1).toEqual(initialTestObject);
expect(StorageMock.getStorageMap().test_2).toEqual(initialTestObject);
expect(StorageMock.getStorageMap().test_3).toEqual(initialTestObject);
expect(StorageMock.getMockStore().test_1).toEqual(initialTestObject);
expect(StorageMock.getMockStore().test_2).toEqual(initialTestObject);
expect(StorageMock.getMockStore().test_3).toEqual(initialTestObject);

// And an empty cache values for the collection keys
expect(OnyxCache.getValue('test_1')).not.toBeDefined();
Expand Down Expand Up @@ -75,17 +75,17 @@ describe('Onyx.mergeCollection() and WebStorage', () => {
expect(OnyxCache.getValue('test_3')).toEqual(finalObject);

// And the storage should reflect the same state
expect(StorageMock.getStorageMap().test_1).toEqual(finalObject);
expect(StorageMock.getStorageMap().test_2).toEqual(finalObject);
expect(StorageMock.getStorageMap().test_3).toEqual(finalObject);
expect(StorageMock.getMockStore().test_1).toEqual(finalObject);
expect(StorageMock.getMockStore().test_2).toEqual(finalObject);
expect(StorageMock.getMockStore().test_3).toEqual(finalObject);
});
});

it('cache updates correctly when accessed again if keys are removed or evicted', () => {
// Given empty storage
expect(StorageMock.getStorageMap().test_1).toBeFalsy();
expect(StorageMock.getStorageMap().test_2).toBeFalsy();
expect(StorageMock.getStorageMap().test_3).toBeFalsy();
expect(StorageMock.getMockStore().test_1).toBeFalsy();
expect(StorageMock.getMockStore().test_2).toBeFalsy();
expect(StorageMock.getMockStore().test_3).toBeFalsy();

// And an empty cache values for the collection keys
expect(OnyxCache.getValue('test_1')).toBeFalsy();
Expand All @@ -106,9 +106,9 @@ describe('Onyx.mergeCollection() and WebStorage', () => {
expect(OnyxCache.getValue('test_1')).toEqual(data);
expect(OnyxCache.getValue('test_2')).toEqual(data);
expect(OnyxCache.getValue('test_3')).toEqual(data);
expect(StorageMock.getStorageMap().test_1).toEqual(data);
expect(StorageMock.getStorageMap().test_2).toEqual(data);
expect(StorageMock.getStorageMap().test_3).toEqual(data);
expect(StorageMock.getMockStore().test_1).toEqual(data);
expect(StorageMock.getMockStore().test_2).toEqual(data);
expect(StorageMock.getMockStore().test_3).toEqual(data);

// When we drop all the cache keys (but do not modify the underlying storage) and merge another object
OnyxCache.drop('test_1');
Expand Down Expand Up @@ -137,15 +137,15 @@ describe('Onyx.mergeCollection() and WebStorage', () => {
expect(OnyxCache.getValue('test_3')).toEqual(finalObject);

// And the storage should reflect the same state
expect(StorageMock.getStorageMap().test_1).toEqual(finalObject);
expect(StorageMock.getStorageMap().test_2).toEqual(finalObject);
expect(StorageMock.getStorageMap().test_3).toEqual(finalObject);
expect(StorageMock.getMockStore().test_1).toEqual(finalObject);
expect(StorageMock.getMockStore().test_2).toEqual(finalObject);
expect(StorageMock.getMockStore().test_3).toEqual(finalObject);
});
});

it('setItem() and multiMerge()', () => {
// Onyx should be empty after clear() is called
expect(StorageMock.getStorageMap()).toEqual({});
expect(StorageMock.getMockStore()).toEqual({});

// Given no previous data and several calls to setItem and call to mergeCollection to update a given key

Expand Down Expand Up @@ -174,7 +174,7 @@ describe('Onyx.mergeCollection() and WebStorage', () => {
};

expect(OnyxCache.getValue('test_1')).toEqual(finalObject);
expect(StorageMock.getStorageMap().test_1).toEqual(finalObject);
expect(StorageMock.getMockStore().test_1).toEqual(finalObject);
});
});
});
8 changes: 4 additions & 4 deletions tests/unit/storage/providers/IDBKeyvalProviderTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('storage/providers/IDBKeyVal', () => {
]);

return waitForPromisesToResolve().then(() => {
IDBKeyValProviderMock.idbKeyvalSet.mockClear();
IDBKeyValProviderMock.mockSet.mockClear();

// Given deltas matching existing structure
const USER_1_DELTA = {
Expand All @@ -83,7 +83,7 @@ describe('storage/providers/IDBKeyVal', () => {
['@USER_2', USER_2_DELTA],
]).then(() => {
// Then each existing item should be set with the merged content
expect(IDBKeyValProviderMock.idbKeyvalSet).toHaveBeenNthCalledWith(1, '@USER_1', {
expect(IDBKeyValProviderMock.mockSet).toHaveBeenNthCalledWith(1, '@USER_1', {
name: 'Tom',
age: 31,
traits: {
Expand All @@ -92,7 +92,7 @@ describe('storage/providers/IDBKeyVal', () => {
},
});

expect(IDBKeyValProviderMock.idbKeyvalSet).toHaveBeenNthCalledWith(2, '@USER_2', {
expect(IDBKeyValProviderMock.mockSet).toHaveBeenNthCalledWith(2, '@USER_2', {
name: 'Sarah',
age: 26,
traits: {
Expand Down Expand Up @@ -131,7 +131,7 @@ describe('storage/providers/IDBKeyVal', () => {
// If StorageProvider.clear() does not abort the queue, more idbKeyval.setItem calls would be executed because they would
// be sitting in the setItemQueue
return waitForPromisesToResolve().then(() => {
expect(IDBKeyValProviderMock.idbKeyvalSet).toHaveBeenCalledTimes(0);
expect(IDBKeyValProviderMock.mockSet).toHaveBeenCalledTimes(0);
expect(IDBKeyValProviderMock.clear).toHaveBeenCalledTimes(1);
});
});
Expand Down

0 comments on commit 3e8ee71

Please sign in to comment.