UNPKG

@river-build/sdk

Version:

For more details, visit the following resources:

217 lines 11.3 kB
/** * @group main */ import { makeTestClient, makeUniqueSpaceStreamId } from '../testUtils'; import { makeUniqueChannelStreamId, makeDMStreamId, streamIdAsString } from '../../id'; import { CreationCookieSchema, InfoRequestSchema } from '@river-build/proto'; import { deriveKeyAndIV, encryptAESGCM } from '../../crypto_utils'; import { create } from '@bufbuild/protobuf'; describe('mediaTests', () => { let bobsClient; beforeEach(async () => { bobsClient = await makeTestClient(); await bobsClient.initializeUser(); bobsClient.startSync(); }); afterEach(async () => { await bobsClient.stop(); }); async function bobCreateMediaStream(chunkCount) { const spaceId = makeUniqueSpaceStreamId(); await expect(bobsClient.createSpace(spaceId)).resolves.not.toThrow(); const channelId = makeUniqueChannelStreamId(spaceId); await expect(bobsClient.createChannel(spaceId, 'Channel', 'Topic', channelId)).resolves.not.toThrow(); return bobsClient.createMediaStream(channelId, spaceId, undefined, chunkCount); } async function bobSendMediaPayloads(creationCookie, chunks) { let cc = create(CreationCookieSchema, creationCookie); for (let i = 0; i < chunks; i++) { const chunk = new Uint8Array(100); // Create novel chunk content for testing purposes chunk.fill(i, 0, 100); const last = i == chunks - 1; const result = await bobsClient.sendMediaPayload(cc, last, chunk, i); cc = create(CreationCookieSchema, { ...cc, prevMiniblockHash: new Uint8Array(result.creationCookie.prevMiniblockHash), miniblockNum: result.creationCookie.miniblockNum, }); } return cc; } async function bobSendEncryptedMediaPayload(creationCookie, last, data, key, iv) { const { ciphertext } = await encryptAESGCM(data, key, iv); const result = await bobsClient.sendMediaPayload(creationCookie, last, ciphertext, 0); return result.creationCookie; } function createTestMediaChunks(chunks) { const data = new Uint8Array(10 * chunks); for (let i = 0; i < chunks; i++) { const start = i * 10; const end = start + 10; data.fill(i, start, end); } return data; } async function bobCreateSpaceMediaStream(spaceId, chunkCount) { await expect(bobsClient.createSpace(spaceId)).resolves.not.toThrow(); const mediaInfo = await bobsClient.createMediaStream(undefined, spaceId, undefined, chunkCount); return mediaInfo; } test('clientCanCreateMediaStream', async () => { await expect(bobCreateMediaStream(10)).resolves.not.toThrow(); }); test('clientCanCreateSpaceMediaStream', async () => { const spaceId = makeUniqueSpaceStreamId(); await expect(bobCreateSpaceMediaStream(spaceId, 10)).resolves.not.toThrow(); }); test('clientCanSendMediaPayload', async () => { const mediaStreamInfo = await bobCreateMediaStream(10); await bobSendMediaPayloads(mediaStreamInfo.creationCookie, 10); }); test('clientCanSendSpaceMediaPayload', async () => { const spaceId = makeUniqueSpaceStreamId(); const mediaStreamInfo = await bobCreateSpaceMediaStream(spaceId, 10); await expect(bobSendMediaPayloads(mediaStreamInfo.creationCookie, 10)).resolves.not.toThrow(); }); test('clientCanSendEncryptedDerivedAesGmPayload', async () => { const spaceId = makeUniqueSpaceStreamId(); const mediaStreamInfo = await bobCreateSpaceMediaStream(spaceId, 3); const { iv, key } = await deriveKeyAndIV(spaceId); const data = createTestMediaChunks(2); await expect(bobSendEncryptedMediaPayload(mediaStreamInfo.creationCookie, false, data, key, iv)).resolves.not.toThrow(); }); test('clientCanDownloadEncryptedDerivedAesGmPayload', async () => { const spaceId = makeUniqueSpaceStreamId(); const mediaStreamInfo = await bobCreateSpaceMediaStream(spaceId, 2); let creationCookie = mediaStreamInfo.creationCookie; const { iv, key } = await deriveKeyAndIV(spaceId); const data = createTestMediaChunks(2); creationCookie = await bobSendEncryptedMediaPayload(creationCookie, false, data, key, iv); await bobSendEncryptedMediaPayload(creationCookie, true, data, key, iv); const decryptedChunks = await bobsClient.getMediaPayload(streamIdAsString(creationCookie.streamId), key, iv); expect(decryptedChunks).toEqual(data); }); test('chunkIndexNeedsToBeWithinBounds', async () => { const result = await bobCreateMediaStream(10); const chunk = new Uint8Array(100); await expect(bobsClient.sendMediaPayload(result.creationCookie, false, chunk, -1)).rejects.toThrow(); await expect(bobsClient.sendMediaPayload(result.creationCookie, false, chunk, 11)).rejects.toThrow(); }); test('chunkSizeCanBeAtLimit', async () => { const result = await bobCreateMediaStream(10); const chunk = new Uint8Array(500000); await expect(bobsClient.sendMediaPayload(result.creationCookie, false, chunk, 0)).resolves.not.toThrow(); }); test('chunkSizeNeedsToBeWithinLimit', async () => { const result = await bobCreateMediaStream(10); const chunk = new Uint8Array(500001); await expect(bobsClient.sendMediaPayload(result.creationCookie, false, chunk, 0)).rejects.toThrow(); }); test('chunkCountNeedsToBeWithinLimit', async () => { await expect(bobCreateMediaStream(11)).rejects.toThrow(); }); test('clientCanOnlyPostToTheirOwnMediaStream', async () => { const result = await bobCreateMediaStream(10); const chunk = new Uint8Array(100); const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); await expect(alicesClient.sendMediaPayload(result.creationCookie, false, chunk, 5)).rejects.toThrow(); await alicesClient.stop(); }); test('clientCanOnlyPostToTheirOwnPublicMediaStream', async () => { const spaceId = makeUniqueSpaceStreamId(); const result = await bobCreateSpaceMediaStream(spaceId, 10); const chunk = new Uint8Array(100); const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); await expect(alicesClient.sendMediaPayload(result.creationCookie, false, chunk, 5)).rejects.toThrow(); await alicesClient.stop(); }); test('channelNeedsToExistBeforeCreatingMediaStream', async () => { const nonExistentSpaceId = makeUniqueSpaceStreamId(); const nonExistentChannelId = makeUniqueChannelStreamId(nonExistentSpaceId); await expect(bobsClient.createMediaStream(nonExistentChannelId, nonExistentSpaceId, undefined, 10)).rejects.toThrow(); }); test('dmChannelNeedsToExistBeforeCreatingMediaStream', async () => { const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); const nonExistentChannelId = makeDMStreamId(bobsClient.userId, alicesClient.userId); await expect(bobsClient.createMediaStream(nonExistentChannelId, undefined, undefined, 10)).rejects.toThrow(); await alicesClient.stop(); }); test('userCanUploadMediaToDmIfMember', async () => { const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); const { streamId } = await bobsClient.createDMChannel(alicesClient.userId); await expect(bobsClient.createMediaStream(streamId, undefined, undefined, 10)).resolves.not.toThrow(); await expect(alicesClient.createMediaStream(streamId, undefined, undefined, 10)).resolves.not.toThrow(); await alicesClient.stop(); }); test('userCanUploadMediaToGdmIfMember', async () => { const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); const charliesClient = await makeTestClient(); await charliesClient.initializeUser(); charliesClient.startSync(); const { streamId } = await bobsClient.createGDMChannel([ alicesClient.userId, charliesClient.userId, ]); await expect(bobsClient.createMediaStream(streamId, undefined, undefined, 10)).resolves.not.toThrow(); await alicesClient.stop(); await charliesClient.stop(); }); test('userCannotUploadMediaToDmUnlessMember', async () => { const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); const charliesClient = await makeTestClient(); await charliesClient.initializeUser(); charliesClient.startSync(); const { streamId } = await bobsClient.createDMChannel(alicesClient.userId); await expect(charliesClient.createMediaStream(streamId, undefined, undefined, 10)).rejects.toThrow(); await alicesClient.stop(); await charliesClient.stop(); }); // This test is flaky because there is a bug in GetStreamEx where sometimes the miniblock is not // finalized before the client tries to fetch it. This is a known issue, see HNT-5291. test.skip('mediaStreamGetStreamEx', async () => { const { creationCookie } = await bobCreateMediaStream(10); const streamId = streamIdAsString(creationCookie.streamId); // Send a series of media chunks await bobSendMediaPayloads(creationCookie, 10); // Force server to flush minipool events into a block await bobsClient.rpcClient.info(create(InfoRequestSchema, { debug: ['make_miniblock', streamId], }), { timeoutMs: 10000 }); // Grab stream from both endpoints const stream = await bobsClient.getStream(streamId); const streamEx = await bobsClient.getStreamEx(streamId); // Assert exact content equality with bobSendMediaPayloads expect(streamEx.mediaContent.info).toBeDefined(); expect(streamEx.mediaContent.info?.chunks.length).toEqual(10); for (let i = 0; i < 10; i++) { const chunk = new Uint8Array(100); chunk.fill(i, 0, 100); expect(streamEx.mediaContent.info?.chunks[i]).toBeDefined(); expect(streamEx.mediaContent.info?.chunks[i]).toEqual(chunk); } // Assert equality of mediaContent between getStream and getStreamEx // use-chunked-media.ts utilizes the tream.mediaContent.info property, so equality here // will result in the same behavior in the client app. expect(stream.mediaContent).toEqual(streamEx.mediaContent); }); test('userMediaStream', async () => { const alicesClient = await makeTestClient(); await alicesClient.initializeUser(); alicesClient.startSync(); await expect(alicesClient.createMediaStream(undefined, undefined, alicesClient.userId, 10)).resolves.not.toThrow(); }); }); //# sourceMappingURL=media.test.js.map