import fs from "node:fs" ;
import path from "node:path" ;
import { afterEach, describe, expect, it, vi } from "vitest" ;
import {
evaluateRuntimeEligibility,
evaluateRuntimeRequires,
hasBinary,
isConfigPathTruthyWithDefaults,
isTruthy,
resolveConfigPath,
resolveRuntimePlatform,
} from "./config-eval.js" ;
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform" );
const originalPath = process.env.PATH;
const originalPathExt = process.env.PATHEXT;
function setPlatform(platform: NodeJS.Platform): void {
Object.defineProperty(process, "platform" , {
value: platform,
configurable: true ,
});
}
afterEach(() => {
vi.restoreAllMocks();
process.env.PATH = originalPath;
if (originalPathExt === undefined) {
delete process.env.PATHEXT;
} else {
process.env.PATHEXT = originalPathExt;
}
if (originalPlatformDescriptor) {
Object.defineProperty(process, "platform" , originalPlatformDescriptor);
}
});
describe("config-eval helpers" , () => {
it("normalizes truthy values across primitive types" , () => {
expect(isTruthy(undefined)).toBe(false );
expect(isTruthy(null )).toBe(false );
expect(isTruthy(false )).toBe(false );
expect(isTruthy(true )).toBe(true );
expect(isTruthy(0 )).toBe(false );
expect(isTruthy(1 )).toBe(true );
expect(isTruthy(" " )).toBe(false );
expect(isTruthy(" ok " )).toBe(true );
expect(isTruthy({})).toBe(true );
});
it("resolves nested config paths and missing branches safely" , () => {
const config = {
browser: {
enabled: true ,
nested: {
count: 1 ,
},
},
};
expect(resolveConfigPath(config, "browser.enabled" )).toBe(true );
expect(resolveConfigPath(config, ".browser..nested.count." )).toBe(1 );
expect(resolveConfigPath(config, "browser.missing.value" )).toBeUndefined();
expect(resolveConfigPath("not-an-object" , "browser.enabled" )).toBeUndefined();
});
it("uses defaults only when config paths are unresolved" , () => {
const config = {
browser: {
enabled: false ,
},
};
expect(
isConfigPathTruthyWithDefaults(config, "browser.enabled" , { "browser.enabled" : true }),
).toBe(false );
expect(
isConfigPathTruthyWithDefaults(config, "browser.missing" , { "browser.missing" : true }),
).toBe(true );
expect(isConfigPathTruthyWithDefaults(config, "browser.other" , {})).toBe(false );
});
it("returns the active runtime platform" , () => {
setPlatform("darwin" );
expect(resolveRuntimePlatform()).toBe("darwin" );
});
it("caches binary lookups until PATH changes" , () => {
setPlatform("linux" );
process.env.PATH = ["/missing/bin" , "/found/bin" ].join(path.delimiter);
const accessSpy = vi.spyOn(fs, "accessSync" ).mockImplementation((candidate) => {
if (String(candidate) === path.join("/found/bin" , "tool" )) {
return undefined;
}
throw new Error("missing" );
});
expect(hasBinary("tool" )).toBe(true );
expect(hasBinary("tool" )).toBe(true );
expect(accessSpy).toHaveBeenCalledTimes(2 );
process.env.PATH = "/other/bin" ;
accessSpy.mockClear();
accessSpy.mockImplementation(() => {
throw new Error("missing" );
});
expect(hasBinary("tool" )).toBe(false );
expect(accessSpy).toHaveBeenCalledTimes(1 );
});
it("checks PATHEXT candidates on Windows" , () => {
setPlatform("win32" );
const toolsDir = path.join(path.sep, "tools" );
process.env.PATH = toolsDir;
process.env.PATHEXT = ".EXE;.CMD" ;
const plainCandidate = path.join(toolsDir, "tool" );
const exeCandidate = path.join(toolsDir, "tool.EXE" );
const cmdCandidate = path.join(toolsDir, "tool.CMD" );
const accessSpy = vi.spyOn(fs, "accessSync" ).mockImplementation((candidate) => {
if (String(candidate) === cmdCandidate) {
return undefined;
}
throw new Error("missing" );
});
expect(hasBinary("tool" )).toBe(true );
expect(accessSpy.mock.calls.map(([candidate]) => String(candidate))).toEqual([
plainCandidate,
exeCandidate,
cmdCandidate,
]);
});
});
describe("evaluateRuntimeRequires" , () => {
it("accepts remote bins and remote any-bin matches" , () => {
const result = evaluateRuntimeRequires({
requires: {
bins: ["node" ],
anyBins: ["bun" , "deno" ],
env: ["OPENAI_API_KEY" ],
config: ["browser.enabled" ],
},
hasBin: () => false ,
hasRemoteBin: (bin) => bin === "node" ,
hasAnyRemoteBin: (bins) => bins.includes("deno" ),
hasEnv: (name) => name === "OPENAI_API_KEY" ,
isConfigPathTruthy: (path) => path === "browser.enabled" ,
});
expect(result).toBe(true );
});
it("rejects when any required runtime check is still unsatisfied" , () => {
expect(
evaluateRuntimeRequires({
requires: { bins: ["node" ] },
hasBin: () => false ,
hasEnv: () => true ,
isConfigPathTruthy: () => true ,
}),
).toBe(false );
expect(
evaluateRuntimeRequires({
requires: { anyBins: ["bun" , "node" ] },
hasBin: () => false ,
hasAnyRemoteBin: () => false ,
hasEnv: () => true ,
isConfigPathTruthy: () => true ,
}),
).toBe(false );
});
});
describe("evaluateRuntimeEligibility" , () => {
it("rejects entries when required OS does not match local or remote" , () => {
const result = evaluateRuntimeEligibility({
os: ["definitely-not-a-runtime-platform" ],
remotePlatforms: [],
hasBin: () => true ,
hasEnv: () => true ,
isConfigPathTruthy: () => true ,
});
expect(result).toBe(false );
});
it("accepts entries when remote platform satisfies OS requirements" , () => {
const result = evaluateRuntimeEligibility({
os: ["linux" ],
remotePlatforms: ["linux" ],
hasBin: () => true ,
hasEnv: () => true ,
isConfigPathTruthy: () => true ,
});
expect(result).toBe(true );
});
it("bypasses runtime requirements when always=true" , () => {
const result = evaluateRuntimeEligibility({
always: true ,
requires: { env: ["OPENAI_API_KEY" ] },
hasBin: () => false ,
hasEnv: () => false ,
isConfigPathTruthy: () => false ,
});
expect(result).toBe(true );
});
it("evaluates runtime requirements when always is false" , () => {
const result = evaluateRuntimeEligibility({
requires: {
bins: ["node" ],
anyBins: ["bun" , "node" ],
env: ["OPENAI_API_KEY" ],
config: ["browser.enabled" ],
},
hasBin: (bin) => bin === "node" ,
hasAnyRemoteBin: () => false ,
hasEnv: (name) => name === "OPENAI_API_KEY" ,
isConfigPathTruthy: (path) => path === "browser.enabled" ,
});
expect(result).toBe(true );
});
});
Messung V0.5 in Prozent C=100 H=96 G=97
¤ Dauer der Verarbeitung: 0.3 Sekunden
¤
*© Formatika GbR, Deutschland