UNPKG

@towns-protocol/sdk

Version:

For more details, visit the following resources:

114 lines 5.52 kB
/** * @group main */ import { createEventDecryptedPromise, makeTestClient, waitFor } from '../testUtils'; import { make_MemberPayload_KeyFulfillment, make_MemberPayload_KeySolicitation } from '../../types'; import { hexToBytes } from 'ethereum-cryptography/utils'; import { MembershipOp } from '@towns-protocol/proto'; // Scaffold for ephemeral events tests describe('ephemeralEvents', () => { let clients = []; const makeInitAndStartClient = async () => { const client = await makeTestClient(); await client.initializeUser(); client.startSync(); clients.push(client); return client; }; beforeEach(async () => { }); afterEach(async () => { for (const client of clients) { await client.stop(); } clients = []; }); test('should allow ephemeral key fulfillments', async () => { const alice = await makeInitAndStartClient(); const bob = await makeInitAndStartClient(); const { streamId } = await alice.createDMChannel(bob.userId); const deviceKey = alice.userDeviceKey(); const event = make_MemberPayload_KeySolicitation({ deviceKey: deviceKey.deviceKey, fallbackKey: deviceKey.fallbackKey, isNewDevice: false, sessionIds: ['abc'], }); await alice.makeEventAndAddToStream(streamId, event, { ephemeral: true, }); // before actually fulfilling the key solicitation, this event tells everyone that is listening, that bob fulfilled alice's key solicitation const fulfillmentEvent = make_MemberPayload_KeyFulfillment({ userAddress: hexToBytes(bob.userId), deviceKey: deviceKey.deviceKey, sessionIds: ['abc'], }); /* out of scope for this test, Bob sends Alice the keys on a private channel <- */ await expect(bob.makeEventAndAddToStream(streamId, fulfillmentEvent, { ephemeral: false, })).rejects.toThrow('solicitation with matching device key not found'); await bob.makeEventAndAddToStream(streamId, fulfillmentEvent, { ephemeral: true, }); }); test('should convert ephemeral to non-ephemeral after timeout', async () => { // let's use a GDM to allow people to join after stream creation. we need to test that the ephemeral // solicitation is converted to non-ephemeral after the timeout. const alice = await makeInitAndStartClient(); const bob = await makeInitAndStartClient(); const charlie = await makeInitAndStartClient(); const chuck = await makeInitAndStartClient(); // Set a short timeout for testing chuck['decryptionExtensions'].ephemeralTimeoutMs = 100; const { streamId } = await alice.createGDMChannel([bob.userId, charlie.userId]); await waitFor(() => { return alice.streams.get(streamId)?.view.membershipContent.joined.size === 3; }); const solicitationEphemeralTypes = []; chuck.on('newKeySolicitation', (_, __, ___, ____, _____, ______, ephemeral) => { solicitationEphemeralTypes.push(ephemeral ?? false); }); // Send a message that will trigger ephemeral key solicitation await alice.sendMessage(streamId, 'test message'); await alice.inviteUser(streamId, chuck.userId); await alice.stop(); await bob.stop(); await charlie.stop(); const stream = await chuck.waitForStream(streamId); await stream.waitForMembership(MembershipOp.SO_INVITE); await expect(chuck.joinStream(streamId)).resolves.not.toThrow(); // Wait for ephemeral and non-ephemeral solicitation // [true, false] indicates that chuck sent two solicitation events, one ephemeral and one non-ephemeral await waitFor(() => solicitationEphemeralTypes.length === 2); expect(solicitationEphemeralTypes).toEqual([true, false]); }); test('should handle ephemeral key exchange', async () => { const alice = await makeInitAndStartClient(); const bob = await makeInitAndStartClient(); const charlie = await makeInitAndStartClient(); const chuck = await makeInitAndStartClient(); const { streamId } = await alice.createGDMChannel([bob.userId, charlie.userId]); const ephemeralSolicitations = []; alice.on('newKeySolicitation', (_, __, ___, ____, _____, ______, ephemeral) => { ephemeralSolicitations.push(ephemeral ?? false); }); const ephemeralFulfillments = []; alice.on('ephemeralKeyFulfillment', (_) => { ephemeralFulfillments.push(true); }); await waitFor(() => { return alice.streams.get(streamId)?.view.membershipContent.joined.size === 3; }); await alice.sendMessage(streamId, 'hello'); await alice.inviteUser(streamId, chuck.userId); const stream = await chuck.waitForStream(streamId); await stream.waitForMembership(MembershipOp.SO_INVITE); const chuckEventDecryptedPromise = createEventDecryptedPromise(chuck, 'hello'); await expect(chuck.joinStream(streamId)).resolves.not.toThrow(); await expect(chuckEventDecryptedPromise).resolves.not.toThrow(); expect(ephemeralSolicitations).toEqual([true]); expect(ephemeralFulfillments).toEqual([true]); }); }); //# sourceMappingURL=ephemeralEvents.test.js.map