From 6859b1a220e72cff4d09347fa3ec3c334dc06243 Mon Sep 17 00:00:00 2001 From: automated-signal <37887102+automated-signal@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:15:05 -0500 Subject: [PATCH] Export/import attachments in integration tests Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> --- _locales/en/messages.json | 4 + package-lock.json | 8 +- package.json | 2 +- ts/AttachmentCrypto.ts | 16 ++- ts/CI.ts | 2 + ts/background.ts | 3 +- .../InstallScreenBackupImportStep.stories.tsx | 12 ++ .../InstallScreenBackupImportStep.tsx | 30 +++-- ts/jobs/AttachmentBackupManager.ts | 4 + ts/jobs/JobManager.ts | 16 +++ ts/services/backups/index.ts | 71 +++++++++-- ts/state/ducks/installer.ts | 5 + ts/state/smart/InstallScreen.tsx | 1 + ts/test-electron/backup/integration_test.ts | 7 +- .../services/AttachmentBackupManager_test.ts | 1 - .../ensureAttachmentIsReencryptable_test.ts | 2 - ts/test-mock/backups/backups_test.ts | 113 +++++++++++++----- ts/test-mock/bootstrap.ts | 41 ++++++- ts/test-mock/messaging/attachments_test.ts | 57 +++------ ts/types/InstallScreen.ts | 5 + ts/util/ensureAttachmentIsReencryptable.ts | 8 -- 21 files changed, 292 insertions(+), 116 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 88ef501c837..743aff29183 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -4699,6 +4699,10 @@ "messageformat": "Downloading {currentSize} of {totalSize} ({fractionComplete, number, percent})...", "description": "Hint under the progressbar in the backup import screen" }, + "icu:BackupImportScreen__progressbar-hint--processing": { + "messageformat": "Finalizing message transfer...", + "description": "Hint under the progressbar in the backup import screen shown after the backup file is downloaded and while backup data is being processed" + }, "icu:BackupImportScreen__progressbar-hint--preparing": { "messageformat": "Preparing to download...", "description": "Hint under the progressbar in the backup import screen when download size is not yet known" diff --git a/package-lock.json b/package-lock.json index a1da399f71b..7381829ecaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -126,7 +126,7 @@ "@indutny/parallel-prettier": "3.0.0", "@indutny/rezip-electron": "1.3.1", "@indutny/symbolicate-mac": "2.3.0", - "@signalapp/mock-server": "8.0.1", + "@signalapp/mock-server": "8.1.1", "@storybook/addon-a11y": "8.1.11", "@storybook/addon-actions": "8.1.11", "@storybook/addon-controls": "8.1.11", @@ -7296,9 +7296,9 @@ } }, "node_modules/@signalapp/mock-server": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@signalapp/mock-server/-/mock-server-8.0.1.tgz", - "integrity": "sha512-qfyBOtMmQ3RF3Kig0DTafrxUx8MZ2hB+5H6ZJVV1lQS022U6bOHiVjZyAJ0uZgU98FJZIXlT/zWJ24kFl6/pGQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@signalapp/mock-server/-/mock-server-8.1.1.tgz", + "integrity": "sha512-TlQpOyUYnDBV7boxyaLDaeGTN5WIn4trbF+9rq4+6rXfpzIBnf2A4Y1fzFRVL9F9/F4ZEPtrv+V3oplNrfoZ9w==", "dev": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 8e550412d69..bd310c94a34 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "@indutny/parallel-prettier": "3.0.0", "@indutny/rezip-electron": "1.3.1", "@indutny/symbolicate-mac": "2.3.0", - "@signalapp/mock-server": "8.0.1", + "@signalapp/mock-server": "8.1.1", "@storybook/addon-a11y": "8.1.11", "@storybook/addon-actions": "8.1.11", "@storybook/addon-controls": "8.1.11", diff --git a/ts/AttachmentCrypto.ts b/ts/AttachmentCrypto.ts index cf71c70c4ef..1979e6ecaa6 100644 --- a/ts/AttachmentCrypto.ts +++ b/ts/AttachmentCrypto.ts @@ -110,17 +110,22 @@ export type HardcodedIVForEncryptionType = digestToMatch: Uint8Array; }; -type EncryptAttachmentV2PropsType = { +type EncryptAttachmentV2OptionsType = Readonly<{ dangerousIv?: HardcodedIVForEncryptionType; dangerousTestOnlySkipPadding?: boolean; - getAbsoluteAttachmentPath: (relativePath: string) => string; keys: Readonly; needIncrementalMac: boolean; plaintext: PlaintextSourceType; -}; +}>; + +export type EncryptAttachmentV2ToDiskOptionsType = + EncryptAttachmentV2OptionsType & + Readonly<{ + getAbsoluteAttachmentPath: (relativePath: string) => string; + }>; export async function encryptAttachmentV2ToDisk( - args: EncryptAttachmentV2PropsType + args: EncryptAttachmentV2ToDiskOptionsType ): Promise { // Create random output file const relativeTargetPath = getRelativePath(createName()); @@ -152,7 +157,7 @@ export async function encryptAttachmentV2({ needIncrementalMac, plaintext, sink, -}: EncryptAttachmentV2PropsType & { +}: EncryptAttachmentV2OptionsType & { sink?: Writable; }): Promise { const logId = 'encryptAttachmentV2'; @@ -580,7 +585,6 @@ export async function decryptAndReencryptLocally( const [result] = await Promise.all([ decryptAttachmentV2ToSink(options, passthrough), await encryptAttachmentV2({ - getAbsoluteAttachmentPath: options.getAbsoluteAttachmentPath, keys, needIncrementalMac: false, plaintext: { diff --git a/ts/CI.ts b/ts/CI.ts index f6994bd75be..34ba2cf4a4e 100644 --- a/ts/CI.ts +++ b/ts/CI.ts @@ -10,6 +10,7 @@ import * as log from './logging/log'; import { explodePromise } from './util/explodePromise'; import { AccessType, ipcInvoke } from './sql/channels'; import { backupsService } from './services/backups'; +import { AttachmentBackupManager } from './jobs/AttachmentBackupManager'; import { SECOND } from './util/durations'; import { isSignalRoute } from './util/signalRoutes'; import { strictAssert } from './util/assert'; @@ -168,6 +169,7 @@ export function getCI({ deviceName }: GetCIOptionsType): CIType { async function uploadBackup() { await backupsService.upload(); + await AttachmentBackupManager.waitForIdle(); } function unlink() { diff --git a/ts/background.ts b/ts/background.ts index a46bc69620e..795412a3db8 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -1604,8 +1604,9 @@ export async function startApp(): Promise { // Download backup before enabling request handler and storage service try { await backupsService.download({ - onProgress: (currentBytes, totalBytes) => { + onProgress: (backupStep, currentBytes, totalBytes) => { window.reduxActions.installer.updateBackupImportProgress({ + backupStep, currentBytes, totalBytes, }); diff --git a/ts/components/installScreen/InstallScreenBackupImportStep.stories.tsx b/ts/components/installScreen/InstallScreenBackupImportStep.stories.tsx index 92c2dd25918..6e2eaa806a9 100644 --- a/ts/components/installScreen/InstallScreenBackupImportStep.stories.tsx +++ b/ts/components/installScreen/InstallScreenBackupImportStep.stories.tsx @@ -5,6 +5,7 @@ import React from 'react'; import type { Meta, StoryFn } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { setupI18n } from '../../util/setupI18n'; +import { InstallScreenBackupStep } from '../../types/InstallScreen'; import enMessages from '../../../_locales/en/messages.json'; import type { PropsType } from './InstallScreenBackupImportStep'; import { InstallScreenBackupImportStep } from './InstallScreenBackupImportStep'; @@ -27,25 +28,36 @@ const Template: StoryFn = (args: PropsType) => ( export const NoBytes = Template.bind({}); NoBytes.args = { + backupStep: InstallScreenBackupStep.Download, currentBytes: undefined, totalBytes: undefined, }; export const Bytes = Template.bind({}); Bytes.args = { + backupStep: InstallScreenBackupStep.Download, currentBytes: 500 * 1024, totalBytes: 1024 * 1024, }; export const Full = Template.bind({}); Full.args = { + backupStep: InstallScreenBackupStep.Download, currentBytes: 1024, totalBytes: 1024, }; export const Error = Template.bind({}); Error.args = { + backupStep: InstallScreenBackupStep.Download, currentBytes: 500 * 1024, totalBytes: 1024 * 1024, hasError: true, }; + +export const Processing = Template.bind({}); +Processing.args = { + backupStep: InstallScreenBackupStep.Process, + currentBytes: 500 * 1024, + totalBytes: 1024 * 1024, +}; diff --git a/ts/components/installScreen/InstallScreenBackupImportStep.tsx b/ts/components/installScreen/InstallScreenBackupImportStep.tsx index 2c848c34f5d..71650e60abb 100644 --- a/ts/components/installScreen/InstallScreenBackupImportStep.tsx +++ b/ts/components/installScreen/InstallScreenBackupImportStep.tsx @@ -4,18 +4,21 @@ import React, { useState, useCallback } from 'react'; import type { LocalizerType } from '../../types/Util'; +import { InstallScreenBackupStep } from '../../types/InstallScreen'; import { formatFileSize } from '../../util/formatFileSize'; import { TitlebarDragArea } from '../TitlebarDragArea'; import { ProgressBar } from '../ProgressBar'; import { ConfirmationDialog } from '../ConfirmationDialog'; import { InstallScreenSignalLogo } from './InstallScreenSignalLogo'; import { roundFractionForProgressBar } from '../../util/numbers'; +import { missingCaseError } from '../../util/missingCaseError'; // We can't always use destructuring assignment because of the complexity of this props // type. export type PropsType = Readonly<{ i18n: LocalizerType; + backupStep: InstallScreenBackupStep; currentBytes?: number; totalBytes?: number; hasError?: boolean; @@ -25,6 +28,7 @@ export type PropsType = Readonly<{ export function InstallScreenBackupImportStep({ i18n, + backupStep, currentBytes, totalBytes, hasError, @@ -66,26 +70,33 @@ export function InstallScreenBackupImportStep({ }, [onRetry]); let progress: JSX.Element; - let isCancelPossible = true; if (currentBytes != null && totalBytes != null) { - isCancelPossible = currentBytes !== totalBytes; - const fractionComplete = roundFractionForProgressBar( currentBytes / totalBytes ); + let hint: string; + if (backupStep === InstallScreenBackupStep.Download) { + hint = i18n('icu:BackupImportScreen__progressbar-hint', { + currentSize: formatFileSize(currentBytes), + totalSize: formatFileSize(totalBytes), + fractionComplete, + }); + } else if (backupStep === InstallScreenBackupStep.Process) { + hint = i18n('icu:BackupImportScreen__progressbar-hint--processing'); + } else { + throw missingCaseError(backupStep); + } + progress = ( <>
- {i18n('icu:BackupImportScreen__progressbar-hint', { - currentSize: formatFileSize(currentBytes), - totalSize: formatFileSize(totalBytes), - fractionComplete, - })} + {hint}
); @@ -93,6 +104,7 @@ export function InstallScreenBackupImportStep({ progress = ( <> @@ -119,7 +131,7 @@ export function InstallScreenBackupImportStep({ - {isCancelPossible && ( + {backupStep === InstallScreenBackupStep.Download && (