diff --git a/lib/shared/device-list-utils.test.js b/lib/shared/device-list-utils.test.js --- a/lib/shared/device-list-utils.test.js +++ b/lib/shared/device-list-utils.test.js @@ -3,6 +3,8 @@ import { createAndSignInitialDeviceList, verifyAndGetDeviceList, + addDeviceToDeviceList, + removeDeviceFromDeviceList, } from './device-list-utils.js'; import type { RawDeviceList, @@ -165,6 +167,62 @@ }); }); +describe(addDeviceToDeviceList, () => { + it('adds device to device list', async () => { + mockOlmAPISign(); + + const updates: $ReadOnlyArray = [ + createDeviceList({ devices: ['D1'], timestamp: 1 }), + ]; + const identityClient = mockIdentityClientWithDeviceListHistory(updates); + + await addDeviceToDeviceList(identityClient, 'user1', 'D2'); + + expect(identityClient.updateDeviceList).toHaveBeenCalledWith( + matchingSignedDeviceListWithDevices(['D1', 'D2']), + ); + }); + + it('skips adding device to device list if already exists', async () => { + const updates: $ReadOnlyArray = [ + createDeviceList({ devices: ['D1', 'D2'], timestamp: 1 }), + ]; + const identityClient = mockIdentityClientWithDeviceListHistory(updates); + + await addDeviceToDeviceList(identityClient, 'user1', 'D2'); + + expect(identityClient.updateDeviceList).not.toHaveBeenCalled(); + }); +}); + +describe(removeDeviceFromDeviceList, () => { + it('removes device from device list', async () => { + mockOlmAPISign(); + + const updates: $ReadOnlyArray = [ + createDeviceList({ devices: ['D1', 'D2'], timestamp: 1 }), + ]; + const identityClient = mockIdentityClientWithDeviceListHistory(updates); + + await removeDeviceFromDeviceList(identityClient, 'user1', 'D2'); + + expect(identityClient.updateDeviceList).toHaveBeenCalledWith( + matchingSignedDeviceListWithDevices(['D1']), + ); + }); + + it("skips removing device from device list if it doesn't exist", async () => { + const updates: $ReadOnlyArray = [ + createDeviceList({ devices: ['D1'], timestamp: 1 }), + ]; + const identityClient = mockIdentityClientWithDeviceListHistory(updates); + + await removeDeviceFromDeviceList(identityClient, 'user1', 'D2'); + + expect(identityClient.updateDeviceList).not.toHaveBeenCalled(); + }); +}); + function createDeviceList( rawList: RawDeviceList, curPrimarySignature?: string, @@ -184,6 +242,7 @@ getDeviceListHistoryForUser: jest .fn() .mockResolvedValueOnce(history), + updateDeviceList: jest.fn(), }; return client; } @@ -196,10 +255,32 @@ config.registerConfig(cfg); } -function mockOlmAPISign(func: JestMockFn<[string], Promise>) { +const defaultOlmAPISignMock = jest + .fn<[string], string>() + .mockResolvedValue('mock_signature'); + +function mockOlmAPISign( + func: JestMockFn<[string], Promise> = defaultOlmAPISignMock, + ed25519: string = 'fake_ed25519_public_key', +) { const olmAPI: any = { + initializeCryptoAccount: jest.fn(), + getUserPublicKey: jest + .fn<[], mixed>() + .mockResolvedValue({ primaryIdentityPublicKeys: { ed25519 } }), signMessage: func, }; const cfg: any = { olmAPI }; config.registerConfig(cfg); } + +function matchingSignedDeviceListWithDevices( + devices: $ReadOnlyArray, +): Object { + return expect.objectContaining({ + rawDeviceList: expect.stringContaining( + // slice out closing braces to let it match timestamp + JSON.stringify({ devices }).slice(0, -2), + ), + }); +}