~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-socialweb-computer-requester

socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons

0
0
main
92 lines · 5.3 KB Raw History
// node --test modules/cicd-workload/test.mjs  (after `cargo wasm`; ../atlibs: its JS host)
// The workload composed with process-mcp, the machine's MCP shell a stub iroh: fetcher: checkout, then
// each step as `sh -s` with its env on stdin; output as results; the first failing step ends the job.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { test } from "node:test";

import { loadComposition, runCompose } from "../../../atlibs/javascript/src/compose/index.js";

const lib = (name) => ({ ...JSON.parse(readFileSync(new URL(`../${name}/module.json`, import.meta.url), "utf8")), module: readFileSync(new URL(`../../target/wasm32-unknown-unknown/release/${name.replaceAll("-", "_")}.wasm`, import.meta.url)) });
const ctx = { wasm: { run: async ({ module, entry, input }) => {
	const x = new WebAssembly.Instance(new WebAssembly.Module(module), {}).exports;
	const data = new TextEncoder().encode(JSON.stringify(input));
	const ptr = x.alloc(data.length);
	new Uint8Array(x.memory.buffer, ptr, data.length).set(data);
	const packed = BigInt.asUintN(64, x[entry](ptr, data.length));
	return JSON.parse(new TextDecoder().decode(new Uint8Array(x.memory.buffer, Number(packed >> 32n), Number(packed & 0xffffffffn))));
} } };

const P = "computer.socialweb.cicd.process.";
const manifest = {
	$type: "com.fedfork.exec.composition",
	name: "workload",
	instances: [{ name: "workload", module: "cicd-workload" }, { name: "process", module: "process-mcp" }],
	edges: ["start", "write", "read"].map((e) => ({ from: "workload", nsid: P + e, to: "process", reason: `the job's ${e}` })),
	exports: [{ nsid: "computer.socialweb.cicd.workload.run", instance: "workload", entry: "run" }],
};

// The guest agent's MCP shell: each job's output is "ran <its script's last line>", in two reads; a script
// with `exit 3` in it exits 3.
function shell() {
	const jobs = [];
	const calls = [];
	const tools = {
		start: ({ command, stdin }) => (assert.equal(stdin, undefined), jobs.push({ command, stdin: "", reads: 0 }), { id: String(jobs.length) }),
		write: ({ id, data, close }) => (assert.equal(close, true), (jobs[id - 1].stdin = data), {}),
		read: ({ id, cursor }) => {
			const job = jobs[id - 1];
			const out = `ran ${job.stdin.trim().split("\n").pop()}\n`;
			if (job.reads++ === 0) return { output: out.slice(0, 4), cursor: 4, done: false };
			return { output: out.slice(cursor), cursor: out.length, done: true, exitCode: job.stdin.includes("exit 3") ? 3 : 0 };
		},
	};
	const fetch = async ({ method, url, body }) => {
		assert.equal(method, "POST");
		assert.equal(url, "iroh:///e30/shell/mcp");
		const { params } = JSON.parse(body);
		calls.push(params.name);
		const out = tools[params.name](params.arguments);
		return { status: 200, headers: {}, body: JSON.stringify({ jsonrpc: "2.0", id: 1, result: { content: [{ type: "text", text: JSON.stringify(out) }], structuredContent: out } }) };
	};
	return { jobs, calls, fetchers: { "iroh:": fetch } };
}

const job = (steps) => ({ checkout: { git: "https://knotless.dev/alice.test/proj", commit: "abc123" }, env: { CI: "1" }, steps });

for (const [name, steps, ok, codes] of [
	["every step runs", [{ name: "build", run: "make" }, { name: "test", run: "make test", env: { X: "it's" } }], true, [0, 0, 0]],
	["the first failing step ends the job", [{ name: "bad", run: "exit 3" }, { name: "never", run: "true" }], false, [0, 3]],
]) {
	test(`cicd-workload: ${name}`, async () => {
		const m = shell();
		const comp = await loadComposition(ctx, manifest, { load: async (key) => lib(key) });
		const said = [];
		const onEvent = (event, out) => out?.result?.output && said.push(out.result.output);
		const ran = await runCompose(ctx, comp, "workload", "run", { url: "iroh:///e30/shell/mcp", job: job(steps) }, { fetchers: m.fetchers, http: { "iroh://*": {} }, onEvent, budget: { calls: Infinity, wallMs: Infinity } });
		assert.equal(ran.error, undefined, ran.error);
		assert.equal(ran.result.ok, ok);
		assert.ok(said.join("").includes("ran "), JSON.stringify(said));
		assert.deepEqual(ran.result.steps.map((s) => s.exitCode), codes);
		assert.equal(ran.result.steps[0].name, "checkout");
		assert.match(m.jobs[0].stdin, /git clone -q 'https:\/\/knotless.dev\/alice.test\/proj' \/work\ncd \/work\ngit checkout -q 'abc123'/);
		assert.match(m.jobs[1].stdin, /^cd \/work\nexport KNOTLESS_RUN_OUTPUTS=\/outputs\nexport CI='1'\n/);
		if (ok) assert.match(m.jobs[2].stdin, /export X='it'\\''s'\nexec sh -ec 'make test'\n$/);
		assert.equal(m.jobs.length, codes.length);
		assert.ok(m.calls.every((c) => ["start", "write", "read"].includes(c)));
	});
}

test("cicd-workload: a runner checks out once, then runs a step a call; nothing to run is ok", async () => {
	const comp = await loadComposition(ctx, manifest, { load: async (key) => lib(key) });
	const run = async (j) => {
		const m = shell();
		const ran = await runCompose(ctx, comp, "workload", "run", { url: "iroh:///e30/shell/mcp", job: j }, { fetchers: m.fetchers, http: { "iroh://*": {} }, budget: { calls: Infinity, wallMs: Infinity } });
		return { ran, m };
	};
	const one = await run({ steps: [{ name: "test", run: "make test" }] });
	assert.deepEqual(one.ran.result.steps, [{ name: "test", exitCode: 0 }]);
	assert.equal(one.m.jobs.length, 1);
	const none = await run({});
	assert.deepEqual([none.ran.result, none.m.jobs.length], [{ steps: [], ok: true }, 0]);
});