@river-build/sdk
Version:
For more details, visit the following resources:
199 lines • 8.53 kB
JavaScript
/**
* @group main
*/
import { makeEvent } from '../../sign';
import { MembershipOp, SyncOp } from '@river-build/proto';
import { makeRandomUserContext, makeTestRpcClient, makeUniqueSpaceStreamId, TEST_ENCRYPTED_MESSAGE_PROPS, waitForSyncStreams, } from '../testUtils';
import { makeUniqueChannelStreamId, makeUserStreamId, streamIdToBytes, userIdFromAddress, } from '../../id';
import { make_ChannelPayload_Inception, make_ChannelPayload_Message, make_MemberPayload_Membership2, make_SpacePayload_Inception, make_UserPayload_Inception, make_UserPayload_UserMembership, } from '../../types';
async function readSyncStreams(stream, callback) {
for await (const resp of stream) {
if (callback(resp)) {
// callback returns true to break from the loop
break;
}
}
}
describe('streamRpcClient using v2 sync', () => {
let alicesContext;
let bobsContext;
beforeEach(async () => {
alicesContext = await makeRandomUserContext();
bobsContext = await makeRandomUserContext();
});
test('syncStreamsGetsSyncId', async () => {
/** Arrange */
const alice = await makeTestRpcClient();
const alicesUserId = userIdFromAddress(alicesContext.creatorAddress);
const alicesUserStreamIdStr = makeUserStreamId(alicesUserId);
const alicesUserStreamId = streamIdToBytes(alicesUserStreamIdStr);
// create account for alice
await alice.createStream({
events: [
await makeEvent(alicesContext, make_UserPayload_Inception({
streamId: alicesUserStreamId,
})),
],
streamId: alicesUserStreamId,
});
// alice creates a space
const spaceIdStr = makeUniqueSpaceStreamId();
const spaceId = streamIdToBytes(spaceIdStr);
const inceptionEvent = await makeEvent(alicesContext, make_SpacePayload_Inception({
streamId: spaceId,
}));
const joinEvent = await makeEvent(alicesContext, make_MemberPayload_Membership2({
userId: alicesUserId,
op: MembershipOp.SO_JOIN,
initiatorId: alicesUserId,
}));
await alice.createStream({
events: [inceptionEvent, joinEvent],
streamId: spaceId,
});
// alice creates a channel
const channelIdStr = makeUniqueChannelStreamId(spaceIdStr);
const channelId = streamIdToBytes(channelIdStr);
const channelInceptionEvent = await makeEvent(alicesContext, make_ChannelPayload_Inception({
streamId: channelId,
spaceId: spaceId,
}));
const event = await makeEvent(alicesContext, make_MemberPayload_Membership2({
userId: alicesUserId,
op: MembershipOp.SO_JOIN,
initiatorId: alicesUserId,
}));
const alicesStream = await alice.createStream({
events: [channelInceptionEvent, event],
streamId: channelId,
});
/** Act */
// alice calls syncStreams, and waits for the syncId in the response stream
let syncId = undefined;
const syncCookie = alicesStream.stream.nextSyncCookie;
const aliceStreamIterable = alice.syncStreams({
syncPos: [syncCookie],
}, { timeoutMs: -1 });
await expect(waitForSyncStreams(aliceStreamIterable, async (res) => {
syncId = res.syncId;
return res.syncOp === SyncOp.SYNC_NEW && res.syncId !== undefined;
})).resolves.not.toThrow();
await alice.cancelSync({ syncId });
/** Assert */
expect(syncId).toBeDefined();
});
test('addStreamToSyncGetsEvents', async () => {
/** Arrange */
const alice = await makeTestRpcClient();
const alicesUserId = userIdFromAddress(alicesContext.creatorAddress);
const alicesUserStreamIdStr = makeUserStreamId(alicesUserId);
const alicesUserStreamId = streamIdToBytes(alicesUserStreamIdStr);
const bob = await makeTestRpcClient();
const bobsUserId = userIdFromAddress(bobsContext.creatorAddress);
const bobsUserStreamIdStr = makeUserStreamId(bobsUserId);
const bobsUserStreamId = streamIdToBytes(bobsUserStreamIdStr);
// create accounts for alice and bob
await alice.createStream({
events: [
await makeEvent(alicesContext, make_UserPayload_Inception({
streamId: alicesUserStreamId,
})),
],
streamId: alicesUserStreamId,
});
const bobsUserStream = await bob.createStream({
events: [
await makeEvent(bobsContext, make_UserPayload_Inception({
streamId: bobsUserStreamId,
})),
],
streamId: bobsUserStreamId,
});
// alice creates a space
const spaceIdStr = makeUniqueSpaceStreamId();
const spaceId = streamIdToBytes(spaceIdStr);
const inceptionEvent = await makeEvent(alicesContext, make_SpacePayload_Inception({
streamId: spaceId,
}));
const joinEvent = await makeEvent(alicesContext, make_MemberPayload_Membership2({
userId: alicesUserId,
op: MembershipOp.SO_JOIN,
initiatorId: alicesUserId,
}));
await alice.createStream({
events: [inceptionEvent, joinEvent],
streamId: spaceId,
});
// alice creates a channel
const channelIdStr = makeUniqueChannelStreamId(spaceIdStr);
const channelId = streamIdToBytes(channelIdStr);
const channelInceptionEvent = await makeEvent(alicesContext, make_ChannelPayload_Inception({
streamId: channelId,
spaceId: spaceId,
}));
let event = await makeEvent(alicesContext, make_MemberPayload_Membership2({
userId: alicesUserId,
op: MembershipOp.SO_JOIN,
initiatorId: alicesUserId,
streamParentId: spaceIdStr,
}));
const alicesChannel = await alice.createStream({
events: [channelInceptionEvent, event],
streamId: channelId,
});
/** Act */
// bob calls syncStreams, and waits for the syncId in the response stream
const bobSyncStreams = bob.syncStreams({
syncPos: [],
}, { timeoutMs: -1 });
// bob reads the syncId from the response stream
let syncId = undefined;
for await (const resp of bobSyncStreams) {
if (resp.syncOp === SyncOp.SYNC_NEW) {
syncId = resp.syncId;
break;
}
}
// bob joins the channel
event = await makeEvent(bobsContext, make_UserPayload_UserMembership({
op: MembershipOp.SO_JOIN,
streamId: channelId,
streamParentId: spaceId,
}), bobsUserStream.stream?.miniblocks.at(-1)?.header?.hash);
await bob.addEvent({
streamId: bobsUserStreamId,
event,
});
// bob adds alice's channel to his syncStreams
const bobsChannelStream = await bob.getStream({ streamId: channelId }, { timeoutMs: -1 });
await bob.addStreamToSync({
syncId: syncId,
syncPos: bobsChannelStream.stream.nextSyncCookie,
});
// alice posts a message
event = await makeEvent(alicesContext, make_ChannelPayload_Message({
...TEST_ENCRYPTED_MESSAGE_PROPS,
ciphertext: 'hello',
}), alicesChannel.stream?.miniblocks.at(-1)?.header?.hash);
await alice.addEvent({
streamId: channelId,
event,
});
// bob should see the message in his sync stream
// hnt-3683 explains:
// When AddEvent is called, node calls streamImpl.notifyToSubscribers() twice
// first time is from addEventImpl called by AddEvent.
// second time is from the MakeMiniBlock triggered by miniblockTick
let messagesReceived = 0;
await readSyncStreams(bobSyncStreams, function (_) {
//log('bobSyncStreams', `resp #${++messagesReceived}`, resp)
++messagesReceived;
return messagesReceived === 2;
});
/** Assert */
expect(syncId).toBeTruthy();
expect(messagesReceived).toEqual(2);
await bob.cancelSync({ syncId });
});
});
//# sourceMappingURL=streamRpcClientSync.test.js.map