mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
test: server-info e2e tests (#3948)
This commit is contained in:
parent
b7fd5dcb4a
commit
4e5bf7ae2e
7 changed files with 176 additions and 7 deletions
|
@ -1,7 +1,7 @@
|
|||
import { DomainModule } from '@app/domain';
|
||||
import { InfraModule } from '@app/infra';
|
||||
import { AssetEntity, ExifEntity } from '@app/infra/entities';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Module, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
@ -66,4 +66,14 @@ import {
|
|||
FileUploadInterceptor,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule implements OnModuleInit, OnModuleDestroy {
|
||||
constructor(private appService: AppService) {}
|
||||
|
||||
async onModuleInit() {
|
||||
await this.appService.init();
|
||||
}
|
||||
|
||||
onModuleDestroy() {
|
||||
this.appService.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,8 @@ export class AppService {
|
|||
await this.searchService.init();
|
||||
this.logger.log(`Feature Flags: ${JSON.stringify(await this.serverService.getFeatures(), null, 2)}`);
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
this.searchService.teardown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import { NestExpressApplication } from '@nestjs/platform-express';
|
|||
import { json } from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { AppModule } from './app.module';
|
||||
import { AppService } from './app.service';
|
||||
import { useSwagger } from './app.utils';
|
||||
|
||||
const logger = new Logger('ImmichServer');
|
||||
|
@ -27,7 +26,6 @@ export async function bootstrap() {
|
|||
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
||||
useSwagger(app, isDev);
|
||||
|
||||
await app.get(AppService).init();
|
||||
const server = await app.listen(port);
|
||||
server.requestTimeout = 30 * 60 * 1000;
|
||||
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
"globalSetup": "<rootDir>/test/e2e/setup.ts",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"testTimeout": 15000,
|
||||
"testTimeout": 60000,
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": ["<rootDir>/src/**/*.(t|j)s", "!<rootDir>/src/infra/**/*"],
|
||||
"collectCoverageFrom": [
|
||||
"<rootDir>/src/**/*.(t|j)s",
|
||||
"!<rootDir>/src/**/*.spec.(t|s)s",
|
||||
"!<rootDir>/src/infra/migrations/**"
|
||||
],
|
||||
"coverageDirectory": "./coverage",
|
||||
"moduleNameMapper": {
|
||||
"^@test(|/.*)$": "<rootDir>/test/$1",
|
||||
|
|
148
server/test/e2e/server-info.e2e-spec.ts
Normal file
148
server/test/e2e/server-info.e2e-spec.ts
Normal file
|
@ -0,0 +1,148 @@
|
|||
import { LoginResponseDto } from '@app/domain';
|
||||
import { AppModule, ServerInfoController } from '@app/immich';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import request from 'supertest';
|
||||
import { errorStub } from '../fixtures';
|
||||
import { api, db } from '../test-utils';
|
||||
|
||||
describe(`${ServerInfoController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let accessToken: string;
|
||||
let loginResponse: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = await moduleFixture.createNestApplication().init();
|
||||
server = app.getHttpServer();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
await api.adminSignUp(server);
|
||||
loginResponse = await api.adminLogin(server);
|
||||
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');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
|
||||
it('should return the disk information', async () => {
|
||||
const { status, body } = await request(server).get('/server-info').set('Authorization', `Bearer ${accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
diskAvailable: expect.any(String),
|
||||
diskAvailableRaw: expect.any(Number),
|
||||
diskSize: expect.any(String),
|
||||
diskSizeRaw: expect.any(Number),
|
||||
diskUsagePercentage: expect.any(Number),
|
||||
diskUse: expect.any(String),
|
||||
diskUseRaw: expect.any(Number),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/ping', () => {
|
||||
it('should respond with pong', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/ping');
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({ res: 'pong' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/version', () => {
|
||||
it('should respond with the server version', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/version');
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
major: expect.any(Number),
|
||||
minor: expect.any(Number),
|
||||
patch: expect.any(Number),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/features', () => {
|
||||
it('should respond with the server features', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/features');
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
clipEncode: true,
|
||||
configFile: false,
|
||||
facialRecognition: true,
|
||||
oauth: false,
|
||||
oauthAutoLaunch: false,
|
||||
passwordLogin: true,
|
||||
search: false,
|
||||
sidecar: true,
|
||||
tagImage: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/stats', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/stats');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
|
||||
it('should only work for admins', async () => {
|
||||
const loginDto = { email: 'test@immich.app', password: 'Immich123' };
|
||||
await api.userApi.create(server, accessToken, { ...loginDto, firstName: 'test', lastName: 'test' });
|
||||
const { accessToken: userAccessToken } = await api.login(server, loginDto);
|
||||
const { status, body } = await request(server)
|
||||
.get('/server-info/stats')
|
||||
.set('Authorization', `Bearer ${userAccessToken}`);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorStub.forbidden);
|
||||
});
|
||||
|
||||
it('should return the server stats', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/server-info/stats')
|
||||
.set('Authorization', `Bearer ${accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
photos: 0,
|
||||
usage: 0,
|
||||
usageByUser: [
|
||||
{
|
||||
photos: 0,
|
||||
usage: 0,
|
||||
userFirstName: 'Immich',
|
||||
userId: loginResponse.userId,
|
||||
userLastName: 'Admin',
|
||||
videos: 0,
|
||||
},
|
||||
],
|
||||
videos: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/media-types', () => {
|
||||
it('should return accepted media types', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/media-types');
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
sidecar: ['.xmp'],
|
||||
image: expect.any(Array),
|
||||
video: expect.any(Array),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,7 +2,7 @@ import { PostgreSqlContainer } from '@testcontainers/postgresql';
|
|||
import { GenericContainer } from 'testcontainers';
|
||||
export default async () => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
process.env.TYPESENSE_API_KEY = 'abc123';
|
||||
process.env.TYPESENSE_ENABLED = 'false';
|
||||
|
||||
const pg = await new PostgreSqlContainer('postgres')
|
||||
.withExposedPorts(5432)
|
||||
|
|
5
server/test/fixtures/error.stub.ts
vendored
5
server/test/fixtures/error.stub.ts
vendored
|
@ -4,6 +4,11 @@ export const errorStub = {
|
|||
statusCode: 401,
|
||||
message: 'Authentication required',
|
||||
},
|
||||
forbidden: {
|
||||
error: 'Forbidden',
|
||||
statusCode: 403,
|
||||
message: expect.any(String),
|
||||
},
|
||||
wrongPassword: {
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
|
|
Loading…
Reference in a new issue