mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
* test: prepare bounded TypeScript child probes Prepare native test children through the invocation-owned compiler before their existing deadlines. Preserve module boundaries for import guards and script entry detection, and keep source-loader contracts explicit. Retain native process isolation, worker lifetimes, assertions, and deadlines. Cover MTS declaration selection in the existing compiler fixture. Also declare the generated fallback plugin fixture as ESM and remove one unused internal script export. * test: prepare remaining worker and tooling fixtures Keep installed tooling at its native package location so prepared probes retain native bindings and relative assets. Preserve existing child deadlines, assertions, and source-layout contracts. * test: assert native CLI delegation before option rejection * test: preserve sparse prepared fixture entry contracts * test: mark prepared budget fixtures as ES modules * test: preserve serialized fixture inputs under type-aware lint * test: prepare sparse wrapper and report fixtures * test: prepare remaining CLI wrapper children * test: complete bounded child fixture preparation
139 lines
4.6 KiB
TypeScript
139 lines
4.6 KiB
TypeScript
import { execFileSync, spawnSync } from "node:child_process";
|
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { resolveBuildIdentityEnvironment } from "../../scripts/lib/build-identity.mts";
|
|
import {
|
|
resolveRuntimeWorkerArgv,
|
|
resolveRuntimeWorkerUrl,
|
|
} from "../../src/infra/runtime-worker-url.js";
|
|
import { resolveTestNodeExecPath } from "../../src/test-utils/node-process.js";
|
|
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
|
import { toolingProbeRuntimeEntrypoints } from "./tooling-probe-runtime.test-support.mts";
|
|
|
|
const buildIdentityUrl = resolveRuntimeWorkerUrl(toolingProbeRuntimeEntrypoints.buildIdentity);
|
|
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
|
const testNodeExecPath = resolveTestNodeExecPath();
|
|
|
|
function readGitCommitInChild(cwd: string, env: NodeJS.ProcessEnv = process.env) {
|
|
const result = spawnSync(
|
|
testNodeExecPath,
|
|
[
|
|
...resolveRuntimeWorkerArgv(buildIdentityUrl, testNodeExecPath).slice(0, -1),
|
|
"--input-type=module",
|
|
"--eval",
|
|
`const { readCurrentGitCommit } = await import(${JSON.stringify(buildIdentityUrl.href)}); process.stdout.write(JSON.stringify(readCurrentGitCommit()));`,
|
|
],
|
|
{
|
|
cwd,
|
|
encoding: "utf8",
|
|
env,
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
timeout: 10_000,
|
|
},
|
|
);
|
|
expect(result.error).toBeUndefined();
|
|
expect(result.status, result.stderr).toBe(0);
|
|
return JSON.parse(result.stdout) as string | null;
|
|
}
|
|
|
|
describe("readCurrentGitCommit", () => {
|
|
it("reads HEAD from the inherited cwd and returns null when Git cannot resolve it", () => {
|
|
const root = tempDirs.make("openclaw-build-identity-");
|
|
const repo = join(root, "repo");
|
|
const outsideRepo = join(root, "outside");
|
|
mkdirSync(repo);
|
|
mkdirSync(outsideRepo);
|
|
|
|
execFileSync("git", ["init", "-q"], { cwd: repo, stdio: "ignore" });
|
|
writeFileSync(join(repo, "fixture.txt"), "fixture\n");
|
|
execFileSync("git", ["add", "fixture.txt"], { cwd: repo, stdio: "ignore" });
|
|
execFileSync(
|
|
"git",
|
|
[
|
|
"-c",
|
|
"user.name=OpenClaw Tests",
|
|
"-c",
|
|
"user.email=openclaw-tests@example.invalid",
|
|
"commit",
|
|
"-qm",
|
|
"fixture",
|
|
],
|
|
{ cwd: repo, stdio: "ignore" },
|
|
);
|
|
const expected = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
cwd: repo,
|
|
encoding: "utf8",
|
|
}).trim();
|
|
|
|
expect(readGitCommitInChild(repo)).toBe(expected);
|
|
expect(readGitCommitInChild(outsideRepo)).toBeNull();
|
|
|
|
const envWithoutGit = { ...process.env };
|
|
for (const key of Object.keys(envWithoutGit)) {
|
|
if (key.toLowerCase() === "path") {
|
|
delete envWithoutGit[key];
|
|
}
|
|
}
|
|
envWithoutGit.PATH = "";
|
|
expect(readGitCommitInChild(repo, envWithoutGit)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("resolveBuildIdentityEnvironment", () => {
|
|
it.each([
|
|
{
|
|
env: { GIT_COMMIT: "A".repeat(40), GIT_SHA: "b".repeat(40) },
|
|
expected: "a".repeat(40),
|
|
readsCheckout: false,
|
|
},
|
|
{
|
|
env: { GIT_SHA: "B".repeat(40), GITHUB_SHA: "c".repeat(40) },
|
|
expected: "b".repeat(40),
|
|
readsCheckout: false,
|
|
},
|
|
{
|
|
env: { GITHUB_SHA: "c".repeat(40) },
|
|
expected: "d".repeat(40),
|
|
readsCheckout: true,
|
|
},
|
|
])("preserves build source precedence %#", ({ env, expected, readsCheckout }) => {
|
|
const readGitCommit = vi.fn(() => "D".repeat(40));
|
|
const resolved = resolveBuildIdentityEnvironment({
|
|
commitLabel: "build commit",
|
|
env,
|
|
now: () => new Date("2026-07-10T12:34:56.000Z"),
|
|
readGitCommit,
|
|
});
|
|
|
|
expect(resolved.GIT_COMMIT).toBe(expected);
|
|
expect(readGitCommit).toHaveBeenCalledTimes(readsCheckout ? 1 : 0);
|
|
});
|
|
|
|
it("uses workflow identity only when the checkout cannot be read", () => {
|
|
expect(
|
|
resolveBuildIdentityEnvironment({
|
|
commitLabel: "runtime pack commit",
|
|
env: {
|
|
GITHUB_SHA: "e".repeat(40),
|
|
OPENCLAW_BUILD_TIMESTAMP: " 2026-07-10T01:02:03.000Z ",
|
|
},
|
|
now: () => new Date("2026-07-11T12:34:56.000Z"),
|
|
readGitCommit: () => null,
|
|
}),
|
|
).toMatchObject({
|
|
GIT_COMMIT: "e".repeat(40),
|
|
OPENCLAW_BUILD_TIMESTAMP: "2026-07-10T01:02:03.000Z",
|
|
});
|
|
});
|
|
|
|
it("uses the owner label in malformed commit diagnostics", () => {
|
|
expect(() =>
|
|
resolveBuildIdentityEnvironment({
|
|
commitLabel: "runtime pack commit",
|
|
env: { GIT_COMMIT: "deadbeef" },
|
|
readGitCommit: () => null,
|
|
}),
|
|
).toThrow("runtime pack commit must be a full 40-character hexadecimal SHA");
|
|
});
|
|
});
|