feat: upgrade a ton of dependencies (#842)

* feat: upgrade a ton of dependencies
This commit is contained in:
Eli Bosley
2024-01-12 13:05:51 -05:00
committed by GitHub
parent 7061be60f4
commit d73324a141
27 changed files with 5467 additions and 2393 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[api]
version="3.3.0+eff31423"
version="3.4.0"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
+2 -2
View File
@@ -1,5 +1,5 @@
[api]
version="3.3.0+eff31423"
version="3.4.0"
extraOrigins="https://google.com,https://test.com"
[local]
[notifier]
@@ -21,4 +21,4 @@ dynamicRemoteAccessType="DISABLED"
[upc]
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
[connectionStatus]
minigraph="ERROR_RETRYING"
minigraph="PRE_INIT"
+4914 -2299
View File
File diff suppressed because it is too large Load Diff
+73 -73
View File
@@ -34,8 +34,8 @@
"tsc": "tsc --noEmit",
"lint": "DEBUG=eslint:cli-engine eslint . --config .eslintrc.cjs",
"lint:fix": "DEBUG=eslint:cli-engine eslint . --fix --config .eslintrc.cjs",
"test:watch": "vitest --segfault-retry=3 --no-threads",
"test": "vitest run --segfault-retry=3 --no-threads",
"test:watch": "vitest --segfault-retry=3 --pool=forks",
"test": "vitest run --segfault-retry=3 --pool=forks",
"coverage": "vitest run --segfault-retry=3 --coverage",
"patch:subscriptions-transport-ws": "node ./.scripts/patches/subscriptions-transport-ws.cjs",
"release": "standard-version",
@@ -59,21 +59,21 @@
"unraid-api"
],
"dependencies": {
"@apollo/client": "^3.7.12",
"@apollo/server": "^4.6.0",
"@apollo/client": "^3.8.9",
"@apollo/server": "^4.10.0",
"@as-integrations/fastify": "^2.1.1",
"@graphql-codegen/client-preset": "^4.0.0",
"@graphql-codegen/client-preset": "^4.1.0",
"@graphql-tools/load-files": "^7.0.0",
"@graphql-tools/merge": "^9.0.0",
"@graphql-tools/schema": "^10.0.0",
"@graphql-tools/utils": "^10.0.0",
"@graphql-tools/merge": "^9.0.1",
"@graphql-tools/schema": "^10.0.2",
"@graphql-tools/utils": "^10.0.12",
"@nestjs/apollo": "^12.0.11",
"@nestjs/core": "^10.2.9",
"@nestjs/core": "^10.3.0",
"@nestjs/graphql": "^12.0.11",
"@nestjs/passport": "^10.0.2",
"@nestjs/platform-fastify": "^10.2.9",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-fastify": "^10.3.0",
"@nestjs/schedule": "^4.0.0",
"@reduxjs/toolkit": "^1.9.5",
"@reduxjs/toolkit": "^2.0.1",
"@reflet/cron": "^1.3.1",
"@runonflux/nat-upnp": "^1.0.2",
"accesscontrol": "^2.2.1",
@@ -86,27 +86,27 @@
"catch-exit": "^1.2.2",
"chokidar": "^3.5.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"class-validator": "^0.14.1",
"cli-table": "^0.3.11",
"command-exists": "^1.2.9",
"convert": "^4.10.0",
"convert": "^4.14.1",
"cors": "^2.8.5",
"cross-fetch": "^4.0.0",
"docker-event-emitter": "^0.3.0",
"dockerode": "^3.3.5",
"dotenv": "^16.0.3",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"find-process": "^1.4.7",
"graphql": "^16.8.1",
"graphql-fields": "^2.0.3",
"graphql-scalars": "^1.21.3",
"graphql-scalars": "^1.22.4",
"graphql-subscriptions": "^2.0.0",
"graphql-tag": "^2.12.6",
"graphql-type-json": "^0.3.2",
"graphql-type-uuid": "^0.2.0",
"graphql-ws": "^5.14.2",
"graphql-ws": "^5.14.3",
"htpasswd-js": "^1.0.2",
"ini": "^4.1.0",
"ini": "^4.1.1",
"ip": "^1.1.8",
"jose": "^4.14.2",
"lodash": "^4.17.21",
@@ -114,96 +114,96 @@
"mustache": "^4.2.0",
"nanobus": "^4.5.0",
"nest-access-control": "^3.1.0",
"nestjs-pino": "^3.5.0",
"nestjs-pino": "^4.0.0",
"node-cache": "^5.1.2",
"node-window-polyfill": "^1.0.2",
"openid-client": "^5.4.0",
"openid-client": "^5.6.4",
"p-iteration": "^1.1.8",
"p-retry": "^4.6.2",
"passport-http-header-strategy": "^1.1.0",
"pidusage": "^3.0.2",
"pino": "^8.16.2",
"pino-http": "^8.5.1",
"pino-pretty": "^10.2.3",
"reflect-metadata": "^0.1.13",
"pino": "^8.17.2",
"pino-http": "^9.0.0",
"pino-pretty": "^10.3.1",
"reflect-metadata": "^0.1.14",
"request": "^2.88.2",
"semver": "^7.4.0",
"semver": "^7.5.4",
"stoppable": "^1.1.0",
"systeminformation": "^5.21.2",
"ts-command-line-args": "^2.5.0",
"uuid": "^9.0.0",
"systeminformation": "^5.21.22",
"ts-command-line-args": "^2.5.1",
"uuid": "^9.0.1",
"ws": "^8.13.0",
"wtfnode": "^0.9.1",
"xhr2": "^0.2.1",
"zod": "^3.22.2"
"zod": "^3.22.4"
},
"devDependencies": {
"@babel/runtime": "^7.21.0",
"@babel/runtime": "^7.23.8",
"@graphql-codegen/add": "^5.0.0",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/fragment-matcher": "^5.0.0",
"@graphql-codegen/import-types-preset": "^3.0.0",
"@graphql-codegen/typed-document-node": "^5.0.0",
"@graphql-codegen/typescript": "^4.0.0",
"@graphql-codegen/typescript-operations": "^4.0.0",
"@graphql-codegen/typed-document-node": "^5.0.1",
"@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1",
"@graphql-codegen/typescript-resolvers": "4.0.1",
"@graphql-typed-document-node/core": "^3.2.0",
"@nestjs/testing": "^10.2.10",
"@swc/core": "^1.3.81",
"@types/async-exit-hook": "^2.0.0",
"@types/btoa": "^1.2.3",
"@types/bytes": "^3.1.1",
"@types/cli-table": "^0.3.1",
"@types/command-exists": "^1.2.0",
"@nestjs/testing": "^10.3.0",
"@swc/core": "^1.3.102",
"@types/async-exit-hook": "^2.0.2",
"@types/btoa": "^1.2.5",
"@types/bytes": "^3.1.4",
"@types/cli-table": "^0.3.4",
"@types/command-exists": "^1.2.3",
"@types/dockerode": "^3.3.16",
"@types/express": "^4.17.17",
"@types/graphql-fields": "^1.3.5",
"@types/graphql-type-uuid": "^0.2.3",
"@types/ini": "^1.3.31",
"@types/lodash": "^4.14.192",
"@types/mustache": "^4.2.2",
"@types/node": "^18.17.12",
"@types/pidusage": "^2.0.2",
"@types/pify": "^5.0.1",
"@types/semver": "^7.3.13",
"@types/sendmail": "^1.4.4",
"@types/stoppable": "^1.1.1",
"@types/uuid": "^9.0.1",
"@types/express": "^4.17.21",
"@types/graphql-fields": "^1.3.9",
"@types/graphql-type-uuid": "^0.2.6",
"@types/ini": "^4.1.0",
"@types/lodash": "^4.14.202",
"@types/mustache": "^4.2.5",
"@types/node": "^20.11.0",
"@types/pidusage": "^2.0.5",
"@types/pify": "^5.0.4",
"@types/semver": "^7.5.6",
"@types/sendmail": "^1.4.7",
"@types/stoppable": "^1.1.3",
"@types/uuid": "^9.0.7",
"@types/ws": "^8.5.4",
"@types/wtfnode": "^0.7.0",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"@types/wtfnode": "^0.7.3",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"@unraid/eslint-config": "github:unraid/eslint-config",
"@vitest/coverage-v8": "^0.34.1",
"@vitest/ui": "^0.34.0",
"@vitest/coverage-v8": "^1.2.0",
"@vitest/ui": "^1.2.0",
"camelcase-keys": "^8.0.2",
"cz-conventional-changelog": "3.3.0",
"eslint": "^8.38.0",
"eslint-import-resolver-typescript": "^3.6.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-unicorn": "^48.0.1",
"eslint-plugin-unused-imports": "^2.0.0",
"eslint": "^8.56.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-unicorn": "^50.0.1",
"eslint-plugin-unused-imports": "^3.0.0",
"execa": "^7.1.1",
"filter-obj": "^5.1.0",
"got": "^13.0.0",
"graphql-codegen-typescript-validation-schema": "^0.11.0",
"graphql-codegen-typescript-validation-schema": "^0.12.1",
"ip-regex": "^5.0.0",
"json-difference": "^1.9.1",
"json-difference": "^1.16.0",
"map-obj": "^5.0.2",
"p-props": "^5.0.0",
"path-exists": "^5.0.0",
"path-type": "^5.0.0",
"pkg": "^5.8.1",
"pretty-bytes": "^6.1.0",
"pretty-bytes": "^6.1.1",
"pretty-ms": "^8.0.0",
"standard-version": "^9.5.0",
"tsup": "^7.0.0",
"typescript": "^4.9.4",
"typesync": "^0.11.0",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.34.0",
"zx": "^7.2.1"
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"typesync": "^0.12.1",
"vite-tsconfig-paths": "^4.2.3",
"vitest": "^1.2.0",
"zx": "^7.2.3"
},
"optionalDependencies": {
"@vmngr/libvirt": "github:unraid/libvirt"
@@ -0,0 +1,39 @@
import { test, expect } from 'vitest';
import { parse } from 'ini';
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
import { Serializer } from 'multi-ini';
test('MultiIni breaks when serializing an object with a boolean inside', async () => {
const objectToSerialize = {
root: {
anonMode: false,
},
};
const serializer = new Serializer({ keep_quotes: false });
expect(serializer.serialize(objectToSerialize)).toMatchInlineSnapshot(`
"[root]
anonMode=false
"
`)
});
test('MultiIni can safely serialize an object with a boolean inside', async () => {
const objectToSerialize = {
root: {
anonMode: false,
},
};
expect(safelySerializeObjectToIni(objectToSerialize)).toMatchInlineSnapshot(`
"[root]
anonMode="false"
"
`);
const result = safelySerializeObjectToIni(objectToSerialize);
expect(parse(result)).toMatchInlineSnapshot(`
{
"root": {
"anonMode": false,
},
}
`);
});
@@ -0,0 +1,114 @@
import { test, expect } from 'vitest';
import { parseConfig } from '@app/core/utils/misc/parse-config';
import { Parser as MultiIniParser } from 'multi-ini';
import { readFile, writeFile } from 'fs/promises';
import { parse } from 'ini';
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
const iniTestData = `["root"]
idx="0"
name="root"
desc="Console and webGui login account"
passwd="yes"
["xo"]
idx="1"
name="xo"
desc=""
passwd="yes"
["test_user"]
idx="2"
name="test_user"
desc=""
passwd="no"`;
test('it loads a config from a passed in ini file successfully', () => {
const res = parseConfig<any>({
file: iniTestData,
type: 'ini',
});
expect(res).toMatchInlineSnapshot(`
{
"root": {
"desc": "Console and webGui login account",
"idx": "0",
"name": "root",
"passwd": "yes",
},
"testUser": {
"desc": "",
"idx": "2",
"name": "test_user",
"passwd": "no",
},
"xo": {
"desc": "",
"idx": "1",
"name": "xo",
"passwd": "yes",
},
}
`);
expect(res?.root.desc).toEqual('Console and webGui login account');
});
test('it loads a config from disk properly', () => {
const path = './dev/states/var.ini';
const res = parseConfig<any>({ filePath: path, type: 'ini' });
expect(res.DOMAIN_SHORT).toEqual(undefined);
expect(res.domainShort).toEqual('');
expect(res.shareCount).toEqual('0');
});
test('Confirm Multi-Ini Parser Still Broken', () => {
const parser = new MultiIniParser();
const res = parser.parse(iniTestData);
expect(res).toMatchInlineSnapshot('{}');
});
test('Combine Ini and Multi-Ini to read and then write a file with quotes', async () => {
const parsedFile = parse(iniTestData);
expect(parsedFile).toMatchInlineSnapshot(`
{
"root": {
"desc": "Console and webGui login account",
"idx": "0",
"name": "root",
"passwd": "yes",
},
"test_user": {
"desc": "",
"idx": "2",
"name": "test_user",
"passwd": "no",
},
"xo": {
"desc": "",
"idx": "1",
"name": "xo",
"passwd": "yes",
},
}
`);
const ini = safelySerializeObjectToIni(parsedFile);
await writeFile('/tmp/test.ini', ini);
const file = await readFile('/tmp/test.ini', 'utf-8');
expect(file).toMatchInlineSnapshot(`
"[root]
idx="0"
name="root"
desc="Console and webGui login account"
passwd="yes"
[xo]
idx="1"
name="xo"
desc=""
passwd="yes"
[test_user]
idx="2"
name="test_user"
desc=""
passwd="no"
"
`);
});
@@ -0,0 +1,9 @@
import { checkMothershipAuthentication } from "@app/graphql/resolvers/query/cloud/check-mothership-authentication";
import { expect, test } from "vitest";
import packageJson from '@app/../package.json'
test('It fails to authenticate with mothership with no credentials', async () => {
await expect(checkMothershipAuthentication('BAD', 'BAD')).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Failed to connect to https://mothership.unraid.net/ws with a "426" HTTP error.]`);
expect(packageJson.version).not.toBeNull();
await expect(checkMothershipAuthentication(packageJson.version, 'BAD_API_KEY')).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Invalid credentials]`);
}, 15_000)
@@ -0,0 +1,188 @@
import { expect, test } from 'vitest';
import { type Nginx } from '../../../../core/types/states/nginx';
import { getUrlForField, getUrlForServer, getServerIps, type NginxUrlFields } from '@app/graphql/resolvers/subscription/network';
import { store } from '@app/store';
import { loadStateFiles } from '@app/store/modules/emhttp';
import { loadConfigFile } from '@app/store/modules/config';
test.each([
[{ httpPort: 80, httpsPort: 443, url: 'my-default-url.com' }],
[{ httpPort: 123, httpsPort: 443, url: 'my-default-url.com' }],
[{ httpPort: 80, httpsPort: 12_345, url: 'my-default-url.com' }],
[{ httpPort: 212, httpsPort: 3_233, url: 'my-default-url.com' }],
[{ httpPort: 80, httpsPort: 443, url: 'https://BROKEN_URL' }],
])('getUrlForField', ({ httpPort, httpsPort, url }) => {
const responseInsecure = getUrlForField({
port: httpPort,
url,
});
const responseSecure = getUrlForField({
portSsl: httpsPort,
url,
});
if (httpPort === 80) {
expect(responseInsecure.port).toBe('');
} else {
expect(responseInsecure.port).toBe(httpPort.toString());
}
if (httpsPort === 443) {
expect(responseSecure.port).toBe('');
} else {
expect(responseSecure.port).toBe(httpsPort.toString());
}
});
test('getUrlForServer - field exists, ssl disabled', () => {
const result = getUrlForServer({ nginx: { lanIp: '192.168.1.1', sslEnabled: false, httpPort: 123, httpsPort: 445 } as const as Nginx,
field: 'lanIp',
});
expect(result).toMatchInlineSnapshot('"http://192.168.1.1:123/"');
});
test('getUrlForServer - field exists, ssl yes', () => {
const result = getUrlForServer({
nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'yes', httpPort: 123, httpsPort: 445 } as const as Nginx,
field: 'lanIp',
});
expect(result).toMatchInlineSnapshot('"https://192.168.1.1:445/"');
});
test('getUrlForServer - field exists, ssl yes, port empty', () => {
const result = getUrlForServer(
{ nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'yes', httpPort: 80, httpsPort: 443 } as const as Nginx,
field: 'lanIp',
});
expect(result).toMatchInlineSnapshot('"https://192.168.1.1/"');
});
test('getUrlForServer - field exists, ssl auto', () => {
const getResult = async () => getUrlForServer({
nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'auto', httpPort: 123, httpsPort: 445 } as const as Nginx,
field: 'lanIp',
});
void expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Cannot get IP Based URL for field: "lanIp" SSL mode auto]`);
});
test('getUrlForServer - field does not exist, ssl disabled', () => {
const getResult = async () => getUrlForServer(
{
nginx: { lanIp: '192.168.1.1', sslEnabled: false, sslMode: 'no' } as const as Nginx,
ports: {
port: ':123', portSsl: ':445', defaultUrl: new URL('https://my-default-url.unraid.net'),
},
// @ts-expect-error Field doesn't exist
field: 'idontexist',
});
void 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', () => {
const result = getUrlForServer({
nginx: { lanFqdn: 'my-fqdn.unraid.net', httpsPort: 445 } as const as Nginx,
field: 'lanFqdn',
});
expect(result).toMatchInlineSnapshot('"https://my-fqdn.unraid.net:445/"');
});
test('getUrlForServer - FQDN - field exists, port empty', () => {
const result = getUrlForServer({ nginx: { lanFqdn: 'my-fqdn.unraid.net', httpPort: 80, httpsPort: 443 } as const as Nginx,
field: 'lanFqdn',
});
expect(result).toMatchInlineSnapshot('"https://my-fqdn.unraid.net/"');
});
test.each([
[{ nginx: { lanFqdn: 'my-fqdn.unraid.net', sslEnabled: false, sslMode: 'no', httpPort: 80, httpsPort: 443 } as const as Nginx, field: 'lanFqdn' as NginxUrlFields }],
[{ nginx: { wanFqdn: 'my-fqdn.unraid.net', sslEnabled: true, sslMode: 'yes', httpPort: 80, httpsPort: 443 } as const as Nginx, field: 'wanFqdn' as NginxUrlFields }],
[{ nginx: { wanFqdn6: 'my-fqdn.unraid.net', sslEnabled: true, sslMode: 'auto', httpPort: 80, httpsPort: 443 } as const as Nginx, field: 'wanFqdn6' as NginxUrlFields }],
])('getUrlForServer - FQDN', ({ nginx, field }) => {
const result = getUrlForServer({ nginx, field });
expect(result.toString()).toBe('https://my-fqdn.unraid.net/');
});
test('getUrlForServer - field does not exist, ssl disabled', () => {
const getResult = async () => getUrlForServer({ nginx:
{ lanFqdn: 'my-fqdn.unraid.net' } as const as Nginx,
ports: { portSsl: '', port: '', defaultUrl: new URL('https://my-default-url.unraid.net') },
// @ts-expect-error Field doesn't exist
field: 'idontexist' });
void 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 () => {
await store.dispatch(loadStateFiles());
await store.dispatch(loadConfigFile());
const urls = getServerIps();
expect(urls.urls).toMatchInlineSnapshot(`
[
{
"ipv4": "https://tower.local:4443/",
"ipv6": "https://tower.local:4443/",
"name": "Default",
"type": "DEFAULT",
},
{
"ipv4": "https://192.168.1.150:4443/",
"name": "LAN IPv4",
"type": "LAN",
},
{
"ipv4": "https://tower:4443/",
"name": "LAN Name",
"type": "MDNS",
},
{
"ipv4": "https://tower.local:4443/",
"name": "LAN MDNS",
"type": "MDNS",
},
{
"ipv4": "https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443/",
"name": "LAN FQDN",
"type": "LAN",
},
{
"ipv4": "https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443/",
"name": "WAN FQDN",
"type": "WAN",
},
{
"ipv4": "https://10-252-0-1.hash.myunraid.net:4443/",
"name": "WG FQDN 0",
"type": "WIREGUARD",
},
{
"ipv4": "https://10-252-1-1.hash.myunraid.net:4443/",
"name": "WG FQDN 1",
"type": "WIREGUARD",
},
{
"ipv4": "https://10-253-3-1.hash.myunraid.net:4443/",
"name": "WG FQDN 3",
"type": "WIREGUARD",
},
{
"ipv4": "https://10-253-4-1.hash.myunraid.net:4443/",
"name": "WG FQDN 4",
"type": "WIREGUARD",
},
{
"ipv4": "https://10-253-5-1.hash.myunraid.net:4443/",
"name": "WG FQDN 55",
"type": "WIREGUARD",
},
]
`);
expect(urls.errors).toMatchInlineSnapshot(`
[
[Error: IP URL Resolver: Could not resolve any access URL for field: "lanIp6", is FQDN?: false],
[Error: IP URL Resolver: Could not resolve any access URL for field: "lanFqdn6", is FQDN?: true],
[Error: No URL Provided],
]
`);
});
@@ -0,0 +1,111 @@
import { API_KEY_STATUS } from '@app/mothership/api-key/api-key-types';
import * as apiKeyCheckJobs from '@app/mothership/jobs/api-key-check-jobs';
import * as apiKeyValidator from '@app/mothership/api-key/validate-api-key-with-keyserver';
import { describe, expect, it, vi } from 'vitest';
import { type RecursivePartial } from '@app/types/index';
import { type RootState } from '@app/store/index';
import { logoutUser } from '@app/store/modules/config';
describe('apiKeyCheckJob Tests', () => {
it('API Check Job (with success)', async () => {
const getState = vi.fn<[], RecursivePartial<RootState>>().mockReturnValue({
apiKey: { status: API_KEY_STATUS.PENDING_VALIDATION },
config: { remote: { apikey: '_______________________BIG_API_KEY_HERE_________________________' } },
emhttp: { var: { flashGuid: 'my-flash-guid', version: '6.11.5' } },
});
const dispatch = vi.fn();
const validationSpy = vi.spyOn(apiKeyValidator, 'validateApiKeyWithKeyServer').mockResolvedValue(API_KEY_STATUS.API_KEY_VALID);
await expect(apiKeyCheckJobs.apiKeyCheckJob(getState, dispatch)).resolves.toBe(true);
expect(validationSpy).toHaveBeenCalledOnce();
expect(dispatch).toHaveBeenLastCalledWith({
payload: API_KEY_STATUS.API_KEY_VALID,
type: 'apiKey/setApiKeyState',
});
});
it('API Check Job (with invalid length key)', async () => {
// Setup state
const getState = vi.fn<[], RecursivePartial<RootState>>().mockReturnValue({
apiKey: { status: API_KEY_STATUS.PENDING_VALIDATION },
config: { remote: { apikey: 'too-short-key' } },
emhttp: { var: { flashGuid: 'my-flash-guid', version: '6.11.5' } },
});
const dispatch = vi.fn();
const validationSpy = vi.spyOn(apiKeyValidator, 'validateApiKeyWithKeyServer').mockResolvedValue(API_KEY_STATUS.API_KEY_VALID);
await expect(apiKeyCheckJobs.apiKeyCheckJob(getState, dispatch)).resolves.toBe(false);
expect(dispatch).toHaveBeenCalledWith(expect.any(Function));
expect(validationSpy).not.toHaveBeenCalled();
});
it('API Check Job (with a failure that throws an error - NETWORK_ERROR)', async () => {
const getState = vi.fn<[], RecursivePartial<RootState>>().mockReturnValue({
apiKey: { status: API_KEY_STATUS.PENDING_VALIDATION },
config: { remote: { apikey: '_______________________BIG_API_KEY_HERE_________________________' } },
emhttp: { var: { flashGuid: 'my-flash-guid', version: '6.11.5' } },
});
const dispatch = vi.fn();
const validationSpy = vi.spyOn(apiKeyValidator, 'validateApiKeyWithKeyServer')
.mockResolvedValueOnce(API_KEY_STATUS.NETWORK_ERROR);
await expect(apiKeyCheckJobs.apiKeyCheckJob(getState, dispatch)).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Keyserver Failure, must retry]`);
expect(validationSpy).toHaveBeenCalledOnce();
expect(dispatch).toHaveBeenCalledWith({
payload: API_KEY_STATUS.NETWORK_ERROR,
type: 'apiKey/setApiKeyState',
});
});
it('API Check Job (with a failure that throws an error - INVALID_RESPONSE)', async () => {
const getState = vi.fn<[], RecursivePartial<RootState>>().mockReturnValue({
apiKey: { status: API_KEY_STATUS.PENDING_VALIDATION },
config: { remote: { apikey: '_______________________BIG_API_KEY_HERE_________________________' } },
emhttp: { var: { flashGuid: 'my-flash-guid', version: '6.11.5' } },
});
const dispatch = vi.fn();
const validationSpy = vi.spyOn(apiKeyValidator, 'validateApiKeyWithKeyServer')
.mockResolvedValueOnce(API_KEY_STATUS.INVALID_KEYSERVER_RESPONSE);
await expect(apiKeyCheckJobs.apiKeyCheckJob(getState, dispatch)).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Keyserver Failure, must retry]`);
expect(validationSpy).toHaveBeenCalledOnce();
expect(dispatch).toHaveBeenCalledWith({
payload: API_KEY_STATUS.INVALID_KEYSERVER_RESPONSE,
type: 'apiKey/setApiKeyState',
});
}, 10_000);
it('API Check Job (with failure that results in a log out)', async () => {
const getState = vi.fn<[], RecursivePartial<RootState>>().mockReturnValue({
apiKey: { status: API_KEY_STATUS.PENDING_VALIDATION },
config: { remote: { apikey: '_______________________BIG_API_KEY_HERE_________________________' } },
emhttp: { var: { flashGuid: 'my-flash-guid', version: '6.11.5' } },
});
const dispatch = vi.fn();
const validationSpy = vi.spyOn(apiKeyValidator, 'validateApiKeyWithKeyServer')
.mockResolvedValue(API_KEY_STATUS.API_KEY_INVALID);
await expect(apiKeyCheckJobs.apiKeyCheckJob(getState, dispatch)).resolves.toBe(false);
expect(validationSpy).toHaveBeenCalledOnce();
expect(dispatch).toHaveBeenCalledTimes(1);
expect(dispatch).toHaveBeenCalledWith(expect.any(Function));
}, 10_000);
});
-1
View File
@@ -1,5 +1,4 @@
import { apiLogger } from '@app/core/log';
import { BYPASS_PERMISSION_CHECKS } from '@app/environment';
import { ServerHeaderStrategy } from '@app/unraid-api/auth/header.strategy';
import { IS_PUBLIC_KEY } from '@app/unraid-api/auth/public.decorator';
import {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { ArrayResolver } from './array.resolver';
describe('ArrayResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { CloudResolver } from './cloud.resolver';
describe('CloudResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { ConfigResolver } from './config.resolver';
describe('ConfigResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { DisksResolver } from './disks.resolver';
describe('DisksResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { DisplayResolver } from './display.resolver';
describe('DisplayResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { DockerContainersResolver } from './docker-containers.resolver';
describe('DockerContainersResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { FlashResolver } from './flash.resolver';
describe('FlashResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { InfoResolver } from './info.resolver';
describe('InfoResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { NotificationsResolver } from './notifications.resolver';
describe('NotificationsResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { OnlineResolver } from './online.resolver';
describe('OnlineResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { OwnerResolver } from './owner.resolver';
describe('OwnerResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { RegistrationResolver } from './registration.resolver';
describe('RegistrationResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { VarsResolver } from './vars.resolver';
describe('VarsResolver', () => {
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { VmsResolver } from './vms.resolver';
describe('VmsResolver', () => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Test, TestingModule } from '@nestjs/testing';
import { Test, type TestingModule } from '@nestjs/testing';
import { RestService } from './rest.service';
describe('RestService', () => {
-1
View File
@@ -7,7 +7,6 @@ export default defineConfig(() => {
// Manually set NODE_ENV to make sure we always run tests in test mode
process.env.NODE_ENV = 'test';
return {
plugins: [tsconfigPaths()],
test: {
globals: true,