Files
dbx/.github/scripts/ci-workflow.test.mjs
T

101 lines
5.8 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const workflow = readFileSync(new URL("../workflows/ci.yml", import.meta.url), "utf8");
function job(name) {
const jobs = workflow.slice(workflow.indexOf("\njobs:\n") + 7);
const pattern = /^ ([\w-]+):\s*$/gm;
const definitions = [...jobs.matchAll(pattern)];
const index = definitions.findIndex((match) => match[1] === name);
assert.ok(index >= 0, `missing job ${name}`);
return jobs.slice(definitions[index].index, definitions[index + 1]?.index ?? jobs.length);
}
test("fast checks run format and contracts before graph resolution or compilation", () => {
const fast = job("fast-checks");
for (const command of ["cargo fmt --check", "node --test scripts/core-architecture.test.mjs",
"node scripts/sync-connection-types.mjs --check", "node .github/scripts/ci-lockfiles.mjs", "node .github/scripts/ci-rust-coverage.mjs"]) assert.ok(fast.includes(command));
assert.ok(fast.indexOf("cargo fmt --check") < fast.indexOf("ci-lockfiles.mjs"));
assert.doesNotMatch(fast, /cargo (?:test|build|clippy)/);
assert.ok(fast.includes("needs.changes.outputs.rust_groups_known == 'true'"));
});
test("Agent and Rust matrices are bounded and do not cancel sibling failures", () => {
for (const [name, output, parallel] of [["rust-test", "rust_matrix", 3], ["agent-rust", "agent_rust", 2],
["agent-go", "agent_go", 8], ["agent-integration", "agent_integration", 8]]) {
const content = job(name);
assert.match(content, /fail-fast: false/);
assert.ok(content.includes(`max-parallel: ${parallel}`));
assert.ok(content.includes(`fromJSON(needs.changes.outputs.${output})`));
assert.match(content, /if: needs\.changes\.outputs\.[\w_]+ == 'true'/);
if (name.startsWith("agent-")) assert.ok(content.includes("needs: [changes, fast-checks, agent-checks]"));
}
});
test("stable Rust, Agent and overall gates always inspect selected upstream results", () => {
for (const [name, mode, dependencies] of [["rust", "rust", ["fast-checks", "rust-fmt-clippy", "rust-test"]],
["agents", "agents", ["fast-checks", "agent-checks", "agent-rust", "agent-go", "agent-integration", "agent-java"]],
["frontend", "frontend", ["frontend-checks", "frontend-typecheck", "frontend-test"]],
["ci", "all", ["rust", "agents", "frontend", "packages", "windows-win7-bundle", "duckdb-windows-driver", "nix-packaging"]]]) {
const content = job(name);
assert.match(content, /if: always\(\)/);
assert.ok(content.includes(`node .github/scripts/ci-gate.mjs ${mode}`));
assert.ok(content.includes("${{ toJSON(needs) }}"));
for (const dependency of dependencies) assert.match(content, new RegExp(`^ - ${dependency}$`, "m"));
}
});
test("frontend tests use two shards on separate runners", () => {
const content = job("frontend-test");
assert.match(content, /fail-fast: false/);
assert.match(content, /shard: \[1, 2\]/);
assert.ok(content.includes("--shard=${{ matrix.shard }}/2"));
assert.ok(content.includes("--reporter=github-actions"));
assert.ok(content.includes("ci-vitest-file-timing-reporter.mjs"));
assert.doesNotMatch(job("frontend-typecheck"), /vitest|oxfmt|oxlint/);
});
test("every old Agent stage has an independent owner and Java packaging remains strict", () => {
assert.ok(job("agent-checks").includes("python3 -m unittest discover"));
assert.ok(job("agent-checks").includes("python3 scripts/validate_agents.py"));
assert.ok(job("agent-checks").includes("bump-agent-versions.test.mjs"));
assert.ok(job("agent-java").includes("./gradlew test shadowJar --continue"));
assert.ok(job("agent-java").includes("python3 scripts/validate_agent_jars.py"));
assert.ok(job("agent-rust").includes('cargo test --manifest-path "drivers/$DRIVER/Cargo.toml" --locked'));
assert.ok(job("agent-rust").includes("cargo build --manifest-path drivers/tdengine/Cargo.toml --locked --release --bin dbx-tdengine-driver"));
assert.ok(job("agent-go").includes("ci-agent-go.sh"));
assert.ok(job("agent-integration").includes("ci-agent-integration.sh"));
assert.ok(job("agent-integration").includes("Setup RocketMQ server Java"));
assert.ok(job("agent-integration").includes("if: matrix.scenario == 'rocketmq'"));
assert.ok(job("agent-integration").includes('java-version: "21"'));
for (const name of ["agent-rust", "agent-go", "agent-integration", "agent-java"]) assert.doesNotMatch(job(name), /continue-on-error: true/);
});
test("native Rust driver caches exclude failed build artifacts", () => {
const content = job("agent-rust");
assert.ok(content.includes('shared-key: ci-agent-rust-v2-${{ matrix.driver }}'));
assert.ok(content.includes("cache-on-failure: false"));
});
test("DuckDB Windows builds persist Rust and C++ compiler results", () => {
const content = job("duckdb-windows-driver");
assert.ok(content.includes('SCCACHE_GHA_ENABLED: "true"'));
assert.ok(content.includes('CC: "sccache cl.exe"'));
assert.ok(content.includes('CXX: "sccache cl.exe"'));
assert.ok(content.includes("0b201ec74fa43914dc39ae48a89fd1d8cb592756"));
assert.ok(content.includes("fc920bf0ec8de6ee65d409111f7ec508035751ba"));
assert.ok(content.includes('version: "v0.16.0"'));
});
test("the planner uses the exact event base and preserves a single workflow cancellation scope", () => {
const changes = job("changes");
assert.ok(changes.includes("github.event.pull_request.base.sha || github.event.before"));
assert.ok(changes.includes("node .github/scripts/ci-plan.mjs"));
for (const flag of ["rust", "rust_full", "rust_matrix", "agents", "agent_go", "agent_rust", "agent_integration", "plan"]) {
assert.ok(changes.includes(`steps.plan.outputs.${flag}`));
}
assert.ok(workflow.includes("group: ${{ github.workflow }}-${{ github.ref }}"));
assert.ok(workflow.includes("cancel-in-progress: true"));
});