feat: migrate secrets to Infisical
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { validateRuntimeEnvironment } from './runtime-environment.js';
|
||||
|
||||
const validAppEnvironment = {
|
||||
APP_ORIGIN: 'https://stage.shd.build',
|
||||
ORIGIN: 'https://stage.shd.build',
|
||||
PASSKEY_RP_ID: 'shd.build',
|
||||
SCHEDULER_TOKEN: 'scheduler-secret-with-32-characters',
|
||||
TDR_DATABASE_PATH: '/app/data/shd-build-staging.db',
|
||||
TDR_AVATAR_STORAGE_PATH: '/app/data/avatars',
|
||||
TDR_GAME_ICON_STORAGE_PATH: '/app/data/game-icons',
|
||||
};
|
||||
|
||||
describe('validateRuntimeEnvironment', () => {
|
||||
it('accepts the hosted app contract without optional providers', () => {
|
||||
expect(validateRuntimeEnvironment('app', validAppEnvironment)).toEqual([]);
|
||||
});
|
||||
|
||||
it('reports every missing required app value', () => {
|
||||
expect(validateRuntimeEnvironment('app', {})).toEqual([
|
||||
'APP_ORIGIN is required.',
|
||||
'ORIGIN is required.',
|
||||
'PASSKEY_RP_ID is required.',
|
||||
'SCHEDULER_TOKEN is required.',
|
||||
'TDR_DATABASE_PATH is required.',
|
||||
'TDR_AVATAR_STORAGE_PATH is required.',
|
||||
'TDR_GAME_ICON_STORAGE_PATH is required.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('validates canonical origins and passkey hostname scope', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
APP_ORIGIN: 'https://stage.shd.build/sign-in',
|
||||
}),
|
||||
).toContain('APP_ORIGIN must contain only a URL origin.');
|
||||
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
PASSKEY_RP_ID: 'example.com',
|
||||
}),
|
||||
).toContain(
|
||||
'PASSKEY_RP_ID must equal APP_ORIGIN hostname or be its registrable parent domain.',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects mismatched app and adapter hostnames', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
ORIGIN: 'https://other.shd.build',
|
||||
}),
|
||||
).toContain('APP_ORIGIN and ORIGIN must use the same hostname.');
|
||||
});
|
||||
|
||||
it('requires optional integration credentials only as pairs', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
DISCORD_CLIENT_ID: 'client-id',
|
||||
TURNSTILE_SECRET_KEY: 'secret',
|
||||
}),
|
||||
).toEqual([
|
||||
'DISCORD_CLIENT_ID and DISCORD_CLIENT_SECRET must be configured together.',
|
||||
'TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY must be configured together.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('allows SMTP defaults without enabling email and validates enabled SMTP', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
SMTP_PORT: '587',
|
||||
SMTP_SECURE: 'false',
|
||||
}),
|
||||
).toEqual([]);
|
||||
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
SMTP_HOST: 'smtp.example.com',
|
||||
SMTP_USER: 'agent',
|
||||
SMTP_PORT: '70000',
|
||||
}),
|
||||
).toEqual([
|
||||
'SMTP_HOST and SMTP_FROM must be configured together.',
|
||||
'SMTP_USER and SMTP_PASSWORD must be configured together.',
|
||||
'SMTP_PORT must be an integer from 1 through 65535.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('validates optional redirect URLs when supplied', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
DISCORD_REDIRECT_URI: 'ftp://stage.shd.build/callback',
|
||||
}),
|
||||
).toContain('DISCORD_REDIRECT_URI must be a valid HTTP or HTTPS URL.');
|
||||
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
DISCORD_REDIRECT_URI: 'https://dev.shd.build/auth/discord/callback',
|
||||
}),
|
||||
).toContain(
|
||||
'DISCORD_REDIRECT_URI must use APP_ORIGIN hostname and /auth/discord/callback.',
|
||||
);
|
||||
});
|
||||
|
||||
it('validates hosted paths and SMTP TLS configuration', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('app', {
|
||||
...validAppEnvironment,
|
||||
SMTP_SECURE: 'yes',
|
||||
TDR_DATABASE_PATH: './data/staging.db',
|
||||
}),
|
||||
).toEqual([
|
||||
'SMTP_SECURE must be true or false.',
|
||||
'TDR_DATABASE_PATH must be an absolute hosted path.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('accepts a configured scheduler and rejects missing or invalid values', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('scheduler', {
|
||||
SCHEDULER_URL: 'http://app:3000/api/internal/scheduler',
|
||||
SCHEDULER_TOKEN: 'scheduler-secret-with-32-characters',
|
||||
}),
|
||||
).toEqual([]);
|
||||
|
||||
expect(
|
||||
validateRuntimeEnvironment('scheduler', {
|
||||
SCHEDULER_URL: 'not a URL',
|
||||
}),
|
||||
).toEqual([
|
||||
'SCHEDULER_TOKEN is required.',
|
||||
'SCHEDULER_URL must be a valid HTTP or HTTPS URL.',
|
||||
'SCHEDULER_URL must use the internal app scheduler endpoint.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('requires a strong scheduler token and fixed internal endpoint', () => {
|
||||
expect(
|
||||
validateRuntimeEnvironment('scheduler', {
|
||||
SCHEDULER_URL: 'https://example.com/api/internal/scheduler',
|
||||
SCHEDULER_TOKEN: 'too-short',
|
||||
}),
|
||||
).toEqual([
|
||||
'SCHEDULER_URL must use the internal app scheduler endpoint.',
|
||||
'SCHEDULER_TOKEN must contain at least 32 non-whitespace characters.',
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects unsupported runtime modes', () => {
|
||||
expect(() =>
|
||||
validateRuntimeEnvironment('worker' as 'app', validAppEnvironment),
|
||||
).toThrow('Unsupported runtime mode: worker');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user