mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
support conventional uploads
This commit is contained in:
@@ -99,6 +99,9 @@ describe('/upload', () => {
|
||||
visibility: 'timeline',
|
||||
}),
|
||||
);
|
||||
const downloaded = await utils.downloadAsset(admin.accessToken, body.id);
|
||||
expect(downloaded.size).toBe(content.byteLength);
|
||||
expect(content.compare(await downloaded.bytes())).toBe(0);
|
||||
});
|
||||
|
||||
it('should create a complete upload with Upload-Incomplete: ?0 if version is 3', async () => {
|
||||
@@ -136,6 +139,99 @@ describe('/upload', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should support conventional upload', async () => {
|
||||
const content = randomBytes(1024);
|
||||
|
||||
const checksum = createHash('sha1').update(content).digest('base64');
|
||||
const { status, headers, body } = await request(app)
|
||||
.post('/upload')
|
||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||
.set('X-Immich-Asset-Data', assetData)
|
||||
.set('Repr-Digest', `sha=:${checksum}:`)
|
||||
.set('Content-Type', 'image/jpeg')
|
||||
.set('Upload-Length', '1024')
|
||||
.send(content);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(headers['upload-complete']).toBeUndefined();
|
||||
expect(headers['upload-incomplete']).toBeUndefined();
|
||||
expect(headers['location']).toBeUndefined();
|
||||
expect(body).toEqual(expect.objectContaining({ id: expect.any(String) }));
|
||||
|
||||
const asset = await utils.getAssetInfo(user.accessToken, body.id);
|
||||
expect(asset).toEqual(
|
||||
expect.objectContaining({
|
||||
id: body.id,
|
||||
checksum,
|
||||
ownerId: user.userId,
|
||||
exifInfo: expect.objectContaining({ fileSizeInByte: content.byteLength }),
|
||||
originalFileName: 'test-image.jpg',
|
||||
deviceAssetId: 'rufh',
|
||||
deviceId: 'test',
|
||||
isFavorite: true,
|
||||
visibility: 'timeline',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('overwrite partial duplicate if conventional upload', { timeout: 1000 }, async () => {
|
||||
const content = randomBytes(10240);
|
||||
|
||||
const checksum = createHash('sha1').update(content).digest('base64');
|
||||
|
||||
// simulate interrupted upload by starting a request and not completing it
|
||||
const req = httpRequest({
|
||||
hostname: 'localhost',
|
||||
port: 2285,
|
||||
path: '/upload',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${user.accessToken}`,
|
||||
'X-Immich-Asset-Data': assetData,
|
||||
'Repr-Digest': `sha=:${checksum}:`,
|
||||
'Upload-Length': '1024',
|
||||
'Content-Length': '1024',
|
||||
'Content-Type': 'image/jpeg',
|
||||
},
|
||||
});
|
||||
req.write(content.subarray(0, 512));
|
||||
|
||||
await setTimeout(50);
|
||||
|
||||
const { status, headers, body } = await request(app)
|
||||
.post('/upload')
|
||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||
.set('X-Immich-Asset-Data', assetData)
|
||||
.set('Repr-Digest', `sha=:${checksum}:`)
|
||||
.set('Content-Type', 'image/jpeg')
|
||||
.set('Upload-Length', '10240')
|
||||
.send(content);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(headers['upload-complete']).toBeUndefined();
|
||||
expect(headers['upload-incomplete']).toBeUndefined();
|
||||
expect(headers['location']).toBeUndefined();
|
||||
expect(body).toEqual(expect.objectContaining({ id: expect.any(String) }));
|
||||
|
||||
const asset = await utils.getAssetInfo(user.accessToken, body.id);
|
||||
expect(asset).toEqual(
|
||||
expect.objectContaining({
|
||||
id: body.id,
|
||||
checksum,
|
||||
ownerId: user.userId,
|
||||
exifInfo: expect.objectContaining({ fileSizeInByte: content.byteLength }),
|
||||
originalFileName: 'test-image.jpg',
|
||||
deviceAssetId: 'rufh',
|
||||
deviceId: 'test',
|
||||
isFavorite: true,
|
||||
visibility: 'timeline',
|
||||
}),
|
||||
);
|
||||
const downloaded = await utils.downloadAsset(user.accessToken, body.id);
|
||||
expect(downloaded.size).toBe(content.byteLength);
|
||||
expect(content.compare(await downloaded.bytes())).toBe(0);
|
||||
});
|
||||
|
||||
it('should reject when Upload-Complete: ?1 with mismatching Content-Length and Upload-Length', async () => {
|
||||
const content = randomBytes(1000);
|
||||
|
||||
@@ -789,6 +885,12 @@ describe('/upload', () => {
|
||||
.send(content.subarray(2000));
|
||||
|
||||
expect(secondResponse.status).toBe(200);
|
||||
expect(secondResponse.headers['upload-complete']).toBe('?1');
|
||||
expect(secondResponse.body).toEqual(expect.objectContaining({ id: expect.any(String) }));
|
||||
|
||||
const downloaded = await utils.downloadAsset(user.accessToken, secondResponse.body.id);
|
||||
expect(downloaded.size).toBe(content.byteLength);
|
||||
expect(content.compare(await downloaded.bytes())).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -561,6 +561,16 @@ export const utils = {
|
||||
await utils.waitForQueueFinish(accessToken, 'sidecar');
|
||||
await utils.waitForQueueFinish(accessToken, 'metadataExtraction');
|
||||
},
|
||||
|
||||
downloadAsset: async (accessToken: string, id: string) => {
|
||||
const downloadedRes = await fetch(`${baseUrl}/api/assets/${id}/original`, {
|
||||
headers: asBearerAuth(accessToken),
|
||||
});
|
||||
if (!downloadedRes.ok) {
|
||||
throw new Error(`Failed to download asset ${id}: ${downloadedRes.status} ${await downloadedRes.text()}`);
|
||||
}
|
||||
return await downloadedRes.blob();
|
||||
},
|
||||
};
|
||||
|
||||
utils.initSdk();
|
||||
|
||||
Reference in New Issue
Block a user