mirror of
https://github.com/unraid/api.git
synced 2026-01-01 22:20:05 -06:00
fix: update tests
This commit is contained in:
@@ -58,15 +58,15 @@ test('getUrlForServer - field exists, ssl yes, port empty', () => {
|
|||||||
expect(result).toMatchInlineSnapshot('"https://192.168.1.1/"');
|
expect(result).toMatchInlineSnapshot('"https://192.168.1.1/"');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getUrlForServer - field exists, ssl auto', () => {
|
test('getUrlForServer - field exists, ssl auto', async () => {
|
||||||
const getResult = async () => getUrlForServer({
|
const getResult = async () => getUrlForServer({
|
||||||
nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'auto', httpPort: 123, httpsPort: 445 } as const as Nginx,
|
nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'auto', httpPort: 123, httpsPort: 445 } as const as Nginx,
|
||||||
field: 'lanIp',
|
field: 'lanIp',
|
||||||
});
|
});
|
||||||
void expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Cannot get IP Based URL for field: "lanIp" SSL mode auto]`);
|
await expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Cannot get IP Based URL for field: "lanIp" SSL mode auto]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getUrlForServer - field does not exist, ssl disabled', () => {
|
test('getUrlForServer - field does not exist, ssl disabled', async () => {
|
||||||
const getResult = async () => getUrlForServer(
|
const getResult = async () => getUrlForServer(
|
||||||
{
|
{
|
||||||
nginx: { lanIp: '192.168.1.1', sslEnabled: false, sslMode: 'no' } as const as Nginx,
|
nginx: { lanIp: '192.168.1.1', sslEnabled: false, sslMode: 'no' } as const as Nginx,
|
||||||
@@ -76,7 +76,7 @@ test('getUrlForServer - field does not exist, ssl disabled', () => {
|
|||||||
// @ts-expect-error Field doesn't exist
|
// @ts-expect-error Field doesn't exist
|
||||||
field: 'idontexist',
|
field: 'idontexist',
|
||||||
});
|
});
|
||||||
void expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: IP URL Resolver: Could not resolve any access URL for field: "idontexist", is FQDN?: false]`);
|
await expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: IP URL Resolver: Could not resolve any access URL for field: "idontexist", is FQDN?: false]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getUrlForServer - FQDN - field exists, port non-empty', () => {
|
test('getUrlForServer - FQDN - field exists, port non-empty', () => {
|
||||||
@@ -104,13 +104,13 @@ test.each([
|
|||||||
expect(result.toString()).toBe('https://my-fqdn.unraid.net/');
|
expect(result.toString()).toBe('https://my-fqdn.unraid.net/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getUrlForServer - field does not exist, ssl disabled', () => {
|
test('getUrlForServer - field does not exist, ssl disabled', async () => {
|
||||||
const getResult = async () => getUrlForServer({ nginx:
|
const getResult = async () => getUrlForServer({ nginx:
|
||||||
{ lanFqdn: 'my-fqdn.unraid.net' } as const as Nginx,
|
{ lanFqdn: 'my-fqdn.unraid.net' } as const as Nginx,
|
||||||
ports: { portSsl: '', port: '', defaultUrl: new URL('https://my-default-url.unraid.net') },
|
ports: { portSsl: '', port: '', defaultUrl: new URL('https://my-default-url.unraid.net') },
|
||||||
// @ts-expect-error Field doesn't exist
|
// @ts-expect-error Field doesn't exist
|
||||||
field: 'idontexist' });
|
field: 'idontexist' });
|
||||||
void expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: IP URL Resolver: Could not resolve any access URL for field: "idontexist", is FQDN?: false]`);
|
await expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: IP URL Resolver: Could not resolve any access URL for field: "idontexist", is FQDN?: false]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('integration test, loading nginx ini and generating all URLs', async () => {
|
test('integration test, loading nginx ini and generating all URLs', async () => {
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { expect, test } from 'vitest';
|
import { expect, test } from 'vitest';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { store } from '@app/store';
|
import { store } from '@app/store';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
test('Before init returns default values for all fields', async () => {
|
test('Before init returns default values for all fields', async () => {
|
||||||
const state = store.getState().config;
|
const state = store.getState().config;
|
||||||
expect(state).toMatchInlineSnapshot(`
|
expect(state).toMatchInlineSnapshot(`
|
||||||
@@ -74,7 +80,7 @@ test('After init returns values from cfg file for all fields', async () => {
|
|||||||
dynamicRemoteAccessType: 'DISABLED',
|
dynamicRemoteAccessType: 'DISABLED',
|
||||||
email: 'test@example.com',
|
email: 'test@example.com',
|
||||||
idtoken: '',
|
idtoken: '',
|
||||||
localApiKey: '',
|
localApiKey: '426b62b4d51e441fa97a93dfa1259920390a6eb61bd8675db0caa18dd0e414e9',
|
||||||
refreshtoken: '',
|
refreshtoken: '',
|
||||||
regWizTime: '1611175408732_0951-1653-3509-FBA155FA23C0',
|
regWizTime: '1611175408732_0951-1653-3509-FBA155FA23C0',
|
||||||
upnpEnabled: 'no',
|
upnpEnabled: 'no',
|
||||||
@@ -127,7 +133,7 @@ test('updateUserConfig merges in changes to current state', async () => {
|
|||||||
dynamicRemoteAccessType: 'DISABLED',
|
dynamicRemoteAccessType: 'DISABLED',
|
||||||
email: 'test@example.com',
|
email: 'test@example.com',
|
||||||
idtoken: '',
|
idtoken: '',
|
||||||
localApiKey: '',
|
localApiKey: '426b62b4d51e441fa97a93dfa1259920390a6eb61bd8675db0caa18dd0e414e9',
|
||||||
refreshtoken: '',
|
refreshtoken: '',
|
||||||
regWizTime: '1611175408732_0951-1653-3509-FBA155FA23C0',
|
regWizTime: '1611175408732_0951-1653-3509-FBA155FA23C0',
|
||||||
upnpEnabled: 'no',
|
upnpEnabled: 'no',
|
||||||
|
|||||||
@@ -2,17 +2,24 @@ import { Logger } from '@nestjs/common';
|
|||||||
import { readdir, readFile, writeFile } from 'fs/promises';
|
import { readdir, readFile, writeFile } from 'fs/promises';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { ensureDir } from 'fs-extra';
|
import { ensureDir } from 'fs-extra';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { ZodError } from 'zod';
|
import { ZodError } from 'zod';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import type { ApiKey, ApiKeyWithSecret } from '@app/graphql/generated/api/types';
|
import type { ApiKey, ApiKeyWithSecret } from '@app/graphql/generated/api/types';
|
||||||
import { ApiKeySchema, ApiKeyWithSecretSchema } from '@app/graphql/generated/api/operations';
|
import { ApiKeySchema, ApiKeyWithSecretSchema } from '@app/graphql/generated/api/operations';
|
||||||
import { Role } from '@app/graphql/generated/api/types';
|
import { Role } from '@app/graphql/generated/api/types';
|
||||||
import { getters } from '@app/store';
|
import { getters } from '@app/store';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { ApiKeyService } from './api-key.service';
|
import { ApiKeyService } from './api-key.service';
|
||||||
|
|
||||||
|
|
||||||
vi.mock('fs/promises', async () => ({
|
vi.mock('fs/promises', async () => ({
|
||||||
readdir: vi.fn(),
|
readdir: vi.fn(),
|
||||||
readFile: vi.fn(),
|
readFile: vi.fn(),
|
||||||
@@ -127,15 +134,14 @@ describe('ApiKeyService', () => {
|
|||||||
it('should create ApiKeyWithSecret with generated key', async () => {
|
it('should create ApiKeyWithSecret with generated key', async () => {
|
||||||
const saveSpy = vi.spyOn(apiKeyService, 'saveApiKey').mockResolvedValue();
|
const saveSpy = vi.spyOn(apiKeyService, 'saveApiKey').mockResolvedValue();
|
||||||
const { key, id, description, roles } = mockApiKeyWithSecret;
|
const { key, id, description, roles } = mockApiKeyWithSecret;
|
||||||
const inputName = 'Test API Key';
|
const name = 'Test API Key';
|
||||||
const expectedName = 'TEST_API_KEY';
|
|
||||||
|
|
||||||
const result = await apiKeyService.create(inputName, description ?? '', roles);
|
const result = await apiKeyService.create(name, description ?? '', roles);
|
||||||
|
|
||||||
expect(result).toMatchObject({
|
expect(result).toMatchObject({
|
||||||
id,
|
id,
|
||||||
key,
|
key,
|
||||||
name: expectedName,
|
name: name,
|
||||||
description,
|
description,
|
||||||
roles,
|
roles,
|
||||||
createdAt: expect.any(String),
|
createdAt: expect.any(String),
|
||||||
@@ -148,7 +154,7 @@ describe('ApiKeyService', () => {
|
|||||||
const saveSpy = vi.spyOn(apiKeyService, 'saveApiKey');
|
const saveSpy = vi.spyOn(apiKeyService, 'saveApiKey');
|
||||||
|
|
||||||
await expect(apiKeyService.create('', 'desc', [Role.GUEST])).rejects.toThrow(
|
await expect(apiKeyService.create('', 'desc', [Role.GUEST])).rejects.toThrow(
|
||||||
'API key name is required'
|
'API key name must be alphanumeric + spaces'
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(apiKeyService.create('name', 'desc', [])).rejects.toThrow(
|
await expect(apiKeyService.create('name', 'desc', [])).rejects.toThrow(
|
||||||
@@ -300,13 +306,11 @@ describe('ApiKeyService', () => {
|
|||||||
expect(readFile).toHaveBeenCalledTimes(2);
|
expect(readFile).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw authentication error when file read fails', async () => {
|
it('Should return null if an API key is invalid', async () => {
|
||||||
vi.mocked(readdir).mockResolvedValue(['key1.json'] as any);
|
vi.mocked(readdir).mockResolvedValue(['key1.json'] as any);
|
||||||
vi.mocked(readFile).mockRejectedValue(new Error('Read error'));
|
vi.mocked(readFile).mockRejectedValue(new Error('Read error'));
|
||||||
|
|
||||||
await expect(apiKeyService.findByKey(mockApiKeyWithSecret.key)).rejects.toThrow(
|
await expect(apiKeyService.findByKey(mockApiKeyWithSecret.key)).resolves.toBeNull();
|
||||||
'Authentication system error'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw specific error for corrupted JSON', async () => {
|
it('should throw specific error for corrupted JSON', async () => {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export class ApiKeyService implements OnModuleInit {
|
|||||||
if (/^[\p{L}\p{N} ]+$/u.test(name)) {
|
if (/^[\p{L}\p{N} ]+$/u.test(name)) {
|
||||||
return name;
|
return name;
|
||||||
} else {
|
} else {
|
||||||
throw new GraphQLError('API key name must be alphanumeric and can only contain spaces');
|
throw new GraphQLError('API key name must be alphanumeric + spaces');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ export class ApiKeyService implements OnModuleInit {
|
|||||||
try {
|
try {
|
||||||
const files = await readdir(this.basePath);
|
const files = await readdir(this.basePath);
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of (files ?? [])) {
|
||||||
if (!file.endsWith('.json')) continue;
|
if (!file.endsWith('.json')) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -233,7 +233,7 @@ export class ApiKeyService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logger.error(`Failed to read API key storage: ${error}`);
|
this.logger.error(`Failed to read API key storage: ${error}`);
|
||||||
throw new GraphQLError('Authentication system unavailable');
|
throw new GraphQLError('Authentication system unavailable - please see logs');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user