UNPKG

@yoroi/portfolio

Version:

The Portfolio package of Yoroi SDK

212 lines (206 loc) 7.89 kB
import { mountMMKVStorage, observableStorageMaker } from '@yoroi/common'; import { Portfolio } from '@yoroi/types'; import { portfolioTokenManagerMaker } from './token-manager'; import { portfolioTokenStorageMaker } from './adapters/mmkv-storage/token-storage-maker'; import { portfolioApiMock } from './adapters/dullahan-api/api-maker.mocks'; import { tokenInfoMocks } from './adapters/token-info.mocks'; import { createCachedUnknownTokenInfo } from './helpers/create-cached-unknown-token-info'; describe('portfolioTokenManagerMaker', () => { const tokenInfoStorage = observableStorageMaker(mountMMKVStorage({ path: `/test/token-info/` })); const storage = portfolioTokenStorageMaker({ tokenInfoStorage }); afterEach(() => { jest.resetAllMocks(); storage.clear(); }); it('should hydrate the cache', () => { const api = portfolioApiMock.success; const manager = portfolioTokenManagerMaker({ api, storage }); const subscriber = jest.fn(); manager.subscribe(subscriber); manager.hydrate({ sourceId: 'sourceId' }); expect(subscriber).toHaveBeenCalledTimes(1); expect(subscriber).toHaveBeenCalledWith({ on: Portfolio.Event.ManagerOn.Hydrate, sourceId: 'sourceId' }); }); it('should sync token infos with unknowns', async () => { storage.token.infos.save(tokenInfoMocks.storage.notModified); const api = portfolioApiMock.success; const manager = portfolioTokenManagerMaker({ api, storage }); const unknownTokenId = 'unknown.id'; const secondaryTokenIds = [tokenInfoMocks.rnftWhatever.id, tokenInfoMocks.nftCryptoKitty.id, unknownTokenId]; manager.hydrate({ sourceId: 'sourceId' }); const subscriber = jest.fn(); manager.subscribe(subscriber); const unknownCachedTokenInfo = createCachedUnknownTokenInfo(unknownTokenId); const result = await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(subscriber).toHaveBeenCalledTimes(1); expect(subscriber).toHaveBeenCalledWith({ on: Portfolio.Event.ManagerOn.Sync, ids: expect.arrayContaining(secondaryTokenIds.slice(1)), sourceId: 'sourceId' }); expect(result.size).toBe(3); // should update the stale cache records with api data and refresh cache info const resultNftCryptoKitty = result.get(tokenInfoMocks.nftCryptoKitty.id); if (!resultNftCryptoKitty) fail(); expect(resultNftCryptoKitty.expires).toBeGreaterThan(Date.now()); expect(resultNftCryptoKitty.hash).toBe('hash2-1'); expect(resultNftCryptoKitty.record).toEqual(tokenInfoMocks.nftCryptoKitty); // should create unknown token info when api doesn't return it // creates expired so it will always hit the api when syncing const resultUnknownToken = result.get(unknownTokenId); if (!resultUnknownToken) fail(); expect(resultUnknownToken.expires).toBe(0); expect(resultUnknownToken.hash).toBe(''); expect(resultUnknownToken.record).toEqual(unknownCachedTokenInfo.record); // should return the cached record when api returns NotModified // yet, should update the expires but not the hash const resultRnftWhatever = result.get(tokenInfoMocks.rnftWhatever.id); if (!resultRnftWhatever) fail(); expect(resultRnftWhatever.expires).toBeGreaterThan(Date.now()); expect(resultRnftWhatever.hash).toBe('hash3'); expect(resultRnftWhatever.record).toEqual(tokenInfoMocks.rnftWhatever); manager.destroy(); }); it('should read all from cache', async () => { storage.token.infos.save(tokenInfoMocks.storage.cached); const api = portfolioApiMock.success; const manager = portfolioTokenManagerMaker({ api, storage }); const secondaryTokenIds = [tokenInfoMocks.rnftWhatever.id]; const result = await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(result.size).toBe(1); // should return the cached record and not hit the api const resultRnftWhatever = result.get(tokenInfoMocks.rnftWhatever.id); if (!resultRnftWhatever) fail(); expect(resultRnftWhatever.expires).toBeGreaterThan(Date.now()); expect(resultRnftWhatever.hash).toBe('hash3'); expect(resultRnftWhatever.record).toEqual(tokenInfoMocks.rnftWhatever); }); it('should create as unknwon when api error', async () => { const api = portfolioApiMock.error; const manager = portfolioTokenManagerMaker({ api, storage }); const secondaryTokenIds = [tokenInfoMocks.rnftWhatever.id]; const unknownCachedTokenInfo = createCachedUnknownTokenInfo(tokenInfoMocks.rnftWhatever.id); const result = await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(result.size).toBe(1); // should return the unknown record const resultRnftWhatever = result.get(tokenInfoMocks.rnftWhatever.id); if (!resultRnftWhatever) fail(); expect(resultRnftWhatever).toEqual(unknownCachedTokenInfo); }); it('should call destroy (coverage) observer is not exposed', () => { const api = portfolioApiMock.success; const manager = portfolioTokenManagerMaker({ api, storage }); manager.destroy(); }); it('should revalidate cache', async () => { const api = portfolioApiMock.success; storage.token.infos.save(tokenInfoMocks.storage.notModified); const manager = portfolioTokenManagerMaker({ api, storage }); const secondaryTokenIds = [tokenInfoMocks.rnftWhatever.id]; const result = await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(result.size).toBe(1); // should return the unknown record const resultRnftWhatever = result.get(tokenInfoMocks.rnftWhatever.id); if (!resultRnftWhatever) fail(); expect(resultRnftWhatever.expires).toBeGreaterThan(Date.now()); expect(resultRnftWhatever.hash).toBe('hash3'); expect(resultRnftWhatever.record).toEqual(tokenInfoMocks.rnftWhatever); }); it('should sync empty', async () => { const api = portfolioApiMock.success; storage.token.infos.save(tokenInfoMocks.storage.notModified); const manager = portfolioTokenManagerMaker({ api, storage }); const secondaryTokenIds = []; const result = await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(result.size).toBe(0); }); it('should sync empty to revalidate', async () => { const api = portfolioApiMock.success; const manager = portfolioTokenManagerMaker({ api, storage }); const secondaryTokenIds = [tokenInfoMocks.rnftWhatever.id]; const unknownCachedTokenInfo = createCachedUnknownTokenInfo(tokenInfoMocks.rnftWhatever.id); const result = await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(result.size).toBe(1); const resultRnftWhatever = result.get(tokenInfoMocks.rnftWhatever.id); if (!resultRnftWhatever) fail(); expect(resultRnftWhatever).toEqual(unknownCachedTokenInfo); }); it('should clear all data in the storage', async () => { const api = portfolioApiMock.success; const manager = portfolioTokenManagerMaker({ api, storage }); const secondaryTokenIds = [tokenInfoMocks.rnftWhatever.id]; await manager.sync({ secondaryTokenIds, sourceId: 'sourceId' }); expect(storage.token.infos.read([tokenInfoMocks.rnftWhatever.id]).length).toBe(1); const subscriber = jest.fn(); manager.subscribe(subscriber); manager.clear({ sourceId: 'sourceId' }); expect(storage.token.infos.all().length).toBe(0); expect(subscriber).toHaveBeenCalledWith({ on: Portfolio.Event.ManagerOn.Clear, sourceId: 'sourceId' }); manager.destroy(); }); }); //# sourceMappingURL=token-manager.test.js.map