Files
HiveGrid-Wireframes/hivegrid-cloud/apps/api/src/config.test.ts
T

154 lines
4.7 KiB
TypeScript

import { ApiConfigError, type ApiConfig } from "@toolctrl/config";
import { afterEach, describe, expect, test } from "vitest";
import { getApiRuntimeConfig, resetApiRuntimeConfigForTests, setApiRuntimeConfigForTests } from "./config.js";
const originalEnv = { ...process.env };
const REQUIRED_PRODUCTION_ENV = {
APP_NAME: "Production API",
DATABASE_URL: "postgres://example.invalid/app",
APP_BASE_DOMAIN: "example.com",
WEB_APP_HOST: "app.example.com",
ADMIN_APP_HOST: "admin.example.com",
API_HOST: "api.example.com",
SESSION_SECRET: "session-secret-value",
CRYPTO_SECRET: "crypto-secret-value",
API_ACCESS_TOKEN: "api-access-token-value",
JWT_SECRET: "jwt-secret-value",
ANALYTICS_TOKEN: "analytics-token-value",
API_PORT: "8081",
APP_PROTOCOL: "https",
};
const SECRET_VALUES = [
"api-access-token-value",
"jwt-secret-value",
"secret-like-invalid-port-value",
"session-secret-value",
"crypto-secret-value",
"analytics-token-value",
];
afterEach(() => {
resetApiRuntimeConfigForTests();
process.env = { ...originalEnv };
});
describe("getApiRuntimeConfig", () => {
test("loads required API runtime config from a complete production-like environment", () => {
process.env = {
...originalEnv,
...REQUIRED_PRODUCTION_ENV,
NODE_ENV: "production",
};
expect(getApiRuntimeConfig()).toMatchObject({
appName: "Production API",
nodeEnv: "production",
listen: { port: 8081 },
routing: {
appBaseDomain: "example.com",
webAppHost: "app.example.com",
adminAppHost: "admin.example.com",
apiHost: "api.example.com",
protocol: "https",
},
apiAccessToken: "api-access-token-value",
jwtSecret: "jwt-secret-value",
analytics: {
token: "analytics-token-value",
},
});
});
test("throws redacted ApiConfigError when API_ACCESS_TOKEN is missing", () => {
process.env = {
...originalEnv,
...REQUIRED_PRODUCTION_ENV,
NODE_ENV: "production",
API_ACCESS_TOKEN: undefined,
};
expect(() => getApiRuntimeConfig()).toThrow(ApiConfigError);
try {
getApiRuntimeConfig();
} catch (error) {
expect(error).toBeInstanceOf(ApiConfigError);
const apiConfigError = error as ApiConfigError;
expect(apiConfigError.message).toBe("Invalid API configuration (API_ACCESS_TOKEN:missing-required).");
expect(apiConfigError.details.diagnostics).toEqual([
expect.objectContaining({
code: "missing-required",
name: "API_ACCESS_TOKEN",
classification: "required-per-app",
}),
]);
const errorText = JSON.stringify({ message: apiConfigError.message, details: apiConfigError.details });
for (const secretValue of SECRET_VALUES) {
expect(errorText).not.toContain(secretValue);
}
}
});
test("throws redacted ApiConfigError when API_PORT is invalid", () => {
process.env = {
...originalEnv,
...REQUIRED_PRODUCTION_ENV,
NODE_ENV: "production",
API_PORT: "secret-like-invalid-port-value",
};
try {
getApiRuntimeConfig();
} catch (error) {
expect(error).toBeInstanceOf(ApiConfigError);
const apiConfigError = error as ApiConfigError;
expect(apiConfigError.message).toBe("Invalid API configuration (API_PORT:invalid-port).");
expect(apiConfigError.details.diagnostics).toEqual([
expect.objectContaining({
code: "invalid-port",
name: "API_PORT",
}),
]);
const errorText = JSON.stringify({ message: apiConfigError.message, details: apiConfigError.details });
for (const secretValue of SECRET_VALUES) {
expect(errorText).not.toContain(secretValue);
}
}
});
test("uses setApiRuntimeConfigForTests override without reading required environment", () => {
process.env = { NODE_ENV: "production" };
const overrideConfig: ApiConfig = {
appName: "Override API",
nodeEnv: "test",
revision: "unknown",
listen: { port: 9999 },
routing: {
appBaseDomain: "override.localhost",
webAppHost: "app.override.localhost",
adminAppHost: "admin.override.localhost",
apiHost: "api.override.localhost",
protocol: "http",
},
origins: {},
apiAccessToken: "override-token",
jwtSecret: "override-jwt-secret",
analytics: {},
mailjet: {},
stripe: {},
apiKeys: {
allowLegacyPlaintextFallback: true,
},
storage: {},
};
setApiRuntimeConfigForTests(overrideConfig);
expect(getApiRuntimeConfig()).toBe(overrideConfig);
resetApiRuntimeConfigForTests();
expect(() => getApiRuntimeConfig()).toThrow(ApiConfigError);
});
});