Backup and end-to-end test!

This commit is contained in:
Scott Nonnenberg 2018-04-20 14:55:33 -07:00
parent c3acf43c47
commit a7d44d3344
No known key found for this signature in database
GPG key ID: 5F82280C35134661
6 changed files with 477 additions and 28 deletions

View file

@ -1,5 +1,8 @@
/* global Signal: false */
/* global Whisper: false */
/* global assert: false */
/* global textsecure: false */
/* global _: false */
'use strict';
@ -223,4 +226,270 @@ describe('Backup', () => {
);
});
});
describe('end-to-end', () => {
it('exports then imports to produce the same data we started with', async () => {
const {
attachmentsPath,
fs,
fse,
glob,
path,
tmp,
} = window.test;
const {
upgradeMessageSchema,
loadAttachmentData,
} = window.Signal.Migrations;
const key = new Uint8Array([
1, 3, 4, 5, 6, 7, 8, 11,
23, 34, 1, 34, 3, 5, 45, 45,
1, 3, 4, 5, 6, 7, 8, 11,
23, 34, 1, 34, 3, 5, 45, 45,
]);
const attachmentsPattern = path.join(attachmentsPath, '**');
const OUR_NUMBER = '+12025550000';
const CONTACT_ONE_NUMBER = '+12025550001';
async function wrappedLoadAttachment(attachment) {
return _.omit(await loadAttachmentData(attachment), ['path']);
}
async function clearAllData() {
await textsecure.storage.protocol.removeAllData();
await fse.emptyDir(attachmentsPath);
}
function removeId(model) {
return _.omit(model, ['id']);
}
const twoSlashes = /.*\/.*\/.*/;
function removeDirs(dirs) {
return _.filter(dirs, (fullDir) => {
const dir = fullDir.replace(attachmentsPath, '');
return twoSlashes.test(dir);
});
}
function _mapQuotedAttachments(mapper) {
return async (message, context) => {
if (!message.quote) {
return message;
}
const wrappedMapper = async (attachment) => {
if (!attachment || !attachment.thumbnail) {
return attachment;
}
return Object.assign({}, attachment, {
thumbnail: await mapper(attachment.thumbnail, context),
});
};
const quotedAttachments = (message.quote && message.quote.attachments) || [];
return Object.assign({}, message, {
quote: Object.assign({}, message.quote, {
attachments: await Promise.all(quotedAttachments.map(wrappedMapper)),
}),
});
};
}
async function loadAllFilesFromDisk(message) {
const loadThumbnails = _mapQuotedAttachments((thumbnail) => {
// we want to be bulletproof to thumbnails without data
if (!thumbnail.path) {
return thumbnail;
}
return wrappedLoadAttachment(thumbnail);
});
const promises = (message.attachments || []).map(attachment =>
wrappedLoadAttachment(attachment));
return Object.assign(
{},
await loadThumbnails(message),
{
attachments: await Promise.all(promises),
}
);
}
let backupDir;
try {
const ATTACHMENT_COUNT = 2;
const MESSAGE_COUNT = 1;
const CONVERSATION_COUNT = 1;
const messageWithAttachments = {
conversationId: CONTACT_ONE_NUMBER,
body: 'Totally!',
source: OUR_NUMBER,
received_at: 1524185933350,
timestamp: 1524185933350,
errors: [],
attachments: [{
contentType: 'image/gif',
fileName: 'sad_cat.gif',
data: new Uint8Array([
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
]).buffer,
}],
quote: {
text: "Isn't it cute?",
author: CONTACT_ONE_NUMBER,
id: 12345678,
attachments: [{
contentType: 'audio/mp3',
fileName: 'song.mp3',
}, {
contentType: 'image/gif',
fileName: 'happy_cat.gif',
thumbnail: {
contentType: 'image/png',
data: new Uint8Array([
2, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
]).buffer,
},
}],
},
};
console.log('Backup test: Clear all data');
await clearAllData();
console.log('Backup test: Create models, save to db/disk');
const message = await upgradeMessageSchema(messageWithAttachments);
console.log({ message });
const messageModel = new Whisper.Message(message);
await window.wrapDeferred(messageModel.save());
const conversation = {
active_at: 1524185933350,
color: 'orange',
expireTimer: 0,
id: CONTACT_ONE_NUMBER,
lastMessage: 'Heyo!',
name: 'Someone Somewhere',
profileAvatar: {
contentType: 'image/jpeg',
data: new Uint8Array([
3, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
]).buffer,
size: 64,
},
profileKey: new Uint8Array([
4, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
1, 2, 3, 4, 5, 6, 7, 8,
]).buffer,
profileName: 'Someone! 🤔',
profileSharing: true,
timestamp: 1524185933350,
tokens: [
'someone somewhere',
'someone',
'somewhere',
'2025550001',
'12025550001',
],
type: 'private',
unreadCount: 0,
verified: 0,
};
console.log({ conversation });
const conversationModel = new Whisper.Conversation(conversation);
await window.wrapDeferred(conversationModel.save());
console.log('Backup test: Ensure that all attachments were saved to disk');
const attachmentFiles = removeDirs(glob.sync(attachmentsPattern));
console.log({ attachmentFiles });
assert.strictEqual(ATTACHMENT_COUNT, attachmentFiles.length);
console.log('Backup test: Export!');
backupDir = tmp.dirSync().name;
console.log({ backupDir });
await Signal.Backup.exportToDirectory(backupDir, { key });
console.log('Backup test: Ensure that messages.zip exists');
const zipPath = path.join(backupDir, 'messages.zip');
const messageZipExists = fs.existsSync(zipPath);
assert.strictEqual(true, messageZipExists);
console.log('Backup test: Ensure that all attachments made it to backup dir');
const backupAttachmentPattern = path.join(backupDir, 'attachments/*');
const backupAttachments = glob.sync(backupAttachmentPattern);
console.log({ backupAttachments });
assert.strictEqual(ATTACHMENT_COUNT, backupAttachments.length);
console.log('Backup test: Clear all data');
await clearAllData();
console.log('Backup test: Import!');
await Signal.Backup.importFromDirectory(backupDir, { key });
console.log('Backup test: ensure that all attachments were imported');
const recreatedAttachmentFiles = removeDirs(glob.sync(attachmentsPattern));
console.log({ recreatedAttachmentFiles });
assert.strictEqual(ATTACHMENT_COUNT, recreatedAttachmentFiles.length);
assert.deepEqual(attachmentFiles, recreatedAttachmentFiles);
console.log('Backup test: Check messages');
const messageCollection = new Whisper.MessageCollection();
await window.wrapDeferred(messageCollection.fetch());
assert.strictEqual(messageCollection.length, MESSAGE_COUNT);
const messageFromDB = removeId(messageCollection.at(0).attributes);
console.log({ messageFromDB, message });
assert.deepEqual(messageFromDB, message);
console.log('Backup test: check that all attachments were successfully imported');
const messageWithAttachmentsFromDB = await loadAllFilesFromDisk(messageFromDB);
console.log({ messageWithAttachmentsFromDB, messageWithAttachments });
assert.deepEqual(
_.omit(messageWithAttachmentsFromDB, ['schemaVersion']),
messageWithAttachments
);
console.log('Backup test: check conversations');
const conversationCollection = new Whisper.ConversationCollection();
await window.wrapDeferred(conversationCollection.fetch());
assert.strictEqual(conversationCollection.length, CONVERSATION_COUNT);
const conversationFromDB = conversationCollection.at(0).attributes;
console.log({ conversationFromDB, conversation });
assert.deepEqual(
conversationFromDB,
_.omit(conversation, ['profileAvatar'])
);
console.log('Backup test: Clear all data');
await clearAllData();
console.log('Backup test: Complete!');
} finally {
if (backupDir) {
console.log({ backupDir });
console.log('Deleting', backupDir);
await fse.remove(backupDir);
}
}
});
});
});