refactor(cli): organize files, simplify types, use @immich/sdk (#6747)
This commit is contained in:
@@ -1,13 +1,31 @@
|
||||
import { BaseOptionsDto } from 'src/cores/dto/base-options-dto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { ImmichApi } from '../src/services/api.service';
|
||||
|
||||
export const TEST_CONFIG_DIR = '/tmp/immich/';
|
||||
export const TEST_AUTH_FILE = path.join(TEST_CONFIG_DIR, 'auth.yml');
|
||||
export const TEST_IMMICH_INSTANCE_URL = 'https://test/api';
|
||||
export const TEST_IMMICH_API_KEY = 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg';
|
||||
|
||||
export const CLI_BASE_OPTIONS: BaseOptionsDto = { config: TEST_CONFIG_DIR };
|
||||
export const CLI_BASE_OPTIONS = { config: TEST_CONFIG_DIR };
|
||||
|
||||
export const setup = async () => {
|
||||
const api = new ImmichApi(process.env.IMMICH_INSTANCE_URL as string, '');
|
||||
await api.authenticationApi.signUpAdmin({
|
||||
signUpDto: { email: 'cli@immich.app', password: 'password', name: 'Administrator' },
|
||||
});
|
||||
const { data: admin } = await api.authenticationApi.login({
|
||||
loginCredentialDto: { email: 'cli@immich.app', password: 'password' },
|
||||
});
|
||||
const { data: apiKey } = await api.keyApi.createApiKey(
|
||||
{ aPIKeyCreateDto: { name: 'CLI Test' } },
|
||||
{ headers: { Authorization: `Bearer ${admin.accessToken}` } },
|
||||
);
|
||||
|
||||
api.setApiKey(apiKey.secret);
|
||||
|
||||
return api;
|
||||
};
|
||||
|
||||
export const spyOnConsole = () => jest.spyOn(console, 'log').mockImplementation();
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
],
|
||||
"coverageDirectory": "./coverage",
|
||||
"moduleNameMapper": {
|
||||
"^@api(|/.*)$": "<rootDir>../server/e2e/client/$1",
|
||||
"^@test(|/.*)$": "<rootDir>../server/test/$1",
|
||||
"^@app/immich(|/.*)$": "<rootDir>../server/src/immich/$1",
|
||||
"^@app/infra(|/.*)$": "<rootDir>../server/src/infra/$1",
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { APIKeyCreateResponseDto } from '@app/domain';
|
||||
import { api } from '@api';
|
||||
import { restoreTempFolder, testApp } from '@test/../e2e/jobs/utils';
|
||||
import { LoginResponseDto } from '@immich/sdk';
|
||||
import { LoginKey } from 'src/commands/login/key';
|
||||
import { LoginError } from 'src/cores/errors/login-error';
|
||||
import { CLI_BASE_OPTIONS, spyOnConsole } from 'test/cli-test-utils';
|
||||
import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils';
|
||||
import { LoginCommand } from '../../src/commands/login';
|
||||
|
||||
describe(`login-key (e2e)`, () => {
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
let apiKey: APIKeyCreateResponseDto;
|
||||
let apiKey: string;
|
||||
let instanceUrl: string;
|
||||
|
||||
spyOnConsole();
|
||||
|
||||
beforeAll(async () => {
|
||||
server = (await testApp.create()).getHttpServer();
|
||||
await testApp.create();
|
||||
if (!process.env.IMMICH_INSTANCE_URL) {
|
||||
throw new Error('IMMICH_INSTANCE_URL environment variable not set');
|
||||
} else {
|
||||
@@ -30,19 +25,18 @@ describe(`login-key (e2e)`, () => {
|
||||
beforeEach(async () => {
|
||||
await testApp.reset();
|
||||
await restoreTempFolder();
|
||||
await api.authApi.adminSignUp(server);
|
||||
admin = await api.authApi.adminLogin(server);
|
||||
apiKey = await api.apiKeyApi.createApiKey(server, admin.accessToken);
|
||||
process.env.IMMICH_API_KEY = apiKey.secret;
|
||||
|
||||
const api = await setup();
|
||||
apiKey = api.apiKey;
|
||||
});
|
||||
|
||||
it('should error when providing an invalid API key', async () => {
|
||||
await expect(async () => await new LoginKey(CLI_BASE_OPTIONS).run(instanceUrl, 'invalid')).rejects.toThrow(
|
||||
new LoginError(`Failed to connect to server ${instanceUrl}: Request failed with status code 401`),
|
||||
await expect(new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, 'invalid')).rejects.toThrow(
|
||||
`Failed to connect to server ${instanceUrl}: Request failed with status code 401`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should log in when providing the correct API key', async () => {
|
||||
await new LoginKey(CLI_BASE_OPTIONS).run(instanceUrl, apiKey.secret);
|
||||
await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { APIKeyCreateResponseDto } from '@app/domain';
|
||||
import { api } from '@api';
|
||||
import { restoreTempFolder, testApp } from '@test/../e2e/jobs/utils';
|
||||
import { LoginResponseDto } from '@immich/sdk';
|
||||
import { ServerInfo } from 'src/commands/server-info';
|
||||
import { CLI_BASE_OPTIONS, spyOnConsole } from 'test/cli-test-utils';
|
||||
import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils';
|
||||
import { ServerInfoCommand } from '../../src/commands/server-info.command';
|
||||
|
||||
describe(`server-info (e2e)`, () => {
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
let apiKey: APIKeyCreateResponseDto;
|
||||
const consoleSpy = spyOnConsole();
|
||||
|
||||
beforeAll(async () => {
|
||||
server = (await testApp.create()).getHttpServer();
|
||||
await testApp.create();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -23,20 +17,18 @@ describe(`server-info (e2e)`, () => {
|
||||
beforeEach(async () => {
|
||||
await testApp.reset();
|
||||
await restoreTempFolder();
|
||||
await api.authApi.adminSignUp(server);
|
||||
admin = await api.authApi.adminLogin(server);
|
||||
apiKey = await api.apiKeyApi.createApiKey(server, admin.accessToken);
|
||||
process.env.IMMICH_API_KEY = apiKey.secret;
|
||||
const api = await setup();
|
||||
process.env.IMMICH_API_KEY = api.apiKey;
|
||||
});
|
||||
|
||||
it('should show server version', async () => {
|
||||
await new ServerInfo(CLI_BASE_OPTIONS).run();
|
||||
await new ServerInfoCommand(CLI_BASE_OPTIONS).run();
|
||||
|
||||
expect(consoleSpy.mock.calls).toEqual([
|
||||
[expect.stringMatching(new RegExp('Server is running version \\d+.\\d+.\\d+'))],
|
||||
[expect.stringMatching('Supported image types: .*')],
|
||||
[expect.stringMatching('Supported video types: .*')],
|
||||
['Images: 0, Videos: 0, Total: 0'],
|
||||
[expect.stringMatching(new RegExp('Server Version: \\d+.\\d+.\\d+'))],
|
||||
[expect.stringMatching('Image Types: .*')],
|
||||
[expect.stringMatching('Video Types: .*')],
|
||||
['Statistics:\n Images: 0\n Videos: 0\n Total: 0'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { APIKeyCreateResponseDto } from '@app/domain';
|
||||
import { api } from '@api';
|
||||
import { IMMICH_TEST_ASSET_PATH, restoreTempFolder, testApp } from '@test/../e2e/jobs/utils';
|
||||
import { LoginResponseDto } from '@immich/sdk';
|
||||
import { Upload } from 'src/commands/upload';
|
||||
import { CLI_BASE_OPTIONS, spyOnConsole } from 'test/cli-test-utils';
|
||||
import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils';
|
||||
import { UploadCommand } from '../../src/commands/upload.command';
|
||||
import { ImmichApi } from '../../src/services/api.service';
|
||||
|
||||
describe(`upload (e2e)`, () => {
|
||||
let server: any;
|
||||
let admin: LoginResponseDto;
|
||||
let apiKey: APIKeyCreateResponseDto;
|
||||
let api: ImmichApi;
|
||||
|
||||
spyOnConsole();
|
||||
|
||||
beforeAll(async () => {
|
||||
server = (await testApp.create()).getHttpServer();
|
||||
await testApp.create();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -23,60 +20,58 @@ describe(`upload (e2e)`, () => {
|
||||
beforeEach(async () => {
|
||||
await testApp.reset();
|
||||
await restoreTempFolder();
|
||||
await api.authApi.adminSignUp(server);
|
||||
admin = await api.authApi.adminLogin(server);
|
||||
apiKey = await api.apiKeyApi.createApiKey(server, admin.accessToken);
|
||||
process.env.IMMICH_API_KEY = apiKey.secret;
|
||||
api = await setup();
|
||||
process.env.IMMICH_API_KEY = api.apiKey;
|
||||
});
|
||||
|
||||
it('should upload a folder recursively', async () => {
|
||||
await new Upload(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true });
|
||||
const assets = await api.assetApi.getAllAssets(server, admin.accessToken);
|
||||
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true });
|
||||
const { data: assets } = await api.assetApi.getAllAssets({}, { headers: { 'x-api-key': api.apiKey } });
|
||||
expect(assets.length).toBeGreaterThan(4);
|
||||
});
|
||||
|
||||
it('should not create a new album', async () => {
|
||||
await new Upload(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true });
|
||||
const albums = await api.albumApi.getAllAlbums(server, admin.accessToken);
|
||||
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true });
|
||||
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
|
||||
expect(albums.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should create album from folder name', async () => {
|
||||
await new Upload(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
recursive: true,
|
||||
album: true,
|
||||
});
|
||||
|
||||
const albums = await api.albumApi.getAllAlbums(server, admin.accessToken);
|
||||
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
|
||||
expect(albums.length).toEqual(1);
|
||||
const natureAlbum = albums[0];
|
||||
expect(natureAlbum.albumName).toEqual('nature');
|
||||
});
|
||||
|
||||
it('should add existing assets to album', async () => {
|
||||
await new Upload(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
// Upload again, but this time add to album
|
||||
await new Upload(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
// upload again, but this time add to album
|
||||
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
recursive: true,
|
||||
album: true,
|
||||
});
|
||||
|
||||
const albums = await api.albumApi.getAllAlbums(server, admin.accessToken);
|
||||
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
|
||||
expect(albums.length).toEqual(1);
|
||||
const natureAlbum = albums[0];
|
||||
expect(natureAlbum.albumName).toEqual('nature');
|
||||
});
|
||||
|
||||
it('should upload to the specified album name', async () => {
|
||||
await new Upload(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], {
|
||||
recursive: true,
|
||||
albumName: 'testAlbum',
|
||||
});
|
||||
|
||||
const albums = await api.albumApi.getAllAlbums(server, admin.accessToken);
|
||||
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
|
||||
expect(albums.length).toEqual(1);
|
||||
const testAlbum = albums[0];
|
||||
expect(testAlbum.albumName).toEqual('testAlbum');
|
||||
|
||||
Reference in New Issue
Block a user