mirror of
https://github.com/immich-app/immich.git
synced 2025-12-24 01:11:32 +03:00
refactor(server): e2e (#7462)
* refactor: trash e2e * refactor: asset e2e
This commit is contained in:
@@ -538,90 +538,6 @@ describe(`${AssetController.name} (e2e)`, () => {
|
||||
}
|
||||
});
|
||||
|
||||
describe('GET /asset/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get(`/asset/${uuidStub.notFound}`);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
expect(status).toBe(401);
|
||||
});
|
||||
|
||||
it('should require a valid id', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get(`/asset/${uuidStub.invalid}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorStub.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should require access', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get(`/asset/${asset4.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorStub.noPermission);
|
||||
});
|
||||
|
||||
it('should get the asset info', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toMatchObject({ id: asset1.id });
|
||||
});
|
||||
|
||||
it('should work with a shared link', async () => {
|
||||
const sharedLink = await api.sharedLinkApi.create(server, user1.accessToken, {
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
assetIds: [asset1.id],
|
||||
});
|
||||
|
||||
const { status, body } = await request(server).get(`/asset/${asset1.id}?key=${sharedLink.key}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toMatchObject({ id: asset1.id });
|
||||
});
|
||||
|
||||
it('should not send people data for shared links for un-authenticated users', async () => {
|
||||
const personRepository = app.get<IPersonRepository>(IPersonRepository);
|
||||
const person = await personRepository.create({ ownerId: asset1.ownerId, name: 'Test Person' });
|
||||
|
||||
await personRepository.createFaces([
|
||||
{
|
||||
assetId: asset1.id,
|
||||
personId: person.id,
|
||||
embedding: Array.from({ length: 512 }, Math.random),
|
||||
},
|
||||
]);
|
||||
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ isFavorite: true });
|
||||
expect(status).toEqual(200);
|
||||
expect(body).toMatchObject({
|
||||
id: asset1.id,
|
||||
isFavorite: true,
|
||||
people: [
|
||||
{
|
||||
birthDate: null,
|
||||
id: expect.any(String),
|
||||
isHidden: false,
|
||||
name: 'Test Person',
|
||||
thumbnailPath: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const sharedLink = await api.sharedLinkApi.create(server, user1.accessToken, {
|
||||
type: SharedLinkType.INDIVIDUAL,
|
||||
assetIds: [asset1.id],
|
||||
});
|
||||
|
||||
const data = await request(server).get(`/asset/${asset1.id}?key=${sharedLink.key}`);
|
||||
expect(data.status).toBe(200);
|
||||
expect(data.body).toMatchObject({ people: [] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /asset/upload', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server)
|
||||
@@ -759,286 +675,6 @@ describe(`${AssetController.name} (e2e)`, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /asset/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).put(`/asset/:${uuidStub.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
|
||||
it('should require a valid id', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${uuidStub.invalid}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorStub.badRequest(['id must be a UUID']));
|
||||
});
|
||||
|
||||
it('should require access', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset4.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorStub.noPermission);
|
||||
});
|
||||
|
||||
it('should favorite an asset', async () => {
|
||||
expect(asset1).toMatchObject({ isFavorite: false });
|
||||
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ isFavorite: true });
|
||||
expect(body).toMatchObject({ id: asset1.id, isFavorite: true });
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should archive an asset', async () => {
|
||||
expect(asset1).toMatchObject({ isArchived: false });
|
||||
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ isArchived: true });
|
||||
expect(body).toMatchObject({ id: asset1.id, isArchived: true });
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should update date time original', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' });
|
||||
|
||||
expect(body).toMatchObject({
|
||||
id: asset1.id,
|
||||
exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-20T01:11:00.000Z' }),
|
||||
});
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should reject invalid gps coordinates', async () => {
|
||||
for (const test of [
|
||||
{ latitude: 12 },
|
||||
{ longitude: 12 },
|
||||
{ latitude: 12, longitude: 'abc' },
|
||||
{ latitude: 'abc', longitude: 12 },
|
||||
{ latitude: null, longitude: 12 },
|
||||
{ latitude: 12, longitude: null },
|
||||
{ latitude: 91, longitude: 12 },
|
||||
{ latitude: -91, longitude: 12 },
|
||||
{ latitude: 12, longitude: -181 },
|
||||
{ latitude: 12, longitude: 181 },
|
||||
]) {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.send(test)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorStub.badRequest());
|
||||
}
|
||||
});
|
||||
|
||||
it('should update gps data', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ latitude: 12, longitude: 12 });
|
||||
|
||||
expect(body).toMatchObject({
|
||||
id: asset1.id,
|
||||
exifInfo: expect.objectContaining({ latitude: 12, longitude: 12 }),
|
||||
});
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should set the description', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ description: 'Test asset description' });
|
||||
expect(body).toMatchObject({
|
||||
id: asset1.id,
|
||||
exifInfo: expect.objectContaining({ description: 'Test asset description' }),
|
||||
});
|
||||
expect(status).toEqual(200);
|
||||
});
|
||||
|
||||
it('should return tagged people', async () => {
|
||||
const personRepository = app.get<IPersonRepository>(IPersonRepository);
|
||||
const person = await personRepository.create({ ownerId: asset1.ownerId, name: 'Test Person' });
|
||||
|
||||
await personRepository.createFaces([
|
||||
{
|
||||
assetId: asset1.id,
|
||||
personId: person.id,
|
||||
embedding: Array.from({ length: 512 }, Math.random),
|
||||
},
|
||||
]);
|
||||
|
||||
const { status, body } = await request(server)
|
||||
.put(`/asset/${asset1.id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ isFavorite: true });
|
||||
expect(status).toEqual(200);
|
||||
expect(body).toMatchObject({
|
||||
id: asset1.id,
|
||||
isFavorite: true,
|
||||
people: [
|
||||
{
|
||||
birthDate: null,
|
||||
id: expect.any(String),
|
||||
isHidden: false,
|
||||
name: 'Test Person',
|
||||
thumbnailPath: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /asset/statistics', () => {
|
||||
beforeEach(async () => {
|
||||
await api.assetApi.upload(server, user1.accessToken, 'favored_asset', { isFavorite: true });
|
||||
await api.assetApi.upload(server, user1.accessToken, 'archived_asset', { isArchived: true });
|
||||
await api.assetApi.upload(server, user1.accessToken, 'favored_archived_asset', {
|
||||
isFavorite: true,
|
||||
isArchived: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/asset/statistics');
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
|
||||
it('should return stats of all assets', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/statistics')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
|
||||
expect(body).toEqual({ images: 6, videos: 1, total: 7 });
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('should return stats of all favored assets', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/statistics')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.query({ isFavorite: true });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 2, videos: 1, total: 3 });
|
||||
});
|
||||
|
||||
it('should return stats of all archived assets', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/statistics')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.query({ isArchived: true });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 3, videos: 0, total: 3 });
|
||||
});
|
||||
|
||||
it('should return stats of all favored and archived assets', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/statistics')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.query({ isFavorite: true, isArchived: true });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 1, videos: 0, total: 1 });
|
||||
});
|
||||
|
||||
it('should return stats of all assets neither favored nor archived', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/statistics')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.query({ isFavorite: false, isArchived: false });
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ images: 2, videos: 0, total: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /asset/random', () => {
|
||||
beforeAll(async () => {
|
||||
await Promise.all([
|
||||
createAsset(user1, new Date('1970-02-01')),
|
||||
createAsset(user1, new Date('1970-02-01')),
|
||||
createAsset(user1, new Date('1970-02-01')),
|
||||
createAsset(user1, new Date('1970-02-01')),
|
||||
createAsset(user1, new Date('1970-02-01')),
|
||||
createAsset(user1, new Date('1970-02-01')),
|
||||
]);
|
||||
});
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/asset/random');
|
||||
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
|
||||
it.each(Array(10))('should return 1 random assets', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/random')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
|
||||
const assets: AssetResponseDto[] = body;
|
||||
expect(assets.length).toBe(1);
|
||||
expect(assets[0].ownerId).toBe(user1.userId);
|
||||
//
|
||||
// assets owned by user2
|
||||
expect(assets[0].id).not.toBe(asset4.id);
|
||||
// assets owned by user1
|
||||
expect([asset1.id, asset2.id, asset3.id]).toContain(assets[0].id);
|
||||
});
|
||||
|
||||
it.each(Array(10))('should return 2 random assets', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/asset/random?count=2')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
|
||||
const assets: AssetResponseDto[] = body;
|
||||
expect(assets.length).toBe(2);
|
||||
|
||||
for (const asset of assets) {
|
||||
expect(asset.ownerId).toBe(user1.userId);
|
||||
// assets owned by user1
|
||||
expect([asset1.id, asset2.id, asset3.id]).toContain(asset.id);
|
||||
// assets owned by user2
|
||||
expect(asset.id).not.toBe(asset4.id);
|
||||
}
|
||||
});
|
||||
|
||||
it.each(Array(10))(
|
||||
'should return 1 asset if there are 10 assets in the database but user 2 only has 1',
|
||||
async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/[]asset/random')
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual([expect.objectContaining({ id: asset4.id })]);
|
||||
},
|
||||
);
|
||||
|
||||
it('should return error', async () => {
|
||||
const { status } = await request(server)
|
||||
.get('/asset/random?count=ABC')
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
|
||||
expect(status).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /asset/time-buckets', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/asset/time-buckets').query({ size: TimeBucketSize.MONTH });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AssetBulkDeleteDto, AssetResponseDto } from '@app/domain';
|
||||
import { AssetResponseDto } from '@app/domain';
|
||||
import { CreateAssetDto } from '@app/immich/api-v1/asset/dto/create-asset.dto';
|
||||
import { AssetFileUploadResponseDto } from '@app/immich/api-v1/asset/response-dto/asset-file-upload-response.dto';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
@@ -74,8 +74,4 @@ export const assetApi = {
|
||||
expect(status).toBe(200);
|
||||
return body;
|
||||
},
|
||||
delete: async (server: any, accessToken: string, dto: AssetBulkDeleteDto) => {
|
||||
const { status } = await request(server).delete('/asset').set('Authorization', `Bearer ${accessToken}`).send(dto);
|
||||
expect(status).toBe(204);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import { LoginResponseDto } from '@app/domain';
|
||||
import { api } from 'e2e/client';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { basename, join } from 'node:path';
|
||||
import type { App } from 'supertest/types';
|
||||
import { IMMICH_TEST_ASSET_PATH, testApp } from '../../../src/test-utils/utils';
|
||||
|
||||
const assetFilePath = join(IMMICH_TEST_ASSET_PATH, 'formats/png/density_plot.png');
|
||||
|
||||
describe(`Trash (e2e)`, () => {
|
||||
let server: App;
|
||||
let admin: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
const app = await testApp.create();
|
||||
server = app.getHttpServer();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await testApp.reset();
|
||||
await api.authApi.adminSignUp(server);
|
||||
admin = await api.authApi.adminLogin(server);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
it('should move an asset to trash', async () => {
|
||||
const content = await readFile(assetFilePath);
|
||||
const { id: assetId } = await api.assetApi.upload(server, admin.accessToken, 'test-device-id', {
|
||||
content,
|
||||
filename: basename(assetFilePath),
|
||||
});
|
||||
|
||||
const uploadedAsset = await api.assetApi.get(server, admin.accessToken, assetId);
|
||||
expect(uploadedAsset.isTrashed).toBe(false);
|
||||
|
||||
await api.assetApi.delete(server, admin.accessToken, { ids: [assetId] });
|
||||
|
||||
const deletedAsset = await api.assetApi.get(server, admin.accessToken, assetId);
|
||||
expect(deletedAsset.isTrashed).toBe(true);
|
||||
});
|
||||
|
||||
it('should delete all trashed assets', async () => {
|
||||
const content = await readFile(assetFilePath);
|
||||
const { id: assetId } = await api.assetApi.upload(server, admin.accessToken, 'test-device-id', {
|
||||
content,
|
||||
filename: basename(assetFilePath),
|
||||
});
|
||||
|
||||
await api.assetApi.delete(server, admin.accessToken, { ids: [assetId] });
|
||||
|
||||
const assetsBeforeEmpty = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(assetsBeforeEmpty.length).toBe(1);
|
||||
|
||||
await api.trashApi.empty(server, admin.accessToken);
|
||||
|
||||
const assetsAfterEmpty = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
expect(assetsAfterEmpty.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should restore all trashed assets', async () => {
|
||||
const content = await readFile(assetFilePath);
|
||||
const { id: assetId } = await api.assetApi.upload(server, admin.accessToken, 'test-device-id', {
|
||||
content,
|
||||
filename: basename(assetFilePath),
|
||||
});
|
||||
|
||||
await api.assetApi.delete(server, admin.accessToken, { ids: [assetId] });
|
||||
|
||||
const deletedAsset = await api.assetApi.get(server, admin.accessToken, assetId);
|
||||
expect(deletedAsset.isTrashed).toBe(true);
|
||||
|
||||
await api.trashApi.restore(server, admin.accessToken);
|
||||
|
||||
const restoredAsset = await api.assetApi.get(server, admin.accessToken, assetId);
|
||||
expect(restoredAsset.isTrashed).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user