UNPKG

@river-build/sdk

Version:

For more details, visit the following resources:

62 lines 3.22 kB
/** * @group main */ import { MembershipOp } from '@river-build/proto'; import { makeTestClient, waitFor } from '../testUtils'; import { genShortId } from '../../id'; describe('syncedStream', () => { test('clientRefreshesStreamOnBadSyncCookie', async () => { const bobDeviceId = genShortId(); const bob = await makeTestClient({ deviceId: bobDeviceId }); await bob.initializeUser(); bob.startSync(); const alice = await makeTestClient(); await alice.initializeUser(); alice.startSync(); const { streamId } = await bob.createDMChannel(alice.userId); const aliceStream = await alice.waitForStream(streamId); // Bob waits for stream and goes offline const bobStreamCached = await bob.waitForStream(streamId); await bobStreamCached.waitForMembership(MembershipOp.SO_JOIN); await bob.stopSync(); // Force the creation of N snapshots, which will make the sync cookie invalid for (let i = 0; i < 10; i++) { await alice.sendMessage(streamId, `'hello ${i}`); await alice.debugForceMakeMiniblock(streamId, { forceSnapshot: true }); } // later, Bob returns const bob2 = await makeTestClient({ context: bob.signerContext, deviceId: bobDeviceId }); await bob2.initializeUser(); bob2.startSync(); // the stream is now loaded from cache const bobStreamFresh = await bob2.waitForStream(streamId); const fresh = bobStreamFresh.view.timeline.map((e) => e.remoteEvent?.hashStr); const cached = bobStreamCached.view.timeline.map((e) => e.remoteEvent?.hashStr); expect(fresh.length).toBeGreaterThan(0); expect(cached.length).toBeGreaterThan(0); expect(fresh).toEqual(cached); expect(aliceStream.view.timeline.length).toBeGreaterThan(bobStreamFresh.view.timeline.length); // wait for new stream to trigger bad_sync_cookie and get a fresh view sent back await waitFor(() => bobStreamFresh.view.miniblockInfo.max > bobStreamCached.view.miniblockInfo.max); // Backfill the entire stream while (!bobStreamFresh.view.miniblockInfo.terminusReached) { await bob2.scrollback(streamId); } // Once Bob's stream is fully backfilled, the sync cookie should match Alice's await waitFor(() => aliceStream.view.miniblockInfo.max === bobStreamFresh.view.miniblockInfo.max); // check that the events are the same const aliceEvents = aliceStream.view.timeline.map((e) => e.hashStr); const bobEvents = bobStreamFresh.view.timeline.map((e) => e.hashStr); await waitFor(() => aliceEvents.sort() === bobEvents.sort()); const bobEventCount = bobEvents.length; // Alice sends another 5 messages for (let i = 0; i < 5; i++) { await alice.sendMessage(streamId, `'hello again ${i}`); } // Wait for Bob to sync the new messages to verify that sync still works await waitFor(() => bobStreamFresh.view.timeline.length === bobEventCount + 5); await bob2.stopSync(); await alice.stopSync(); }); }); //# sourceMappingURL=syncedStream.test.js.map