Skip to content
54 changes: 52 additions & 2 deletions test/hourly-commercial-readiness-script.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import {
createGhSubprocessEnvironment,
flattenArrayPages,
hasActiveNoemaReviewRun,
latestCheckRunsBySuite,
latestReviewStates,
parseNoemaReviewDecision,
redactSensitiveValue,
} from "../scripts/hourly-commercial-readiness.mjs";

const repository = "ContextualWisdomLab/noema";
Expand Down Expand Up @@ -242,11 +244,59 @@
).toBe(false);
});

it("passes only explicit GitHub CLI authority into child processes", () => {
expect(createGhSubprocessEnvironment({

Check failure on line 248 in test/hourly-commercial-readiness-script.test.ts

View workflow job for this annotation

GitHub Actions / verify

test/hourly-commercial-readiness-script.test.ts > hourly commercial-readiness GitHub adapter > passes only explicit GitHub CLI authority into child processes

TypeError: createGhSubprocessEnvironment is not a function ❯ test/hourly-commercial-readiness-script.test.ts:248:12
PATH: "/trusted/bin",
GH_TOKEN: "read-only-maintainer-token",
GH_HOST: "evil.example",
NO_COLOR: "0",
GITHUB_TOKEN: "ambient-workflow-token",
NVIDIA_NIM_API_KEY: "model-secret",
NOEMA_MAINTAINER_APP_PRIVATE_KEY: "maintainer-private-key",
NOEMA_REVIEWER_APP_PRIVATE_KEY: "reviewer-private-key",
NOEMA_REVIEWER_LOGIN: "reviewer[bot]",
CLOUDFLARE_API_TOKEN: "cloudflare-secret",
HTTPS_PROXY: "http://proxy.invalid",
HTTP_PROXY: "http://proxy.invalid",
ALL_PROXY: "socks5://proxy.invalid",
HOME: "/credential-bearing-home",
NODE_OPTIONS: "--require /tmp/preload.cjs",
NOEMA_MAINTENANCE_ENABLED: "true",
})).toEqual({
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
GH_HOST: "github.com",
NO_COLOR: "1",
PATH: "/trusted/bin",
GH_TOKEN: "read-only-maintainer-token",
});

expect(createGhSubprocessEnvironment({})).toEqual({
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
GH_HOST: "github.com",
NO_COLOR: "1",
});
});

it("redacts an explicit maintainer token before child diagnostics can reach retained outputs", () => {
const token = "read-only-maintainer-token";
const detail = `gh failed with ${token}; retry also exposed ${token}`;

expect(redactSensitiveValue(detail, [token])).toBe(

Check failure on line 282 in test/hourly-commercial-readiness-script.test.ts

View workflow job for this annotation

GitHub Actions / verify

test/hourly-commercial-readiness-script.test.ts > hourly commercial-readiness GitHub adapter > redacts an explicit maintainer token before child diagnostics can reach retained outputs

TypeError: redactSensitiveValue is not a function ❯ test/hourly-commercial-readiness-script.test.ts:282:12
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
"gh failed with [REDACTED]; retry also exposed [REDACTED]",
);
expect(redactSensitiveValue(detail, ["", null, undefined, token])).not.toContain(token);
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
expect(redactSensitiveValue("safe diagnostic", [])).toBe("safe diagnostic");
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
});

it("uses shell-free complete pagination and exact-head write contracts", () => {
const script = readFileSync("scripts/hourly-commercial-readiness.mjs", "utf8");

expect(script).toContain('spawnSync("gh"');
expect(script).toContain("shell: false");
expect(script).toContain("env: childEnvironment");

Check failure on line 294 in test/hourly-commercial-readiness-script.test.ts

View workflow job for this annotation

GitHub Actions / verify

test/hourly-commercial-readiness-script.test.ts > hourly commercial-readiness GitHub adapter > uses shell-free complete pagination and exact-head write contracts

AssertionError: expected '#!/usr/bin/env node\nimport { spawnSy…' to contain 'env: childEnvironment' - Expected + Received - env: childEnvironment + #!/usr/bin/env node + import { spawnSync } from "node:child_process"; + import { appendFileSync, mkdirSync, writeFileSync } from "node:fs"; + import { dirname, resolve } from "node:path"; + import { pathToFileURL } from "node:url"; + import { evaluatePullRequest } from "./lib/commercial-readiness-loop.mjs"; + + const MAX_ERROR_CHARS = 4_000; + const MAX_REPORT_DETAIL_CHARS = 1_000; + const MAX_GH_OUTPUT_BYTES = 16 * 1024 * 1024; + const repositoryPattern = /^ContextualWisdomLab\/[A-Za-z0-9_.-]+$/; + const botLoginPattern = /^[A-Za-z0-9-]+\[bot\]$/; + const fullShaPattern = /^[0-9a-f]{40}$/i; + const noemaMarkerPattern = /<!--\s*noema-review-gate\s+head_sha=([0-9a-f]{40})\s+decision=(approve|request_changes|blocked)\s*-->/gi; + const noemaCredentialMarker = "Reviewer credential: `noema-github-app`"; + const reviewThreadQuery = "query($owner:String!,$name:String!,$number:Int!,$endCursor:String){repository(owner:$owner,name:$name){pullRequest(number:$number){reviewThreads(first:100,after:$endCursor){nodes{isResolved}pageInfo{hasNextPage endCursor}}}}}"; + const activeWorkflowRunStatuses = new Set([ + "requested", + "waiting", + "pending", + "queued", + "in_progress", + ]); + + function bound(value, limit = MAX_REPORT_DETAIL_CHARS) { + const text = String(value ?? "") + .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "") + .trim(); + return text.length <= limit ? text : `${text.slice(0, limit)}…`; + } + + function runGh(args, { input } = {}) { + const completed = spawnSync("gh", args, { + encoding: "utf8", + input, + maxBuffer: MAX_GH_OUTPUT_BYTES, + shell: false, + }); + if (completed.error) { + throw new Error(`GitHub CLI could not start: ${bound(completed.error.message, MAX_ERROR_CHARS)}`); + } + if (completed.status !== 0) { + const detail = completed.stderr || completed.stdout || `exit ${completed.status}`; + throw new Error(`GitHub CLI failed: ${bound(detail, MAX_ERROR_CHARS)}`); + } + return completed.stdout.trim(); + } + + function runGhJson(args, options) { + const raw = runGh(args, options); + if (!raw) { + return null; + } + try { + return JSON.parse(raw); + } catch (error) { + throw new Error(`GitHub CLI returned invalid JSON: ${bound(error.message, MAX_ERROR_CHARS)}`); + } + } + + export function flattenArrayPages(pages) { + if (!Array.isArray(pages)) { + throw new TypeError("Paginated GitHub response must be an array of pages."); + } + return pages.flatMap((page) => { + if (!Array.isArray(page)) { + throw new TypeError("Each paginated GitHub array page must be an array."); + } + return page; + }); + } + + function checkRunTimestamp(check) { + return Math.max( + Date.parse(check?.completed_at || "") || 0, + Date.parse(check?.started_at || "") || 0, + ); + } + + function checkRunChronologicalOrder(left, right) { + const leftId = Number(left?.id); + const rightId = Number(right?.id); + if ( + Number.isSafeInteger(leftId) + && Number.isSafeInteger(rightId) + && leftId !== rightId + ) { + return leftId - rightId; + } + const timeDelta = checkRunTimestamp(left) - checkRunTimestamp(right); + if (timeDelta !== 0) { + return timeDelta; + } + return leftId - rightId; + } + + function checkRunSuiteKey(check) { + const checkId = Number(check?.id); + const suiteId = Number(check?.check_suite?.id); + const name = String(check?.name ?? "").trim(); + const appSlug = String(check?.app?.slug ?? "").trim().toLowerCase(); + if ( + !Number.isSafeInteger(checkId) + || checkId <= 0 + || !Number.isSafeInteger(suiteId) + || suiteId <= 0 + || !name + || !appSlug + ) { + return null; + } + return `${suiteId}\u0000${appSlug}\u0000${name}`; + } + + export function latestCheckRunsBySuite(checkRuns) { + const latestBySuite = new Map(); + for (const check of Array.is
expect(script).not.toContain("env: process.env");
expect(script).toContain(
"redactSensitiveValue(completed.error.message, [childEnvironment.GH_TOKEN])",
);
expect(script).toContain("redactSensitiveValue(rawDetail, [childEnvironment.GH_TOKEN])");
expect(script).toContain('"--paginate", "--slurp"');
expect(script).toContain("pulls?state=open&per_page=100");
expect(script).toContain("check-runs?filter=all&per_page=100");
Expand All @@ -265,16 +315,16 @@
expect(script).toContain("live?.head?.repo?.full_name !== repository");
});

it("writes a bounded report and post-action queue outputs without exposing tokens", () => {
it("writes a bounded report and post-action queue outputs without ambient credential payloads", () => {
const script = readFileSync("scripts/hourly-commercial-readiness.mjs", "utf8");

expect(script).toContain("open_pull_request_count=");
expect(script).toContain("remaining_open_pull_request_count=");
expect(script).toContain("report_path=");
expect(script).toContain("remainingOpenPullRequestCount");
expect(script).toContain("MAX_ERROR_CHARS");
expect(script).not.toContain("GH_TOKEN");
expect(script).not.toContain("GITHUB_TOKEN");
expect(script).not.toContain("read-only-maintainer-token");
});

it("documents the operator contract and buyer-visible governance boundaries", () => {
Expand Down
Loading