signal-desktop/ts/test-electron/backup/integration_test.ts
2024-10-31 10:01:03 -07:00

110 lines
3.2 KiB
TypeScript

// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { readdirSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { basename, join } from 'node:path';
import { Readable } from 'node:stream';
import { BackupLevel } from '@signalapp/libsignal-client/zkgroup';
import { InputStream } from '@signalapp/libsignal-client/dist/io';
import {
ComparableBackup,
Purpose,
} from '@signalapp/libsignal-client/dist/MessageBackup';
import { assert } from 'chai';
import { clearData } from './helpers';
import { loadAllAndReinitializeRedux } from '../../services/allLoaders';
import { backupsService, BackupType } from '../../services/backups';
import { initialize as initializeExpiringMessageService } from '../../services/expiringMessagesDeletion';
import { singleProtoJobQueue } from '../../jobs/singleProtoJobQueue';
import { DataWriter } from '../../sql/Client';
const { BACKUP_INTEGRATION_DIR } = process.env;
class MemoryStream extends InputStream {
private offset = 0;
constructor(private readonly buffer: Buffer) {
super();
}
public override async read(amount: number): Promise<Buffer> {
const result = this.buffer.slice(this.offset, this.offset + amount);
this.offset += amount;
return result;
}
public override async skip(amount: number): Promise<void> {
this.offset += amount;
}
}
describe('backup/integration', () => {
before(async () => {
await initializeExpiringMessageService(singleProtoJobQueue);
});
beforeEach(async () => {
await clearData();
await loadAllAndReinitializeRedux();
});
afterEach(async () => {
await DataWriter.removeAll();
});
if (!BACKUP_INTEGRATION_DIR) {
return;
}
const files = readdirSync(BACKUP_INTEGRATION_DIR)
.filter(file => file.endsWith('.binproto'))
.map(file => join(BACKUP_INTEGRATION_DIR, file));
if (files.length === 0) {
it('no backup tests', () => {
throw new Error('No backup integration tests');
});
}
for (const fullPath of files) {
it(basename(fullPath), async () => {
const expectedBuffer = await readFile(fullPath);
await backupsService.importBackup(() => Readable.from([expectedBuffer]), {
backupType: BackupType.TestOnlyPlaintext,
});
const exported = await backupsService.exportBackupData(
BackupLevel.Paid,
BackupType.TestOnlyPlaintext
);
const actualStream = new MemoryStream(Buffer.from(exported));
const expectedStream = new MemoryStream(expectedBuffer);
const actual = await ComparableBackup.fromUnencrypted(
Purpose.RemoteBackup,
actualStream,
BigInt(exported.byteLength)
);
const expected = await ComparableBackup.fromUnencrypted(
Purpose.RemoteBackup,
expectedStream,
BigInt(expectedBuffer.byteLength)
);
const actualString = actual.comparableString();
const expectedString = expected.comparableString();
if (expectedString.includes('ReleaseChannelDonationRequest')) {
// Skip the unsupported tests
return;
}
// We need "deep*" for fancy diffs
assert.deepStrictEqual(actualString, expectedString);
});
}
});