feat: add persona binding algorithm
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { mkdirSync, mkdtempSync, realpathSync, writeFileSync } from "node:fs";
|
||||
import { chmodSync, mkdirSync, mkdtempSync, realpathSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { loadConfigFromSources } from "./config.js";
|
||||
import { DevflowError } from "./errors.js";
|
||||
|
||||
describe("config loader", () => {
|
||||
it("loads .env, .env.local, then process env in descending precedence", () => {
|
||||
@@ -49,7 +50,100 @@ describe("config loader", () => {
|
||||
expect(config.backends).toContainEqual({ id: "fake", enabled: true });
|
||||
});
|
||||
|
||||
it("parses backend registration from DEVFLOW_BACKENDS_JSON", () => {
|
||||
it("resolves backend binaries from PATH during config load", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "devflow-config-"));
|
||||
const workspace = join(root, "workspace");
|
||||
const binDir = join(root, "bin");
|
||||
const codexBin = join(binDir, "codex");
|
||||
mkdirSync(workspace);
|
||||
mkdirSync(binDir);
|
||||
writeFileSync(codexBin, "#!/bin/sh\nexit 0\n");
|
||||
chmodSync(codexBin, 0o755);
|
||||
|
||||
const config = loadConfigFromSources({
|
||||
cwd: root,
|
||||
env: {
|
||||
DATABASE_URL: "postgres://devflow:devflow@localhost:5432/devflow",
|
||||
WORKSPACE_ROOT: workspace,
|
||||
LOG_LEVEL: "info",
|
||||
PATH: binDir,
|
||||
DEVFLOW_BACKENDS_JSON: JSON.stringify([{ id: "codex", enabled: true }]),
|
||||
},
|
||||
});
|
||||
|
||||
expect(config.backends).toEqual([
|
||||
{ id: "fake", enabled: true },
|
||||
{ id: "codex", enabled: true, binaryPath: realpathSync(codexBin) },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps enabled real backends unavailable when their binary cannot be resolved", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "devflow-config-"));
|
||||
const workspace = join(root, "workspace");
|
||||
const emptyBin = join(root, "empty-bin");
|
||||
mkdirSync(workspace);
|
||||
mkdirSync(emptyBin);
|
||||
|
||||
const config = loadConfigFromSources({
|
||||
cwd: root,
|
||||
env: {
|
||||
DATABASE_URL: "postgres://devflow:devflow@localhost:5432/devflow",
|
||||
WORKSPACE_ROOT: workspace,
|
||||
LOG_LEVEL: "info",
|
||||
PATH: emptyBin,
|
||||
DEVFLOW_BACKENDS_JSON: JSON.stringify([{ id: "codex", enabled: true }]),
|
||||
},
|
||||
});
|
||||
|
||||
expect(config.backends).toEqual([
|
||||
{ id: "fake", enabled: true },
|
||||
{ id: "codex", enabled: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it("requires LOG_LEVEL and classifies invalid config as fatal", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "devflow-config-"));
|
||||
const workspace = join(root, "workspace");
|
||||
mkdirSync(workspace);
|
||||
|
||||
let caught: unknown;
|
||||
try {
|
||||
loadConfigFromSources({
|
||||
cwd: root,
|
||||
env: {
|
||||
DATABASE_URL: "postgres://devflow:devflow@localhost:5432/devflow",
|
||||
WORKSPACE_ROOT: workspace,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
caught = error;
|
||||
}
|
||||
|
||||
expect(caught).toBeInstanceOf(DevflowError);
|
||||
expect((caught as DevflowError).class).toBe("fatal");
|
||||
expect((caught as DevflowError).code).toBe("config_invalid");
|
||||
expect((caught as DevflowError).cause).toBeDefined();
|
||||
});
|
||||
|
||||
it("classifies malformed backend JSON as invalid config", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "devflow-config-"));
|
||||
const workspace = join(root, "workspace");
|
||||
mkdirSync(workspace);
|
||||
|
||||
expect(() =>
|
||||
loadConfigFromSources({
|
||||
cwd: root,
|
||||
env: {
|
||||
DATABASE_URL: "postgres://devflow:devflow@localhost:5432/devflow",
|
||||
WORKSPACE_ROOT: workspace,
|
||||
LOG_LEVEL: "info",
|
||||
DEVFLOW_BACKENDS_JSON: "{",
|
||||
},
|
||||
}),
|
||||
).toThrow(DevflowError);
|
||||
});
|
||||
|
||||
it("freezes config and backend registrations", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "devflow-config-"));
|
||||
const workspace = join(root, "workspace");
|
||||
mkdirSync(workspace);
|
||||
@@ -60,15 +154,14 @@ describe("config loader", () => {
|
||||
DATABASE_URL: "postgres://devflow:devflow@localhost:5432/devflow",
|
||||
WORKSPACE_ROOT: workspace,
|
||||
LOG_LEVEL: "info",
|
||||
DEVFLOW_BACKENDS_JSON: JSON.stringify([
|
||||
{ id: "codex", enabled: true, binaryPath: "/usr/local/bin/codex" },
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
expect(config.backends).toEqual([
|
||||
{ id: "fake", enabled: true },
|
||||
{ id: "codex", enabled: true, binaryPath: "/usr/local/bin/codex" },
|
||||
]);
|
||||
expect(Object.isFrozen(config)).toBe(true);
|
||||
expect(Object.isFrozen(config.backends)).toBe(true);
|
||||
expect(Object.isFrozen(config.backends[0])).toBe(true);
|
||||
expect(() => {
|
||||
(config.backends[0] as { enabled: boolean }).enabled = false;
|
||||
}).toThrow(TypeError);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user