215 lines
6.1 KiB
TypeScript
215 lines
6.1 KiB
TypeScript
// Copyright 2024 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import createDebug from 'debug';
|
|
import { assert } from 'chai';
|
|
import { expect } from 'playwright/test';
|
|
import { type PrimaryDevice, StorageState } from '@signalapp/mock-server';
|
|
import * as path from 'node:path';
|
|
import { readFile } from 'node:fs/promises';
|
|
|
|
import type { App } from '../playwright.js';
|
|
import { Bootstrap } from '../bootstrap.js';
|
|
import {
|
|
getMessageInTimelineByTimestamp,
|
|
getTimelineMessageWithText,
|
|
sendMessageWithAttachments,
|
|
sendTextMessage,
|
|
} from '../helpers.js';
|
|
import * as durations from '../../util/durations/index.js';
|
|
import { strictAssert } from '../../util/assert.js';
|
|
import { VIDEO_MP4 } from '../../types/MIME.js';
|
|
import { toBase64 } from '../../Bytes.js';
|
|
|
|
export const debug = createDebug('mock:test:attachments');
|
|
|
|
const CAT_PATH = path.join(
|
|
__dirname,
|
|
'..',
|
|
'..',
|
|
'..',
|
|
'fixtures',
|
|
'cat-screenshot.png'
|
|
);
|
|
const VIDEO_PATH = path.join(
|
|
__dirname,
|
|
'..',
|
|
'..',
|
|
'..',
|
|
'fixtures',
|
|
'ghost-kitty.mp4'
|
|
);
|
|
|
|
describe('attachments', function (this: Mocha.Suite) {
|
|
this.timeout(durations.MINUTE);
|
|
|
|
let bootstrap: Bootstrap;
|
|
let app: App;
|
|
let pinned: PrimaryDevice;
|
|
|
|
beforeEach(async () => {
|
|
bootstrap = new Bootstrap();
|
|
await bootstrap.init();
|
|
|
|
let state = StorageState.getEmpty();
|
|
|
|
const { phone, contacts } = bootstrap;
|
|
[pinned] = contacts;
|
|
|
|
state = state.addContact(pinned, {
|
|
identityKey: pinned.publicKey.serialize(),
|
|
profileKey: pinned.profileKey.serialize(),
|
|
whitelisted: true,
|
|
});
|
|
|
|
state = state.pin(pinned);
|
|
await phone.setStorageState(state);
|
|
|
|
app = await bootstrap.link();
|
|
});
|
|
|
|
afterEach(async function (this: Mocha.Context) {
|
|
if (!bootstrap) {
|
|
return;
|
|
}
|
|
|
|
await bootstrap.maybeSaveLogs(this.currentTest, app);
|
|
await app.close();
|
|
await bootstrap.teardown();
|
|
});
|
|
|
|
it('can upload attachment to CDN3 and download incoming attachment', async () => {
|
|
const page = await app.getWindow();
|
|
|
|
await page.getByTestId(pinned.device.aci).click();
|
|
|
|
const [attachmentCat] = await sendMessageWithAttachments(
|
|
page,
|
|
pinned,
|
|
'This is my cat',
|
|
[CAT_PATH]
|
|
);
|
|
|
|
const Message = getTimelineMessageWithText(page, 'This is my cat');
|
|
const MessageSent = Message.locator(
|
|
'.module-message__metadata__status-icon--sent'
|
|
);
|
|
|
|
debug('waiting for send');
|
|
await MessageSent.waitFor();
|
|
const timestamp = await Message.getAttribute('data-testid');
|
|
strictAssert(timestamp, 'timestamp must exist');
|
|
|
|
const sentMessage = (
|
|
await app.getMessagesBySentAt(parseInt(timestamp, 10))
|
|
)[0];
|
|
strictAssert(sentMessage, 'message exists in DB');
|
|
const sentAttachment = sentMessage.attachments?.[0];
|
|
|
|
// For this test, just send back the same attachment that was uploaded to test a
|
|
// round-trip
|
|
const incomingTimestamp = Date.now();
|
|
await sendTextMessage({
|
|
from: pinned,
|
|
to: bootstrap.desktop,
|
|
desktop: bootstrap.desktop,
|
|
text: 'Wait, that is MY cat!',
|
|
attachments: [attachmentCat],
|
|
timestamp: incomingTimestamp,
|
|
});
|
|
|
|
await expect(
|
|
getMessageInTimelineByTimestamp(page, incomingTimestamp).locator(
|
|
'img.module-image__image'
|
|
)
|
|
).toBeVisible();
|
|
|
|
const incomingMessage = (
|
|
await app.getMessagesBySentAt(incomingTimestamp)
|
|
)[0];
|
|
strictAssert(incomingMessage, 'message exists in DB');
|
|
const incomingAttachment = incomingMessage.attachments?.[0];
|
|
|
|
assert.strictEqual(incomingAttachment?.key, sentAttachment?.key);
|
|
assert.strictEqual(incomingAttachment?.digest, sentAttachment?.digest);
|
|
});
|
|
|
|
it('can download videos with incrementalMac and is resilient to bad incrementalMacs', async () => {
|
|
const { desktop } = bootstrap;
|
|
const page = await app.getWindow();
|
|
|
|
await page.getByTestId(pinned.device.aci).click();
|
|
|
|
const plaintextVideo = await readFile(VIDEO_PATH);
|
|
const videoPointer1 = await bootstrap.encryptAndStoreAttachmentOnCDN(
|
|
plaintextVideo,
|
|
VIDEO_MP4
|
|
);
|
|
const videoPointer2 = await bootstrap.encryptAndStoreAttachmentOnCDN(
|
|
plaintextVideo,
|
|
VIDEO_MP4
|
|
);
|
|
|
|
const incrementalTimestamp = Date.now();
|
|
const badIncrementalTimestamp = incrementalTimestamp + 1;
|
|
|
|
await sendTextMessage({
|
|
from: pinned,
|
|
to: desktop,
|
|
desktop,
|
|
text: 'video with good incrementalMac',
|
|
attachments: [videoPointer1],
|
|
timestamp: incrementalTimestamp,
|
|
});
|
|
await sendTextMessage({
|
|
from: pinned,
|
|
to: desktop,
|
|
desktop,
|
|
text: 'video with bad incrementalMac',
|
|
attachments: [
|
|
{ ...videoPointer2, chunkSize: (videoPointer2.chunkSize ?? 42) + 1 },
|
|
],
|
|
timestamp: badIncrementalTimestamp,
|
|
});
|
|
|
|
await expect(
|
|
getMessageInTimelineByTimestamp(page, incrementalTimestamp).locator(
|
|
'img.module-image__image'
|
|
)
|
|
).toBeVisible();
|
|
await expect(
|
|
getMessageInTimelineByTimestamp(page, badIncrementalTimestamp).locator(
|
|
'img.module-image__image'
|
|
)
|
|
).toBeVisible();
|
|
|
|
// goodIncrementalMac preserved
|
|
{
|
|
const messageInDB = (
|
|
await app.getMessagesBySentAt(incrementalTimestamp)
|
|
)[0];
|
|
strictAssert(messageInDB, 'message exists in DB');
|
|
const attachmentInDB = messageInDB.attachments?.[0];
|
|
strictAssert(videoPointer1.incrementalMac, 'must exist');
|
|
strictAssert(videoPointer1.chunkSize, 'must exist');
|
|
assert.strictEqual(
|
|
attachmentInDB?.incrementalMac,
|
|
toBase64(videoPointer1.incrementalMac)
|
|
);
|
|
assert.strictEqual(attachmentInDB?.chunkSize, videoPointer1.chunkSize);
|
|
}
|
|
|
|
// badIncrementalMac removed
|
|
{
|
|
const messageInDB = (
|
|
await app.getMessagesBySentAt(badIncrementalTimestamp)
|
|
)[0];
|
|
strictAssert(messageInDB, 'message exists in DB');
|
|
const attachmentInDB = messageInDB.attachments?.[0];
|
|
strictAssert(videoPointer2.incrementalMac, 'must exist');
|
|
strictAssert(videoPointer2.chunkSize, 'must exist');
|
|
assert.strictEqual(attachmentInDB?.incrementalMac, undefined);
|
|
assert.strictEqual(attachmentInDB?.chunkSize, undefined);
|
|
}
|
|
});
|
|
});
|