mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 05:12:11 +03:00
Repaired 47 of 49 pre-existing failing unit test files on release/v3.8.2 (down to docs-site-overhaul, a tr46/tsx/Node24 toolchain blocker, tracked separately). Stale tests reconciled with current source (catalog/registry/version drift), the notable ones: openai gpt-4o / gpt-4o-mini removed from the registry; Antigravity Claude models removed from the public catalog; DEFAULT_CLAUDE_CODE_VERSION and DEFAULT_CODEX_CLIENT_VERSION bumps; voyage-3-large → voyage-4; model-alias seed now routes via gemini-cli; remapToolNames API change; getLKGP return shape; sidebar nav overhaul; CLI commands now write via process.stdout.write; cloudEnabled default true. Real SOURCE bugs found by the tests and fixed (not masked): - fix(db): commandCodeAuth.toSafeStatus + evals.ts read the `*Json` camel keys that rowToCamel does not produce — it auto-parses `*_json` columns under the base name, so metadata/outputs/summary/results/tags were always empty. Read the base keys. - fix(executors): re-register claude-web / cw-web in the executor index (the provider shipped in #2476 but was never wired into the registry). - fix(validation): build the OpenAI-like /models probe with addModelsSuffix so an OpenAI base URL validates against /v1/models, not /v1/chat/completions/models; honor a ya29.* Google OAuth token as Bearer even when authType is apikey/header (it was shadowed by an unreachable else-if); make the Anthropic /models probe best-effort (try/catch) so a 404/malformed-URL throw no longer marks a valid key invalid. - fix(security): add the requireCliToolsAuth guard to the GET handlers of cli-tools/guide-settings/[toolId] and cli-tools/hermes-agent-settings (host config access was unguarded). - revert(stream): restore the SSE heartbeat default to 15s (the 4s round-8 change regressed runtime-timeouts; #2544's early-keepalive route wrapper remains the fix). Also: env-doc sync (OMNIROUTE_SKIP_DB_HEALTHCHECK) and new sidebar i18n keys.
190 lines
7.0 KiB
TypeScript
190 lines
7.0 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
const { syncEnv } = (await import("../../scripts/dev/sync-env.mjs")) as {
|
|
syncEnv: (opts?: { rootDir?: string; quiet?: boolean; scope?: string }) => {
|
|
created: boolean;
|
|
added: number;
|
|
};
|
|
};
|
|
|
|
function createTempRoot(): string {
|
|
return fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-sync-env-"));
|
|
}
|
|
|
|
function writeEnvExample(rootDir: string) {
|
|
fs.writeFileSync(
|
|
path.join(rootDir, ".env.example"),
|
|
[
|
|
"JWT_SECRET=",
|
|
"API_KEY_SECRET=",
|
|
"STORAGE_ENCRYPTION_KEY=",
|
|
"MACHINE_ID_SALT=",
|
|
"CLAUDE_OAUTH_CLIENT_ID=claude-default",
|
|
"CODEX_OAUTH_CLIENT_ID=codex-default",
|
|
'CLAUDE_USER_AGENT="claude-cli/2.1.145 (external, cli)"',
|
|
"# COMMENTED_KEY=skip-me",
|
|
"",
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
}
|
|
|
|
function writeOauthEnvExample(rootDir: string) {
|
|
fs.writeFileSync(
|
|
path.join(rootDir, ".env.example"),
|
|
[
|
|
"# ═══════════════════════════════════════════════════",
|
|
"# OAUTH PROVIDER CREDENTIALS",
|
|
"# ═══════════════════════════════════════════════════",
|
|
"CLAUDE_OAUTH_CLIENT_ID=claude-default",
|
|
"CODEX_OAUTH_CLIENT_ID=codex-default",
|
|
"# ─────────────────────────────────────────────────────────────────────────────",
|
|
"# Provider User-Agent Overrides (optional — customize per-provider UA headers)",
|
|
"# ─────────────────────────────────────────────────────────────────────────────",
|
|
"JWT_SECRET=should-not-be-copied",
|
|
"",
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
}
|
|
|
|
test("syncEnv creates .env from .env.example and generates blank secrets", () => {
|
|
const rootDir = createTempRoot();
|
|
|
|
// Temporarily override DATA_DIR so the encrypted-credentials guard doesn't
|
|
// find the user's real DB at ~/.omniroute/ during tests
|
|
const origDataDir = process.env.DATA_DIR;
|
|
try {
|
|
writeEnvExample(rootDir);
|
|
|
|
process.env.DATA_DIR = rootDir;
|
|
const result = syncEnv({ rootDir, quiet: true });
|
|
const envContent = fs.readFileSync(path.join(rootDir, ".env"), "utf8");
|
|
|
|
assert.deepEqual(result, { created: true, added: 7 });
|
|
assert.match(envContent, /^JWT_SECRET=.{32,}$/m);
|
|
assert.match(envContent, /^API_KEY_SECRET=.{32,}$/m);
|
|
// STORAGE_ENCRYPTION_KEY is generated at server startup (not postinstall — see #1622)
|
|
assert.match(envContent, /^STORAGE_ENCRYPTION_KEY=/m);
|
|
assert.match(envContent, /^MACHINE_ID_SALT=omniroute-/m);
|
|
assert.match(envContent, /^CLAUDE_OAUTH_CLIENT_ID=claude-default$/m);
|
|
assert.match(envContent, /^CODEX_OAUTH_CLIENT_ID=codex-default$/m);
|
|
assert.match(envContent, /^CLAUDE_USER_AGENT="claude-cli\/2\.1\.145 \(external, cli\)"$/m);
|
|
assert.doesNotMatch(envContent, /^COMMENTED_KEY=/m);
|
|
} finally {
|
|
process.env.DATA_DIR = origDataDir;
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("syncEnv appends only missing keys and preserves existing values", () => {
|
|
const rootDir = createTempRoot();
|
|
|
|
const origDataDir = process.env.DATA_DIR;
|
|
try {
|
|
writeEnvExample(rootDir);
|
|
fs.writeFileSync(
|
|
path.join(rootDir, ".env"),
|
|
[
|
|
"JWT_SECRET=my-custom-secret-that-should-stay",
|
|
"CLAUDE_OAUTH_CLIENT_ID=custom-claude",
|
|
"",
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
process.env.DATA_DIR = rootDir;
|
|
const result = syncEnv({ rootDir, quiet: true });
|
|
const envContent = fs.readFileSync(path.join(rootDir, ".env"), "utf8");
|
|
|
|
assert.deepEqual(result, { created: false, added: 5 });
|
|
assert.match(envContent, /^JWT_SECRET=my-custom-secret-that-should-stay$/m);
|
|
assert.match(envContent, /^CLAUDE_OAUTH_CLIENT_ID=custom-claude$/m);
|
|
assert.match(envContent, /^API_KEY_SECRET=.{32,}$/m);
|
|
// STORAGE_ENCRYPTION_KEY is generated at server startup (not postinstall — see #1622)
|
|
assert.match(envContent, /^STORAGE_ENCRYPTION_KEY=/m);
|
|
assert.match(envContent, /^MACHINE_ID_SALT=omniroute-/m);
|
|
assert.match(envContent, /^CODEX_OAUTH_CLIENT_ID=codex-default$/m);
|
|
assert.match(envContent, /^CLAUDE_USER_AGENT=claude-cli\/2\.1\.145 \(external, cli\)$/m);
|
|
assert.match(envContent, /Auto-added by sync-env/);
|
|
} finally {
|
|
process.env.DATA_DIR = origDataDir;
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("syncEnv treats quoted and unquoted values as equivalent", () => {
|
|
const rootDir = createTempRoot();
|
|
|
|
const origDataDir = process.env.DATA_DIR;
|
|
try {
|
|
writeEnvExample(rootDir);
|
|
fs.writeFileSync(
|
|
path.join(rootDir, ".env"),
|
|
[
|
|
"JWT_SECRET=jwt-secret",
|
|
"API_KEY_SECRET=api-secret",
|
|
"STORAGE_ENCRYPTION_KEY=storage-secret",
|
|
"MACHINE_ID_SALT=machine-salt",
|
|
"CLAUDE_OAUTH_CLIENT_ID=claude-default",
|
|
"CODEX_OAUTH_CLIENT_ID=codex-default",
|
|
'CLAUDE_USER_AGENT="claude-cli/2.1.145 (external, cli)"',
|
|
"",
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
process.env.DATA_DIR = rootDir;
|
|
const result = syncEnv({ rootDir, quiet: true });
|
|
|
|
assert.deepEqual(result, { created: false, added: 0 });
|
|
} finally {
|
|
process.env.DATA_DIR = origDataDir;
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("syncEnv is idempotent when .env is already complete", () => {
|
|
const rootDir = createTempRoot();
|
|
|
|
const origDataDir = process.env.DATA_DIR;
|
|
try {
|
|
writeEnvExample(rootDir);
|
|
process.env.DATA_DIR = rootDir;
|
|
syncEnv({ rootDir, quiet: true });
|
|
|
|
const before = fs.readFileSync(path.join(rootDir, ".env"), "utf8");
|
|
const result = syncEnv({ rootDir, quiet: true });
|
|
const after = fs.readFileSync(path.join(rootDir, ".env"), "utf8");
|
|
|
|
assert.deepEqual(result, { created: false, added: 0 });
|
|
assert.equal(after, before);
|
|
} finally {
|
|
process.env.DATA_DIR = origDataDir;
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("syncEnv oauth scope only copies oauth defaults", () => {
|
|
const rootDir = createTempRoot();
|
|
|
|
try {
|
|
writeOauthEnvExample(rootDir);
|
|
|
|
const result = syncEnv({ rootDir, quiet: true, scope: "oauth" });
|
|
const envContent = fs.readFileSync(path.join(rootDir, ".env"), "utf8");
|
|
|
|
assert.deepEqual(result, { created: true, added: 2 });
|
|
assert.match(envContent, /^CLAUDE_OAUTH_CLIENT_ID=claude-default$/m);
|
|
assert.match(envContent, /^CODEX_OAUTH_CLIENT_ID=codex-default$/m);
|
|
assert.doesNotMatch(envContent, /^JWT_SECRET=/m);
|
|
assert.doesNotMatch(envContent, /^Provider User-Agent Overrides/m);
|
|
} finally {
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|