import type { AdapterPayload } from 'oidc-provider'; import type { ExpiringAdapter } from '../../../../src/identity/storage/ExpiringAdapterFactory'; import { ExpiringAdapterFactory } from '../../../../src/identity/storage/ExpiringAdapterFactory'; import type { ExpiringStorage } from '../../../../src/storage/keyvalue/ExpiringStorage'; // Use fixed dates jest.useFakeTimers(); describe('An ExpiringAdapterFactory', (): void => { const storageName = '/storage'; const name = 'nnaammee'; const id = 'http://alice.test.com/card#me'; const grantId = 'grant123456'; let payload: AdapterPayload; let storage: ExpiringStorage; let adapter: ExpiringAdapter; let factory: ExpiringAdapterFactory; const expiresIn = 333 * 1000; beforeEach(async(): Promise => { payload = { data: 'data!' }; const map = new Map(); storage = { get: jest.fn().mockImplementation((key: string): any => map.get(key)), set: jest.fn().mockImplementation((key: string, value: any): any => map.set(key, value)), delete: jest.fn().mockImplementation((key: string): any => map.delete(key)), } as any; factory = new ExpiringAdapterFactory({ storageName, storage }); adapter = factory.createStorageAdapter(name); }); it('can find payload by id.', async(): Promise => { await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); expect(storage.set).toHaveBeenCalledTimes(1); expect(storage.set).toHaveBeenCalledWith(expect.anything(), payload, expiresIn); await expect(adapter.find(id)).resolves.toBe(payload); }); it('can store payloads without expiration time.', async(): Promise => { await expect(adapter.upsert(id, payload)).resolves.toBeUndefined(); expect(storage.set).toHaveBeenCalledTimes(1); expect(storage.set).toHaveBeenCalledWith(expect.anything(), payload, undefined); }); it('can find payload by userCode.', async(): Promise => { const userCode = 'userCode!'; payload.userCode = userCode; await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); expect(storage.set).toHaveBeenCalledTimes(2); expect(storage.set).toHaveBeenCalledWith(expect.anything(), payload, expiresIn); expect(storage.set).toHaveBeenCalledWith(expect.anything(), id, expiresIn); await expect(adapter.findByUserCode(userCode)).resolves.toBe(payload); }); it('can find payload by uid.', async(): Promise => { const uid = 'uid!'; payload.uid = uid; await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); expect(storage.set).toHaveBeenCalledTimes(2); expect(storage.set).toHaveBeenCalledWith(expect.anything(), payload, expiresIn); expect(storage.set).toHaveBeenCalledWith(expect.anything(), id, expiresIn); await expect(adapter.findByUid(uid)).resolves.toBe(payload); }); it('can revoke by grantId.', async(): Promise => { payload.grantId = grantId; await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); expect(storage.set).toHaveBeenCalledTimes(2); expect(storage.set).toHaveBeenCalledWith(expect.anything(), payload, expiresIn); expect(storage.set).toHaveBeenCalledWith(expect.anything(), [ expect.anything() ], expiresIn); await expect(adapter.find(id)).resolves.toBe(payload); await expect(adapter.revokeByGrantId(grantId)).resolves.toBeUndefined(); expect(storage.delete).toHaveBeenCalledTimes(2); await expect(adapter.find(id)).resolves.toBeUndefined(); }); it('does not do anything if revokeByGrantId finds no matching grant.', async(): Promise => { await expect(adapter.revokeByGrantId(grantId)).resolves.toBeUndefined(); expect(storage.delete).toHaveBeenCalledTimes(0); }); it('can store multiple ids for a single grant.', async(): Promise => { payload.grantId = grantId; const id2 = 'id2!'; const payload2 = { data: 'data2!', grantId }; await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); await expect(adapter.upsert(id2, payload2, 333)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toBe(payload); await expect(adapter.find(id2)).resolves.toBe(payload2); await expect(adapter.revokeByGrantId(grantId)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toBeUndefined(); await expect(adapter.find(id2)).resolves.toBeUndefined(); }); it('can destroy the payload.', async(): Promise => { await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toBe(payload); await expect(adapter.destroy(id)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toBeUndefined(); }); it('can consume the payload.', async(): Promise => { // Caching since the object gets modified const cachedPayload = { ...payload }; await expect(adapter.upsert(id, payload, 333)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toEqual(cachedPayload); await expect(adapter.consume(id)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toEqual({ ...cachedPayload, consumed: Math.floor(Date.now() / 1000) }); }); it('does not do anything if consume finds no payload.', async(): Promise => { await expect(adapter.consume(id)).resolves.toBeUndefined(); await expect(adapter.find(id)).resolves.toBeUndefined(); }); });