refactor: e2e tests (#4536)
This commit is contained in:
parent
31987bc043
commit
4b59f83288
22 changed files with 189 additions and 201 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
|||
submodules: "recursive"
|
||||
|
||||
- name: Run e2e tests
|
||||
run: docker-compose -f ./docker/docker-compose.test.yml -p immich-test-e2e up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server-test --remove-orphans --build
|
||||
run: docker compose -f ./docker/docker-compose.test.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build
|
||||
|
||||
doc-tests:
|
||||
name: Run documentation checks
|
||||
|
|
4
Makefile
4
Makefile
|
@ -20,7 +20,7 @@ pull-stage:
|
|||
docker-compose -f ./docker/docker-compose.staging.yml pull
|
||||
|
||||
test-e2e:
|
||||
docker-compose -f ./docker/docker-compose.test.yml -p immich-test-e2e up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server-test --remove-orphans --build
|
||||
docker compose -f ./docker/docker-compose.test.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build
|
||||
|
||||
prod:
|
||||
docker-compose -f ./docker/docker-compose.prod.yml up --build -V --remove-orphans
|
||||
|
@ -32,4 +32,4 @@ api:
|
|||
cd ./server && npm run api:generate
|
||||
|
||||
attach-server:
|
||||
docker exec -it docker_immich-server_1 sh
|
||||
docker exec -it docker_immich-server_1 sh
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
version: "3.8"
|
||||
|
||||
# Compose file for dockerized end-to-end testing of the backend
|
||||
name: "immich-test-e2e"
|
||||
|
||||
services:
|
||||
immich-server-test:
|
||||
image: immich-server-test
|
||||
immich-server:
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
|
@ -14,27 +14,20 @@ services:
|
|||
- ../server:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
environment:
|
||||
- DB_HOSTNAME=immich-database-test
|
||||
- DB_HOSTNAME=database
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=postgres
|
||||
- DB_DATABASE_NAME=e2e_test
|
||||
- IMMICH_RUN_ALL_TESTS=true
|
||||
depends_on:
|
||||
- immich-database-test
|
||||
networks:
|
||||
- immich-test-network
|
||||
- database
|
||||
|
||||
immich-database-test:
|
||||
container_name: immich-database-test
|
||||
database:
|
||||
image: postgres:14-alpine@sha256:28407a9961e76f2d285dc6991e8e48893503cc3836a4755bbc2d40bcc272a441
|
||||
command: -c fsync=off
|
||||
environment:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: e2e_test
|
||||
networks:
|
||||
- immich-test-network
|
||||
logging:
|
||||
driver: none
|
||||
|
||||
networks:
|
||||
immich-test-network:
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "NODE_OPTIONS='--experimental-vm-modules --max_old_space_size=4096' jest --config test/e2e/jest-e2e.json --runInBand --forceExit",
|
||||
"test:e2e": "NODE_OPTIONS='--experimental-vm-modules --max_old_space_size=4096' jest --config test/e2e/jest-e2e.json --runInBand",
|
||||
"typeorm": "typeorm",
|
||||
"typeorm:migrations:create": "typeorm migration:create",
|
||||
"typeorm:migrations:generate": "typeorm migration:generate -d ./dist/infra/database.config.js",
|
||||
|
|
|
@ -109,6 +109,10 @@ export class MetadataService {
|
|||
}
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
await this.repository.teardown();
|
||||
}
|
||||
|
||||
async handleLivePhotoLinking(job: IEntityJob) {
|
||||
const { id } = job;
|
||||
const [asset] = await this.assetRepository.getByIds([id]);
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface ImmichTags extends Omit<Tags, 'FocalLength'> {
|
|||
|
||||
export interface IMetadataRepository {
|
||||
init(options: Partial<InitOptions>): Promise<void>;
|
||||
teardown(): Promise<void>;
|
||||
reverseGeocode(point: GeoPoint): Promise<ReverseGeocodeResult>;
|
||||
deleteCache(): Promise<void>;
|
||||
getExifTags(path: string): Promise<ImmichTags | null>;
|
||||
|
|
|
@ -45,6 +45,10 @@ export class MetadataRepository implements IMetadataRepository {
|
|||
});
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
await exiftool.end();
|
||||
}
|
||||
|
||||
async deleteCache() {
|
||||
const dumpDirectory = REVERSE_GEOCODING_DUMP_DIRECTORY;
|
||||
if (dumpDirectory) {
|
||||
|
|
|
@ -103,4 +103,8 @@ export class AppService {
|
|||
await this.metadataService.init();
|
||||
await this.searchService.init();
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
await this.metadataService.teardown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,10 @@ import { AlbumResponseDto, LoginResponseDto } from '@app/domain';
|
|||
import { AlbumController } from '@app/immich';
|
||||
import { AssetFileUploadResponseDto } from '@app/immich/api-v1/asset/response-dto/asset-file-upload-response.dto';
|
||||
import { SharedLinkType } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub, uuidStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
|
||||
const user1SharedUser = 'user1SharedUser';
|
||||
|
@ -17,7 +16,6 @@ const user2SharedLink = 'user2SharedLink';
|
|||
const user2NotShared = 'user2NotShared';
|
||||
|
||||
describe(`${AlbumController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
let user1: LoginResponseDto;
|
||||
|
@ -27,9 +25,11 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
|||
let user2Albums: AlbumResponseDto[];
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
[server] = await testApp.create();
|
||||
});
|
||||
|
||||
server = app.getHttpServer();
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -37,24 +37,30 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
|||
await api.authApi.adminSignUp(server);
|
||||
admin = await api.authApi.adminLogin(server);
|
||||
|
||||
await api.userApi.create(server, admin.accessToken, {
|
||||
email: 'user1@immich.app',
|
||||
password: 'Password123',
|
||||
firstName: 'User 1',
|
||||
lastName: 'Test',
|
||||
});
|
||||
user1 = await api.authApi.login(server, { email: 'user1@immich.app', password: 'Password123' });
|
||||
await Promise.all([
|
||||
api.userApi.create(server, admin.accessToken, {
|
||||
email: 'user1@immich.app',
|
||||
password: 'Password123',
|
||||
firstName: 'User 1',
|
||||
lastName: 'Test',
|
||||
}),
|
||||
api.userApi.create(server, admin.accessToken, {
|
||||
email: 'user2@immich.app',
|
||||
password: 'Password123',
|
||||
firstName: 'User 2',
|
||||
lastName: 'Test',
|
||||
}),
|
||||
]);
|
||||
|
||||
await api.userApi.create(server, admin.accessToken, {
|
||||
email: 'user2@immich.app',
|
||||
password: 'Password123',
|
||||
firstName: 'User 2',
|
||||
lastName: 'Test',
|
||||
});
|
||||
user2 = await api.authApi.login(server, { email: 'user2@immich.app', password: 'Password123' });
|
||||
[user1, user2] = await Promise.all([
|
||||
api.authApi.login(server, { email: 'user1@immich.app', password: 'Password123' }),
|
||||
api.authApi.login(server, { email: 'user2@immich.app', password: 'Password123' }),
|
||||
]);
|
||||
|
||||
user1Asset = await api.assetApi.upload(server, user1.accessToken, 'example');
|
||||
user1Albums = await Promise.all([
|
||||
|
||||
const albums = await Promise.all([
|
||||
// user 1
|
||||
api.albumApi.create(server, user1.accessToken, {
|
||||
albumName: user1SharedUser,
|
||||
sharedWithUserIds: [user2.userId],
|
||||
|
@ -62,15 +68,8 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
|||
}),
|
||||
api.albumApi.create(server, user1.accessToken, { albumName: user1SharedLink, assetIds: [user1Asset.id] }),
|
||||
api.albumApi.create(server, user1.accessToken, { albumName: user1NotShared, assetIds: [user1Asset.id] }),
|
||||
]);
|
||||
|
||||
// add shared link to user1SharedLink album
|
||||
await api.sharedLinkApi.create(server, user1.accessToken, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
albumId: user1Albums[1].id,
|
||||
});
|
||||
|
||||
user2Albums = await Promise.all([
|
||||
// user 2
|
||||
api.albumApi.create(server, user2.accessToken, {
|
||||
albumName: user2SharedUser,
|
||||
sharedWithUserIds: [user1.userId],
|
||||
|
@ -80,16 +79,22 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
|||
api.albumApi.create(server, user2.accessToken, { albumName: user2NotShared }),
|
||||
]);
|
||||
|
||||
// add shared link to user2SharedLink album
|
||||
await api.sharedLinkApi.create(server, user2.accessToken, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
albumId: user2Albums[1].id,
|
||||
});
|
||||
});
|
||||
user1Albums = albums.slice(0, 3);
|
||||
user2Albums = albums.slice(3);
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
await Promise.all([
|
||||
// add shared link to user1SharedLink album
|
||||
api.sharedLinkApi.create(server, user1.accessToken, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
albumId: user1Albums[1].id,
|
||||
}),
|
||||
|
||||
// add shared link to user2SharedLink album
|
||||
api.sharedLinkApi.create(server, user2.accessToken, {
|
||||
type: SharedLinkType.ALBUM,
|
||||
albumId: user2Albums[1].id,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('GET /album', () => {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { AssetEntity, AssetType, SharedLinkType } from '@app/infra/entities';
|
|||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { errorStub, uuidStub } from '@test/fixtures';
|
||||
import { createTestApp, db } from '@test/test-utils';
|
||||
import { db, testApp } from '@test/test-utils';
|
||||
import { randomBytes } from 'crypto';
|
||||
import request from 'supertest';
|
||||
|
||||
|
@ -86,12 +86,14 @@ describe(`${AssetController.name} (e2e)`, () => {
|
|||
let asset4: AssetEntity;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
|
||||
server = app.getHttpServer();
|
||||
[server, app] = await testApp.create();
|
||||
assetRepository = app.get<IAssetRepository>(IAssetRepository);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
await api.authApi.adminSignUp(server);
|
||||
|
@ -123,11 +125,6 @@ describe(`${AssetController.name} (e2e)`, () => {
|
|||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('POST /asset/upload', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server)
|
||||
|
@ -589,9 +586,11 @@ describe(`${AssetController.name} (e2e)`, () => {
|
|||
|
||||
describe('GET /asset/map-marker', () => {
|
||||
beforeEach(async () => {
|
||||
await assetRepository.save({ id: asset1.id, isArchived: true });
|
||||
await assetRepository.upsertExif({ assetId: asset1.id, latitude: 0, longitude: 0 });
|
||||
await assetRepository.upsertExif({ assetId: asset2.id, latitude: 0, longitude: 0 });
|
||||
await Promise.all([
|
||||
assetRepository.save({ id: asset1.id, isArchived: true }),
|
||||
assetRepository.upsertExif({ assetId: asset1.id, latitude: 0, longitude: 0 }),
|
||||
assetRepository.upsertExif({ assetId: asset2.id, latitude: 0, longitude: 0 }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { AuthController } from '@app/immich';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import {
|
||||
|
@ -12,7 +11,7 @@ import {
|
|||
signupResponseStub,
|
||||
uuidStub,
|
||||
} from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
|
||||
const firstName = 'Immich';
|
||||
|
@ -21,13 +20,16 @@ const password = 'Password123';
|
|||
const email = 'admin@immich.app';
|
||||
|
||||
describe(`${AuthController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let accessToken: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
server = app.getHttpServer();
|
||||
await testApp.reset();
|
||||
[server] = await testApp.create();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -37,11 +39,6 @@ describe(`${AuthController.name} (e2e)`, () => {
|
|||
accessToken = response.accessToken;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('POST /auth/admin-sign-up', () => {
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { LoginResponseDto } from '@app/domain';
|
||||
import { AssetType, LibraryType } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { IMMICH_TEST_ASSET_PATH, createTestApp, db, runAllTests } from '@test/test-utils';
|
||||
import { IMMICH_TEST_ASSET_PATH, db, runAllTests, testApp } from '@test/test-utils';
|
||||
|
||||
describe(`Supported file formats (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
|
||||
|
@ -170,8 +168,11 @@ describe(`Supported file formats (e2e)`, () => {
|
|||
const testsToRun = formatTests.filter((formatTest) => formatTest.runTest);
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp(true);
|
||||
server = app.getHttpServer();
|
||||
[server] = await testApp.create({ jobs: true });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -181,11 +182,6 @@ describe(`Supported file formats (e2e)`, () => {
|
|||
await api.userApi.setExternalPath(server, admin.accessToken, admin.userId, '/');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it.each(testsToRun)('should import file of format $format', async (testedFormat) => {
|
||||
const library = await api.libraryApi.create(server, admin.accessToken, {
|
||||
type: LibraryType.EXTERNAL,
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
import { LibraryResponseDto, LoginResponseDto } from '@app/domain';
|
||||
import { LibraryController } from '@app/immich';
|
||||
import { AssetType, LibraryType } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import {
|
||||
IMMICH_TEST_ASSET_PATH,
|
||||
IMMICH_TEST_ASSET_TEMP_PATH,
|
||||
createTestApp,
|
||||
db,
|
||||
restoreTempFolder,
|
||||
} from '@test/test-utils';
|
||||
import { IMMICH_TEST_ASSET_PATH, IMMICH_TEST_ASSET_TEMP_PATH, db, restoreTempFolder, testApp } from '@test/test-utils';
|
||||
import * as fs from 'fs';
|
||||
import request from 'supertest';
|
||||
import { utimes } from 'utimes';
|
||||
import { errorStub, uuidStub } from '../fixtures';
|
||||
|
||||
describe(`${LibraryController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
|
||||
|
@ -35,8 +27,12 @@ describe(`${LibraryController.name} (e2e)`, () => {
|
|||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp(true);
|
||||
server = app.getHttpServer();
|
||||
[server] = await testApp.create({ jobs: true });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
await restoreTempFolder();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -46,12 +42,6 @@ describe(`${LibraryController.name} (e2e)`, () => {
|
|||
admin = await api.authApi.adminLogin(server);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
await restoreTempFolder();
|
||||
});
|
||||
|
||||
describe('GET /library', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/library');
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { OAuthController } from '@app/immich';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
|
||||
describe(`${OAuthController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
server = app.getHttpServer();
|
||||
[server] = await testApp.create();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -20,11 +21,6 @@ describe(`${OAuthController.name} (e2e)`, () => {
|
|||
await api.authApi.adminSignUp(server);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('POST /oauth/authorize', () => {
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
|
|
|
@ -4,7 +4,7 @@ import { INestApplication } from '@nestjs/common';
|
|||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
|
||||
const user1Dto = {
|
||||
|
@ -31,27 +31,29 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
let user2: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
server = app.getHttpServer();
|
||||
[server, app] = await testApp.create();
|
||||
repository = app.get<IPartnerRepository>(IPartnerRepository);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
await api.authApi.adminSignUp(server);
|
||||
loginResponse = await api.authApi.adminLogin(server);
|
||||
accessToken = loginResponse.accessToken;
|
||||
|
||||
await api.userApi.create(server, accessToken, user1Dto);
|
||||
user1 = await api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password });
|
||||
await Promise.all([
|
||||
api.userApi.create(server, accessToken, user1Dto),
|
||||
api.userApi.create(server, accessToken, user2Dto),
|
||||
]);
|
||||
|
||||
await api.userApi.create(server, accessToken, user2Dto);
|
||||
user2 = await api.authApi.login(server, { email: user2Dto.email, password: user2Dto.password });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
[user1, user2] = await Promise.all([
|
||||
api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password }),
|
||||
api.authApi.login(server, { email: user2Dto.email, password: user2Dto.password }),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('GET /partner', () => {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { INestApplication } from '@nestjs/common';
|
|||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub, uuidStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
|
||||
describe(`${PersonController.name}`, () => {
|
||||
|
@ -18,11 +18,14 @@ describe(`${PersonController.name}`, () => {
|
|||
let hiddenPerson: PersonEntity;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
server = app.getHttpServer();
|
||||
[server, app] = await testApp.create();
|
||||
personRepository = app.get<IPersonRepository>(IPersonRepository);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
await api.authApi.adminSignUp(server);
|
||||
|
@ -46,11 +49,6 @@ describe(`${PersonController.name}`, () => {
|
|||
await personRepository.createFace({ assetId: faceAsset.id, personId: hiddenPerson.id });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('GET /person', () => {
|
||||
beforeEach(async () => {});
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
import { LoginResponseDto } from '@app/domain';
|
||||
import { ServerInfoController } from '@app/immich';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
|
||||
describe(`${ServerInfoController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let accessToken: string;
|
||||
let loginResponse: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
server = app.getHttpServer();
|
||||
[server] = await testApp.create();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -25,11 +26,6 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
|
|||
accessToken = loginResponse.accessToken;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('GET /server-info', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/server-info');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PostgreSqlContainer } from '@testcontainers/postgresql';
|
||||
import * as fs from 'fs';
|
||||
import { access } from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
export default async () => {
|
||||
|
@ -23,8 +23,7 @@ export default async () => {
|
|||
}
|
||||
|
||||
const directoryExists = async (dirPath: string) =>
|
||||
await fs.promises
|
||||
.access(dirPath)
|
||||
await access(dirPath)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
import { AlbumResponseDto, LoginResponseDto, SharedLinkResponseDto } from '@app/domain';
|
||||
import { PartnerController } from '@app/immich';
|
||||
import { LibraryType, SharedLinkType } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub, uuidStub } from '@test/fixtures';
|
||||
import {
|
||||
IMMICH_TEST_ASSET_PATH,
|
||||
IMMICH_TEST_ASSET_TEMP_PATH,
|
||||
createTestApp,
|
||||
restoreTempFolder,
|
||||
} from '@test/test-utils';
|
||||
import { IMMICH_TEST_ASSET_PATH, IMMICH_TEST_ASSET_TEMP_PATH, restoreTempFolder, testApp } from '@test/test-utils';
|
||||
import { cp } from 'fs/promises';
|
||||
import request from 'supertest';
|
||||
|
||||
|
@ -22,7 +16,6 @@ const user1Dto = {
|
|||
};
|
||||
|
||||
describe(`${PartnerController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
let user1: LoginResponseDto;
|
||||
|
@ -30,8 +23,12 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
let sharedLink: SharedLinkResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp(true);
|
||||
server = app.getHttpServer();
|
||||
[server] = await testApp.create({ jobs: true });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
await restoreTempFolder();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -49,12 +46,6 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
|||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
await restoreTempFolder();
|
||||
});
|
||||
|
||||
describe('GET /shared-link', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/shared-link');
|
||||
|
|
|
@ -2,10 +2,11 @@ import { LoginResponseDto, UserResponseDto, UserService } from '@app/domain';
|
|||
import { AppModule, UserController } from '@app/immich';
|
||||
import { UserEntity } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { api } from '@test/api';
|
||||
import { db } from '@test/db';
|
||||
import { errorStub, userSignupStub, userStub } from '@test/fixtures';
|
||||
import { createTestApp } from '@test/test-utils';
|
||||
import { testApp } from '@test/test-utils';
|
||||
import request from 'supertest';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
|
@ -18,10 +19,12 @@ describe(`${UserController.name}`, () => {
|
|||
let userRepository: Repository<UserEntity>;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await createTestApp();
|
||||
userRepository = app.select(AppModule).get('UserEntityRepository');
|
||||
[server, app] = await testApp.create();
|
||||
userRepository = app.select(AppModule).get(getRepositoryToken(UserEntity));
|
||||
});
|
||||
|
||||
server = app.getHttpServer();
|
||||
afterAll(async () => {
|
||||
await testApp.teardown();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
|
|
@ -5,6 +5,7 @@ export const newMetadataRepositoryMock = (): jest.Mocked<IMetadataRepository> =>
|
|||
deleteCache: jest.fn(),
|
||||
getExifTags: jest.fn(),
|
||||
init: jest.fn(),
|
||||
teardown: jest.fn(),
|
||||
reverseGeocode: jest.fn(),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { dataSource } from '@app/infra';
|
||||
|
||||
import { IJobRepository, JobItem, JobItemHandler, QueueName } from '@app/domain';
|
||||
import { AppModule } from '@app/immich';
|
||||
import { INestApplication, Logger } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { dataSource } from '@app/infra';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as fs from 'fs';
|
||||
import path from 'path';
|
||||
import { AppService } from '../src/microservices/app.service';
|
||||
|
@ -36,38 +35,48 @@ export const db = {
|
|||
|
||||
let _handler: JobItemHandler = () => Promise.resolve();
|
||||
|
||||
export async function createTestApp(runJobs = false, log = false): Promise<INestApplication> {
|
||||
const moduleBuilder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
providers: [AppService],
|
||||
})
|
||||
.overrideProvider(IJobRepository)
|
||||
.useValue({
|
||||
addHandler: (_queueName: QueueName, _concurrency: number, handler: JobItemHandler) => (_handler = handler),
|
||||
queue: (item: JobItem) => runJobs && _handler(item),
|
||||
resume: jest.fn(),
|
||||
empty: jest.fn(),
|
||||
setConcurrency: jest.fn(),
|
||||
getQueueStatus: jest.fn(),
|
||||
getJobCounts: jest.fn(),
|
||||
pause: jest.fn(),
|
||||
} as IJobRepository);
|
||||
|
||||
const moduleFixture: TestingModule = await moduleBuilder.compile();
|
||||
|
||||
const app = moduleFixture.createNestApplication();
|
||||
if (log) {
|
||||
app.useLogger(new Logger());
|
||||
} else {
|
||||
app.useLogger(false);
|
||||
}
|
||||
await app.init();
|
||||
const appService = app.get(AppService);
|
||||
await appService.init();
|
||||
|
||||
return app;
|
||||
interface TestAppOptions {
|
||||
jobs: boolean;
|
||||
}
|
||||
|
||||
let app: INestApplication;
|
||||
|
||||
export const testApp = {
|
||||
create: async (options?: TestAppOptions): Promise<[any, INestApplication]> => {
|
||||
const { jobs } = options || { jobs: false };
|
||||
|
||||
const moduleFixture = await Test.createTestingModule({ imports: [AppModule], providers: [AppService] })
|
||||
.overrideProvider(IJobRepository)
|
||||
.useValue({
|
||||
addHandler: (_queueName: QueueName, _concurrency: number, handler: JobItemHandler) => (_handler = handler),
|
||||
queue: (item: JobItem) => jobs && _handler(item),
|
||||
resume: jest.fn(),
|
||||
empty: jest.fn(),
|
||||
setConcurrency: jest.fn(),
|
||||
getQueueStatus: jest.fn(),
|
||||
getJobCounts: jest.fn(),
|
||||
pause: jest.fn(),
|
||||
} as IJobRepository)
|
||||
.compile();
|
||||
|
||||
app = await moduleFixture.createNestApplication().init();
|
||||
|
||||
if (jobs) {
|
||||
await app.get(AppService).init();
|
||||
}
|
||||
|
||||
return [app.getHttpServer(), app];
|
||||
},
|
||||
reset: async () => {
|
||||
await db.reset();
|
||||
},
|
||||
teardown: async () => {
|
||||
await app.get(AppService).teardown();
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
},
|
||||
};
|
||||
|
||||
export const runAllTests: boolean = process.env.IMMICH_RUN_ALL_TESTS === 'true';
|
||||
|
||||
const directoryExists = async (dirPath: string) =>
|
||||
|
|
Loading…
Reference in a new issue