Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/scripts/check-env-drift.test.mjs
T

217 lines
7.2 KiB
JavaScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { afterEach, describe, it } from "vitest";
import { checkEnvDrift, formatEnvDriftResult } from "./check-env-drift.mjs";
const fixtures = [];
const ENV_FIXTURES = {
".env.example": `# Core
APP_NAME=HiveGrid
DATABASE_URL="postgres://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}"
SESSION_SECRET=
AUDIT_CHAIN_SECRET=
CRYPTO_SECRET=
ADMIN_CONNECTION_MASTER_KEY=
APP_BASE_DOMAIN=localhost
WEB_APP_HOST=app.localhost
ADMIN_APP_HOST=admin.localhost
API_HOST=api.localhost
MCP_HOST=0.0.0.0
MCP_PORT=8080
MCP_ALLOWED_HOSTS=mcp.hivegrid.de,staging-mcp.hivegrid.de,localhost,127.0.0.1,::1
MCP_ALLOWED_ORIGINS=https://mcp.hivegrid.de,https://staging-mcp.hivegrid.de,http://localhost,http://127.0.0.1
MCP_RESOURCE_URL=http://mcp.localhost:8080
MCP_AUTHORIZATION_SERVER_URL=http://app.localhost
# APP_PROTOCOL=http
API_ACCESS_TOKEN=
JWT_SECRET=
# REDIS_URL is a fallback only when BULLMQ_REDIS_URL is absent; keep it commented if a platform injects it.
# REDIS_URL=redis://localhost:6379/0
SITE_KEY_EXAMPLE_SITE=
`,
".env.local.example": `APP_NAME=HiveGrid Local
DATABASE_URL="file:./dev.db?connection_limit=1&connect_timeout=30"
SESSION_SECRET=replace-with-local-session-secret
AUDIT_CHAIN_SECRET=replace-with-local-audit-chain-secret
CRYPTO_SECRET=replace-with-local-crypto-secret
ADMIN_CONNECTION_MASTER_KEY=replace-with-local-admin-connection-key
APP_BASE_DOMAIN=localhost:3000
WEB_APP_HOST=app.localhost:3000
ADMIN_APP_HOST=admin.localhost:3000
API_HOST=api.localhost:3001
APP_PROTOCOL=http
API_ACCESS_TOKEN=replace-with-local-api-token
JWT_SECRET=replace-with-local-jwt-secret
SITE_KEY_LOCAL=replace-with-local-site-key
`,
".env.production.example": `APP_NAME=HiveGrid
DATABASE_URL="postgres://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}"
SESSION_SECRET=
AUDIT_CHAIN_SECRET=
CRYPTO_SECRET=
ADMIN_CONNECTION_MASTER_KEY=
APP_BASE_DOMAIN=your-domain.invalid
WEB_APP_HOST=app.your-domain.invalid
ADMIN_APP_HOST=admin.your-domain.invalid
API_HOST=api.your-domain.invalid
MCP_HOST=0.0.0.0
MCP_PORT=8080
MCP_ALLOWED_HOSTS=mcp.your-domain.invalid,staging-mcp.your-domain.invalid
MCP_ALLOWED_ORIGINS=https://mcp.your-domain.invalid,https://staging-mcp.your-domain.invalid
MCP_RESOURCE_URL=https://mcp.your-domain.invalid
MCP_AUTHORIZATION_SERVER_URL=https://app.your-domain.invalid
APP_PROTOCOL=https
API_ACCESS_TOKEN=
JWT_SECRET=
BULLMQ_REDIS_URL=redis://localhost:6379/0
# REDIS_URL=redis://localhost:6379/0
`,
".env.test": `APP_NAME=HiveGrid Test
DATABASE_URL="file:./test.db?connection_limit=1&connect_timeout=30"
SESSION_SECRET=test-session-secret-placeholder
AUDIT_CHAIN_SECRET=test-audit-chain-secret-placeholder
CRYPTO_SECRET=test-crypto-secret-placeholder
ADMIN_CONNECTION_MASTER_KEY=test-admin-connection-key-placeholder
APP_BASE_DOMAIN=localhost.test
WEB_APP_HOST=app.localhost.test
ADMIN_APP_HOST=admin.localhost.test
API_HOST=api.localhost.test
MCP_HOST=127.0.0.1
MCP_PORT=8080
MCP_ALLOWED_HOSTS=mcp.localhost.test,localhost,127.0.0.1,::1
MCP_ALLOWED_ORIGINS=http://mcp.localhost.test,http://localhost,http://127.0.0.1
MCP_RESOURCE_URL=http://mcp.localhost.test:8080
MCP_AUTHORIZATION_SERVER_URL=http://app.localhost.test
APP_PROTOCOL=http
API_ACCESS_TOKEN=test-api-access-token-placeholder
JWT_SECRET=test-jwt-secret-placeholder
SITE_KEY_EXAMPLE_SITE=test-site-key-placeholder
`,
};
function writeFile(rootDir, relativePath, content) {
const absolutePath = path.join(rootDir, relativePath);
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
fs.writeFileSync(absolutePath, content);
}
function createFixture(overrides = {}) {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "env-drift-"));
fixtures.push(rootDir);
for (const [relativePath, content] of Object.entries(ENV_FIXTURES)) {
writeFile(rootDir, relativePath, overrides[relativePath] ?? content);
}
return rootDir;
}
afterEach(() => {
while (fixtures.length > 0) {
fs.rmSync(fixtures.pop(), { recursive: true, force: true });
}
});
describe("check-env-drift", () => {
it("prints PASS output for cataloged env fixtures", () => {
const rootDir = createFixture();
const result = checkEnvDrift(rootDir);
const output = formatEnvDriftResult(result);
assert.deepEqual(result.failures, []);
assert.match(output, /^PASS env drift verification/);
assert.match(output, /PASS \.env\.test: \d+ active keys match catalog/);
assert.match(output, /Summary: \d+ passed, 0 observed, 0 failed/);
});
it("fails for unknown keys", () => {
const rootDir = createFixture({
".env.test": `${ENV_FIXTURES[".env.test"]}UNTRACKED_SECRET=super-secret-value
`,
});
const result = checkEnvDrift(rootDir);
assert.match(result.failures.join("\n"), /\.env\.test:\d+: unknown-key UNTRACKED_SECRET/);
});
it("fails for duplicate keys within one file", () => {
const rootDir = createFixture({
".env.local.example": `${ENV_FIXTURES[".env.local.example"]}APP_NAME=Duplicate
`,
});
const result = checkEnvDrift(rootDir);
assert.match(result.failures.join("\n"), /\.env\.local\.example:\d+: duplicate-key APP_NAME/);
});
it("allows dynamic SITE_KEY-prefixed variables", () => {
const rootDir = createFixture({
".env.test": `${ENV_FIXTURES[".env.test"]}SITE_KEY_CUSTOMER_PORTAL=test-site-key-placeholder
`,
});
const result = checkEnvDrift(rootDir);
assert.deepEqual(result.failures, []);
});
it("fails when production example files keep example.com placeholders", () => {
const rootDir = createFixture({
".env.production.example": ENV_FIXTURES[".env.production.example"].replaceAll("your-domain.invalid", "example.com"),
});
const result = checkEnvDrift(rootDir);
assert.match(result.failures.join("\n"), /\.env\.production\.example:\d+: production-domain-placeholder APP_BASE_DOMAIN/);
});
it("redacts values from diagnostics", () => {
const leakedValue = "top-secret-token-value";
const rootDir = createFixture({
".env.test": `${ENV_FIXTURES[".env.test"]}UNTRACKED_SECRET=${leakedValue}
`,
});
const result = checkEnvDrift(rootDir);
const output = formatEnvDriftResult(result);
assert.match(output, /unknown-key UNTRACKED_SECRET/);
assert.doesNotMatch(output, new RegExp(leakedValue));
});
it("CLI exits non-zero without leaking values", () => {
const leakedValue = "super-secret-cli-value";
const rootDir = createFixture({
".env.test": `${ENV_FIXTURES[".env.test"]}UNTRACKED_SECRET=${leakedValue}
`,
});
const scriptPath = path.resolve("scripts/check-env-drift.mjs");
const nodePath = path.resolve("node_modules");
assert.throws(
() => execFileSync(process.execPath, ["--import", pathToFileURL(path.join(nodePath, "tsx/dist/loader.mjs")).href, scriptPath], {
cwd: rootDir,
encoding: "utf8",
stdio: "pipe",
}),
(error) => {
assert.equal(error.status, 1);
const output = `${error.stdout}${error.stderr}`;
assert.match(output, /FAIL env drift verification/);
assert.match(output, /unknown-key UNTRACKED_SECRET/);
assert.doesNotMatch(output, new RegExp(leakedValue));
return true;
},
);
});
});