env.test.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import assert from 'node:assert/strict';
  2. import test from 'node:test';
  3. import { EnvironmentConfigurationError, loadConfig } from '../src/config/env.js';
  4. const validEnvironment: NodeJS.ProcessEnv = {
  5. NODE_ENV: 'test',
  6. HOST: '127.0.0.1',
  7. PORT: '4400',
  8. DATABASE_URL: 'postgres://user:password@127.0.0.1:5432/saas_voc',
  9. MIGRATION_DATABASE_URL: 'postgres://owner:password@127.0.0.1:5432/saas_voc',
  10. PARSE_APP_ID: 'test-app-id',
  11. PARSE_MASTER_KEY: 'a'.repeat(32),
  12. PARSE_MAINTENANCE_KEY: 'b'.repeat(32),
  13. PARSE_SERVER_URL: 'http://127.0.0.1:4400/parse',
  14. API_AUTH_MODE: 'disabled',
  15. FMODE_BASE_URL: 'http://127.0.0.1:3000/api/voc-e-commerce',
  16. FMODE_API_KEY: 'test-key',
  17. CORS_ORIGINS: 'http://127.0.0.1:4300',
  18. };
  19. test('loadConfig rejects missing secrets before startup', () => {
  20. const environment = { ...validEnvironment };
  21. delete environment.DATABASE_URL;
  22. delete environment.PARSE_MASTER_KEY;
  23. delete environment.PARSE_MAINTENANCE_KEY;
  24. delete environment.FMODE_API_KEY;
  25. assert.throws(
  26. () => loadConfig(environment),
  27. (error: unknown) => {
  28. assert.ok(error instanceof EnvironmentConfigurationError);
  29. assert.match(error.message, /DATABASE_URL/);
  30. assert.match(error.message, /PARSE_MASTER_KEY/);
  31. assert.match(error.message, /PARSE_MAINTENANCE_KEY/);
  32. assert.match(error.message, /FMODE_API_KEY/);
  33. return true;
  34. },
  35. );
  36. });
  37. test('loadConfig normalizes URLs and CORS origins', () => {
  38. const config = loadConfig({
  39. ...validEnvironment,
  40. FMODE_BASE_URL: 'http://127.0.0.1:3000/api/voc-e-commerce/',
  41. CORS_ORIGINS: 'http://127.0.0.1:4300, http://localhost:4200',
  42. });
  43. assert.equal(config.fmode.baseUrl, 'http://127.0.0.1:3000/api/voc-e-commerce');
  44. assert.equal(config.ai.baseUrl, 'https://api.fmode.cn');
  45. assert.equal(config.ai.defaultModel, 'deepseek-v4-pro');
  46. assert.equal(config.ai.token, '');
  47. assert.equal(config.database.migrationUrl, validEnvironment.MIGRATION_DATABASE_URL);
  48. assert.equal(config.worker.enabled, true);
  49. assert.equal(config.worker.staleAfterMs, 900_000);
  50. assert.equal(config.worker.reviewMaxPages, 1);
  51. assert.equal(config.auth.mode, 'disabled');
  52. assert.equal(config.auth.defaultWorkspaceId, 'demashi');
  53. assert.deepEqual(config.corsOrigins, ['http://127.0.0.1:4300', 'http://localhost:4200']);
  54. });
  55. test('loadConfig treats blank optional environment values as unset', () => {
  56. const config = loadConfig({
  57. ...validEnvironment,
  58. MIGRATION_DATABASE_URL: '',
  59. SAAS_BOOTSTRAP_ADMIN_USER_ID: '',
  60. });
  61. assert.equal(config.database.migrationUrl, validEnvironment.DATABASE_URL);
  62. assert.equal(config.auth.bootstrapAdminUserId, '');
  63. });
  64. test('loadConfig supports Parse REST storage without a direct database connection', () => {
  65. const environment: NodeJS.ProcessEnv = { ...validEnvironment, STORAGE_DRIVER: 'parse_rest' };
  66. delete environment.DATABASE_URL;
  67. delete environment.MIGRATION_DATABASE_URL;
  68. delete environment.PARSE_MAINTENANCE_KEY;
  69. environment.PARSE_APP_ID = 'dev';
  70. environment.PARSE_MASTER_KEY = 'short-test-key';
  71. environment.PARSE_SERVER_URL = 'http://dev.example.test/parse';
  72. const config = loadConfig(environment);
  73. assert.equal(config.storageDriver, 'parse_rest');
  74. assert.equal(config.database.url, '');
  75. assert.equal(config.parse.serverUrl, 'http://dev.example.test/parse');
  76. });
  77. test('loadConfig rejects disabled authentication in production', () => {
  78. assert.throws(
  79. () => loadConfig({ ...validEnvironment, NODE_ENV: 'production', API_AUTH_MODE: 'disabled' }),
  80. (error: unknown) => error instanceof EnvironmentConfigurationError
  81. && error.message.includes('disabled authentication is not allowed in production'),
  82. );
  83. });