Reuse files on disk for outgoing messages

This commit is contained in:
trevor-signal
2026-02-23 15:35:11 -05:00
committed by GitHub
parent 491de86ad3
commit 2b243bb457
12 changed files with 410 additions and 111 deletions

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import * as sinon from 'sinon';
import * as Attachment from '../../util/Attachment.std.js';
import type {
@@ -28,6 +29,13 @@ const FAKE_LOCAL_ATTACHMENT: LocalAttachmentV2Type = {
};
describe('Attachment', () => {
let sandbox: sinon.SinonSandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
describe('getFileExtension', () => {
it('should return file extension from content type', () => {
const input: AttachmentType = fakeAttachment({
@@ -443,10 +451,16 @@ describe('Attachment', () => {
return FAKE_LOCAL_ATTACHMENT;
};
const actual = await migrateDataToFileSystem(input, {
writeNewAttachmentData,
logger,
});
const actual = await migrateDataToFileSystem(
input,
{
writeNewAttachmentData,
getExistingAttachmentDataForReuse: async () => null,
getPlaintextHashForInMemoryAttachment: () => 'fakeplaintextHash',
logger,
},
{ id: 'messageId' }
);
assert.deepEqual(actual, expected);
});
@@ -465,10 +479,16 @@ describe('Attachment', () => {
const writeNewAttachmentData = async () => FAKE_LOCAL_ATTACHMENT;
const actual = await migrateDataToFileSystem(input, {
writeNewAttachmentData,
logger,
});
const actual = await migrateDataToFileSystem(
input,
{
writeNewAttachmentData,
getExistingAttachmentDataForReuse: async () => null,
getPlaintextHashForInMemoryAttachment: () => 'fakeplaintextHash',
logger,
},
{ id: 'messageId' }
);
assert.deepEqual(actual, expected);
});
@@ -483,12 +503,60 @@ describe('Attachment', () => {
const writeNewAttachmentData = async () => FAKE_LOCAL_ATTACHMENT;
const actual = await migrateDataToFileSystem(input, {
writeNewAttachmentData,
logger,
});
const actual = await migrateDataToFileSystem(
input,
{
writeNewAttachmentData,
getExistingAttachmentDataForReuse: async () => null,
getPlaintextHashForInMemoryAttachment: () => 'fakeplaintextHash',
logger,
},
{ id: 'messageId' }
);
assert.isUndefined(actual.data);
});
it('should reuse existing data if exists', async () => {
const input = {
contentType: MIME.IMAGE_JPEG,
data: Bytes.fromString('Above us only sky'),
fileName: 'foo.jpg',
size: 1111,
};
const writeNewAttachmentData = sandbox.stub();
const actual = await migrateDataToFileSystem(
input,
{
writeNewAttachmentData,
getExistingAttachmentDataForReuse: async ({
plaintextHash,
contentType,
}) => {
assert.strictEqual(plaintextHash, 'somePlaintextHash');
assert.strictEqual(contentType, MIME.IMAGE_JPEG);
return {
path: 'new-path',
version: 2,
localKey: 'new-local-key',
};
},
getPlaintextHashForInMemoryAttachment: () => 'somePlaintextHash',
logger,
},
{ id: 'messageId' }
);
assert.strictEqual(writeNewAttachmentData.callCount, 0);
assert.deepEqual(actual, {
version: 2,
size: 1111,
plaintextHash: 'somePlaintextHash',
contentType: MIME.IMAGE_JPEG,
path: 'new-path',
localKey: 'new-local-key',
fileName: 'foo.jpg',
});
});
});
});

View File

@@ -24,10 +24,6 @@ const logger = createLogger('EmbeddedContact_test');
describe('Contact', () => {
const NUMBER = '+12025550099';
const writeNewAttachmentData = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const getDefaultMessageAttrs = (): Pick<
MessageAttributesType,
| 'id'
@@ -244,6 +240,16 @@ describe('Contact', () => {
});
describe('parseAndWriteAvatar', () => {
const defaultContext = {
logger,
getRegionCode: () => '1',
writeNewAttachmentData: sinon
.stub()
.throws(new Error("Shouldn't be called")),
getPlaintextHashForInMemoryAttachment: sinon.stub(),
getExistingAttachmentDataForReuse: sinon.stub(),
};
it('handles message with no avatar in contact', async () => {
const upgradeAttachment = sinon
.stub()
@@ -268,11 +274,7 @@ describe('Contact', () => {
};
const result = await upgradeVersion(
message.contact[0],
{
logger,
getRegionCode: () => '1',
writeNewAttachmentData,
},
defaultContext,
message
);
assert.deepEqual(result, message.contact[0]);
@@ -314,9 +316,8 @@ describe('Contact', () => {
const result = await upgradeVersion(
message.contact[0],
{
...defaultContext,
getRegionCode: () => 'US',
logger,
writeNewAttachmentData,
},
message
);
@@ -361,11 +362,7 @@ describe('Contact', () => {
};
const result = await upgradeVersion(
message.contact[0],
{
getRegionCode: () => '1',
writeNewAttachmentData,
logger,
},
defaultContext,
message
);
assert.deepEqual(result, expected);
@@ -449,11 +446,7 @@ describe('Contact', () => {
const result = await upgradeVersion(
message.contact[0],
{
getRegionCode: () => '1',
writeNewAttachmentData,
logger,
},
defaultContext,
message
);
assert.deepEqual(result, expected);
@@ -499,11 +492,7 @@ describe('Contact', () => {
};
const result = await upgradeVersion(
message.contact[0],
{
getRegionCode: () => '1',
writeNewAttachmentData,
logger,
},
defaultContext,
message
);
assert.deepEqual(result, expected);
@@ -549,11 +538,7 @@ describe('Contact', () => {
};
const result = await upgradeVersion(
message.contact[0],
{
getRegionCode: () => '1',
writeNewAttachmentData,
logger,
},
defaultContext,
message
);
assert.deepEqual(result, expected);
@@ -595,11 +580,7 @@ describe('Contact', () => {
};
const result = await upgradeVersion(
message.contact[0],
{
getRegionCode: () => '1',
writeNewAttachmentData,
logger,
},
defaultContext,
message
);
assert.deepEqual(result, expected);
@@ -627,11 +608,7 @@ describe('Contact', () => {
};
const result = await upgradeVersion(
message.contact[0],
{
getRegionCode: () => '1',
writeNewAttachmentData,
logger,
},
defaultContext,
message
);
assert.deepEqual(result, message.contact[0]);

View File

@@ -17,6 +17,7 @@ import type {
LocalAttachmentV2Type,
} from '../../types/Attachment.std.js';
import type { LoggerType } from '../../types/Logging.std.js';
import { testPlaintextHash } from '../../test-helpers/attachments.node.js';
const FAKE_LOCAL_ATTACHMENT: LocalAttachmentV2Type = {
version: 2,
@@ -55,6 +56,7 @@ describe('Message', () => {
props?: Partial<Message.ContextType>
): Message.ContextType {
return {
getExistingAttachmentDataForReuse: () => Promise.resolve(null),
getImageDimensions: async (_params: {
objectUrl: string;
logger: LoggerType;
@@ -62,6 +64,7 @@ describe('Message', () => {
width: 10,
height: 20,
}),
getPlaintextHashForInMemoryAttachment: () => testPlaintextHash(),
doesAttachmentExist: async () => true,
getRegionCode: () => 'region-code',
logger,