agent-comms 9.0.3 → 9.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +4 -0
- package/dist/bridges/user/web/dist/bundle.js +1 -1
- package/dist/bridges/user/web/dist/mesh-worker.js +4 -4
- package/dist/bridges/user/web/dist/sw.js +1 -1
- package/dist/bridges/user/web/frontend/event-pump.d.ts +22 -0
- package/dist/bridges/user/web/frontend/event-pump.d.ts.map +1 -0
- package/dist/bridges/user/web/frontend/event-pump.js +73 -0
- package/dist/bridges/user/web/frontend/event-pump.js.map +1 -0
- package/dist/bridges/user/web/frontend/mesh-client.d.ts +2 -2
- package/dist/bridges/user/web/frontend/mesh-client.d.ts.map +1 -1
- package/dist/bridges/user/web/frontend/mesh-client.js +16 -24
- package/dist/bridges/user/web/frontend/mesh-client.js.map +1 -1
- package/dist/bridges/user/web/frontend/mesh-worker.d.ts +1 -1
- package/dist/bridges/user/web/frontend/mesh-worker.d.ts.map +1 -1
- package/dist/bridges/user/web/frontend/mesh-worker.js +31 -40
- package/dist/bridges/user/web/frontend/mesh-worker.js.map +1 -1
- package/dist/bridges/user/web/test/web-access.unit.test.d.ts +1 -1
- package/dist/bridges/user/web/test/web-access.unit.test.js +19 -375
- package/dist/bridges/user/web/test/web-access.unit.test.js.map +1 -1
- package/dist/core/bridge.d.ts +11 -0
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +17 -2
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/test/build-action.unit.test.d.ts +2 -0
- package/dist/core/test/build-action.unit.test.d.ts.map +1 -0
- package/dist/core/test/build-action.unit.test.js +28 -0
- package/dist/core/test/build-action.unit.test.js.map +1 -0
- package/dist/test/ci-required-checks.unit.test.d.ts +2 -0
- package/dist/test/ci-required-checks.unit.test.d.ts.map +1 -0
- package/dist/test/ci-required-checks.unit.test.js +137 -0
- package/dist/test/ci-required-checks.unit.test.js.map +1 -0
- package/dist/test/claude-command.unit.test.js +2 -21
- package/dist/test/claude-command.unit.test.js.map +1 -1
- package/dist/test/conformance/corpus-files.d.ts +11 -0
- package/dist/test/conformance/corpus-files.d.ts.map +1 -0
- package/dist/test/conformance/corpus-files.js +71 -0
- package/dist/test/conformance/corpus-files.js.map +1 -0
- package/dist/test/conformance/corpus-types.d.ts +62 -0
- package/dist/test/conformance/corpus-types.d.ts.map +1 -0
- package/dist/test/conformance/corpus-types.js +8 -0
- package/dist/test/conformance/corpus-types.js.map +1 -0
- package/dist/test/conformance/corpus.d.ts +34 -0
- package/dist/test/conformance/corpus.d.ts.map +1 -0
- package/dist/test/conformance/corpus.js +134 -0
- package/dist/test/conformance/corpus.js.map +1 -0
- package/dist/test/conformance/read-json.d.ts +2 -0
- package/dist/test/conformance/read-json.d.ts.map +1 -0
- package/dist/test/conformance/read-json.js +7 -0
- package/dist/test/conformance/read-json.js.map +1 -0
- package/dist/test/conformance/text.d.ts +10 -0
- package/dist/test/conformance/text.d.ts.map +1 -0
- package/dist/test/conformance/text.js +64 -0
- package/dist/test/conformance/text.js.map +1 -0
- package/dist/test/conformance/validate.d.ts +14 -0
- package/dist/test/conformance/validate.d.ts.map +1 -0
- package/dist/test/conformance/validate.js +85 -0
- package/dist/test/conformance/validate.js.map +1 -0
- package/dist/test/conformance-functions.helper.d.ts +19 -0
- package/dist/test/conformance-functions.helper.d.ts.map +1 -0
- package/dist/test/conformance-functions.helper.js +583 -0
- package/dist/test/conformance-functions.helper.js.map +1 -0
- package/dist/test/conformance.unit.test.d.ts +2 -0
- package/dist/test/conformance.unit.test.d.ts.map +1 -0
- package/dist/test/conformance.unit.test.js +358 -0
- package/dist/test/conformance.unit.test.js.map +1 -0
- package/dist/test/error-reporting.unit.test.js +2 -90
- package/dist/test/error-reporting.unit.test.js.map +1 -1
- package/dist/test/version-report-actions.unit.test.d.ts +1 -1
- package/dist/test/version-report-actions.unit.test.js +2 -55
- package/dist/test/version-report-actions.unit.test.js.map +1 -1
- package/package.json +4 -2
- package/src/bridges/user/web/dist/bundle.js +1 -1
- package/src/bridges/user/web/dist/mesh-worker.js +4 -4
- package/src/bridges/user/web/dist/sw.js +1 -1
- package/src/core/bridge.ts +30 -1
- package/src/core/index.ts +1 -0
- package/dist/core/test/bridge.unit.test.d.ts +0 -5
- package/dist/core/test/bridge.unit.test.d.ts.map +0 -1
- package/dist/core/test/bridge.unit.test.js +0 -119
- package/dist/core/test/bridge.unit.test.js.map +0 -1
- package/dist/test/agent-table.unit.test.d.ts +0 -5
- package/dist/test/agent-table.unit.test.d.ts.map +0 -1
- package/dist/test/agent-table.unit.test.js +0 -53
- package/dist/test/agent-table.unit.test.js.map +0 -1
- package/dist/test/bind-retry.unit.test.d.ts +0 -2
- package/dist/test/bind-retry.unit.test.d.ts.map +0 -1
- package/dist/test/bind-retry.unit.test.js +0 -42
- package/dist/test/bind-retry.unit.test.js.map +0 -1
- package/dist/test/default-command.unit.test.d.ts +0 -2
- package/dist/test/default-command.unit.test.d.ts.map +0 -1
- package/dist/test/default-command.unit.test.js +0 -26
- package/dist/test/default-command.unit.test.js.map +0 -1
- package/dist/test/delegation-policy.unit.test.d.ts +0 -5
- package/dist/test/delegation-policy.unit.test.d.ts.map +0 -1
- package/dist/test/delegation-policy.unit.test.js +0 -52
- package/dist/test/delegation-policy.unit.test.js.map +0 -1
- package/dist/test/display-name.unit.test.d.ts +0 -5
- package/dist/test/display-name.unit.test.d.ts.map +0 -1
- package/dist/test/display-name.unit.test.js +0 -65
- package/dist/test/display-name.unit.test.js.map +0 -1
- package/dist/test/machine-list-groups.unit.test.d.ts +0 -5
- package/dist/test/machine-list-groups.unit.test.d.ts.map +0 -1
- package/dist/test/machine-list-groups.unit.test.js +0 -61
- package/dist/test/machine-list-groups.unit.test.js.map +0 -1
- package/dist/test/relay-selection.unit.test.d.ts +0 -5
- package/dist/test/relay-selection.unit.test.d.ts.map +0 -1
- package/dist/test/relay-selection.unit.test.js +0 -103
- package/dist/test/relay-selection.unit.test.js.map +0 -1
- package/dist/test/request-timeouts.unit.test.d.ts +0 -2
- package/dist/test/request-timeouts.unit.test.d.ts.map +0 -1
- package/dist/test/request-timeouts.unit.test.js +0 -28
- package/dist/test/request-timeouts.unit.test.js.map +0 -1
- package/dist/test/room-discovery.unit.test.d.ts +0 -2
- package/dist/test/room-discovery.unit.test.d.ts.map +0 -1
- package/dist/test/room-discovery.unit.test.js +0 -29
- package/dist/test/room-discovery.unit.test.js.map +0 -1
- package/dist/test/room-lookup.unit.test.d.ts +0 -2
- package/dist/test/room-lookup.unit.test.d.ts.map +0 -1
- package/dist/test/room-lookup.unit.test.js +0 -59
- package/dist/test/room-lookup.unit.test.js.map +0 -1
- package/dist/test/room-path.unit.test.d.ts +0 -2
- package/dist/test/room-path.unit.test.d.ts.map +0 -1
- package/dist/test/room-path.unit.test.js +0 -54
- package/dist/test/room-path.unit.test.js.map +0 -1
- package/dist/test/send-outcome.unit.test.d.ts +0 -2
- package/dist/test/send-outcome.unit.test.d.ts.map +0 -1
- package/dist/test/send-outcome.unit.test.js +0 -52
- package/dist/test/send-outcome.unit.test.js.map +0 -1
package/dist/core/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export { createMeshErrorReporter, formatMeshError } from "./error-reporting.js";
|
|
|
11
11
|
export { installShutdownSignalHandlers, } from "./shutdown-signals.js";
|
|
12
12
|
export { createBridgeMesh, createBridgeMeshSync, createBridgeMeshFromIdentity, createBridgeMeshSyncFromIdentity, } from "./bridge-mesh.js";
|
|
13
13
|
export { loadIdentityForFront, probeSlotOwner, releaseIdentityLock, } from "./identity-store.js";
|
|
14
|
-
export { buildAction, formatDeliveryEvent, extractStreamingBehavior, isActionableEvent, ensureRegistered, ensureProjectRoom, drainAndFormat, MCP_TOOL_PARAMS, } from "./bridge.js";
|
|
14
|
+
export { buildAction, InvalidToolParamsError, formatDeliveryEvent, extractStreamingBehavior, isActionableEvent, ensureRegistered, ensureProjectRoom, drainAndFormat, MCP_TOOL_PARAMS, } from "./bridge.js";
|
|
15
15
|
export { DiscoveryManager } from "./discovery.js";
|
|
16
16
|
export { MdnsDiscoveryBackend } from "./discovery-mdns.js";
|
|
17
17
|
export { TailscaleDiscoveryBackend } from "./discovery-tailscale.js";
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EACL,6BAA6B,GAG9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,EAC5B,gCAAgC,GACjC,MAAM,kBAAkB,CAAC;AAO1B,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAMlD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EACL,6BAA6B,GAG9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,4BAA4B,EAC5B,gCAAgC,GACjC,MAAM,kBAAkB,CAAC;AAO1B,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAMlD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-action.unit.test.d.ts","sourceRoot":"","sources":["../../../src/core/test/build-action.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildAction, InvalidToolParamsError } from "../bridge.js";
|
|
3
|
+
function failure(params) {
|
|
4
|
+
try {
|
|
5
|
+
buildAction(params);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
return error;
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
describe("buildAction refuses parameters that fail validation", () => {
|
|
13
|
+
it("names an unknown action and where it is, instead of turning the call into whoami", () => {
|
|
14
|
+
const error = failure({ action: "no_such_action" });
|
|
15
|
+
expect(error).toBeInstanceOf(InvalidToolParamsError);
|
|
16
|
+
expect(error.issues.map((i) => i.path)).toEqual([["action"]]);
|
|
17
|
+
expect(error.message).toContain("action");
|
|
18
|
+
});
|
|
19
|
+
it("names a parameter of the wrong type", () => {
|
|
20
|
+
const error = failure({ action: "mesh_listen", host: "h", port: "7" });
|
|
21
|
+
expect(error).toBeInstanceOf(InvalidToolParamsError);
|
|
22
|
+
expect(error.issues.map((i) => i.path)).toEqual([["port"]]);
|
|
23
|
+
});
|
|
24
|
+
it("still builds parameters that validate", () => {
|
|
25
|
+
expect(buildAction({ action: "whoami" })).toEqual({ action: "whoami" });
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=build-action.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-action.unit.test.js","sourceRoot":"","sources":["../../../src/core/test/build-action.unit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEnE,SAAS,OAAO,CAAC,MAA+B;IAC9C,IAAI,CAAC;QACH,WAAW,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAC;QACrD,MAAM,CAAE,KAAgC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CACzE,CAAC,CAAC,QAAQ,CAAC,CAAC,CACb,CAAC;QACF,MAAM,CAAE,KAAgC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAC;QACrD,MAAM,CAAE,KAAgC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CACzE,CAAC,CAAC,MAAM,CAAC,CAAC,CACX,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-required-checks.unit.test.d.ts","sourceRoot":"","sources":["../../src/test/ci-required-checks.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { parse } from "yaml";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const Job = z.looseObject({
|
|
7
|
+
name: z.string().optional(),
|
|
8
|
+
if: z.string().optional(),
|
|
9
|
+
needs: z.union([z.string(), z.array(z.string())]).optional(),
|
|
10
|
+
steps: z
|
|
11
|
+
.array(z.looseObject({
|
|
12
|
+
run: z.string().optional(),
|
|
13
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
14
|
+
}))
|
|
15
|
+
.optional(),
|
|
16
|
+
});
|
|
17
|
+
const Workflow = z.object({ jobs: z.record(z.string(), Job) });
|
|
18
|
+
const workflow = Workflow.parse(parse(readFileSync(new URL("../../.github/workflows/ci.yml", import.meta.url), "utf-8")));
|
|
19
|
+
/** The job the ruleset's required status check should name. */
|
|
20
|
+
const AGGREGATE = "required-checks";
|
|
21
|
+
/**
|
|
22
|
+
* The only conditions that make a job run on a push or a manual dispatch and never on a pull request. Any other condition on a job outside the aggregate is refused, because a condition that merely mentions the push event (`push || pull_request`, a negation, a status function) can still run on a pull request.
|
|
23
|
+
*/
|
|
24
|
+
const NEVER_ON_A_PULL_REQUEST = [
|
|
25
|
+
"github.event_name == 'push'",
|
|
26
|
+
"github.ref == 'refs/heads/main' && github.event_name == 'push'",
|
|
27
|
+
"github.event_name == 'workflow_dispatch' && inputs.registry_ref == ''",
|
|
28
|
+
"!cancelled() && needs.release.result != 'failure' && ((github.ref == 'refs/heads/main' && github.event_name == 'push') || github.event_name == 'workflow_dispatch')",
|
|
29
|
+
];
|
|
30
|
+
/** The status functions that let a job run although a job it needs was skipped or failed. */
|
|
31
|
+
const STATUS_FUNCTION = /\b(?:always|failure|cancelled|success)\s*\(/iu;
|
|
32
|
+
function needsOf(jobs, id) {
|
|
33
|
+
const needs = jobs[id]?.needs;
|
|
34
|
+
if (needs === undefined)
|
|
35
|
+
return [];
|
|
36
|
+
return typeof needs === "string" ? [needs] : needs;
|
|
37
|
+
}
|
|
38
|
+
/** A job's condition with its whitespace collapsed and any `${{ }}` wrapper removed. */
|
|
39
|
+
function normalisedCondition(jobs, id) {
|
|
40
|
+
const condition = jobs[id]?.if;
|
|
41
|
+
if (condition === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
const unwrapped = /^\$\{\{(.*)\}\}$/su.exec(condition.trim());
|
|
44
|
+
return (unwrapped?.[1] ?? condition).trim().replace(/\s+/gu, " ");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether a job never runs on a pull request, where a merge is waiting on it: its condition is one of the exact forms above, or it needs such a job and its own condition has no status function that would run it when that job is skipped.
|
|
48
|
+
*/
|
|
49
|
+
function neverOnPullRequest(jobs, id, seen = new Set()) {
|
|
50
|
+
if (seen.has(id))
|
|
51
|
+
return false;
|
|
52
|
+
const condition = normalisedCondition(jobs, id);
|
|
53
|
+
if (condition !== undefined && NEVER_ON_A_PULL_REQUEST.includes(condition)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (condition !== undefined && STATUS_FUNCTION.test(condition))
|
|
57
|
+
return false;
|
|
58
|
+
return needsOf(jobs, id).some((need) => neverOnPullRequest(jobs, need, new Set([...seen, id])));
|
|
59
|
+
}
|
|
60
|
+
describe("ci.yml required-checks", () => {
|
|
61
|
+
const job = workflow.jobs[AGGREGATE];
|
|
62
|
+
it("has the name a ruleset's required status check should use", () => {
|
|
63
|
+
expect(job?.name).toBe("Required Checks");
|
|
64
|
+
});
|
|
65
|
+
it("needs every job that runs on a pull request, so none can be ignored by the merge", () => {
|
|
66
|
+
const expected = Object.keys(workflow.jobs)
|
|
67
|
+
.filter((id) => id !== AGGREGATE && !neverOnPullRequest(workflow.jobs, id))
|
|
68
|
+
.sort();
|
|
69
|
+
expect([...needsOf(workflow.jobs, AGGREGATE)].sort()).toEqual(expected);
|
|
70
|
+
});
|
|
71
|
+
it("needs only jobs that always run, since a skipped needed job reads as passing", () => {
|
|
72
|
+
const conditioned = needsOf(workflow.jobs, AGGREGATE).filter((id) => workflow.jobs[id]?.if !== undefined);
|
|
73
|
+
expect(conditioned).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
it("leaves out a job only when its condition is exactly a form that never runs on a pull request", () => {
|
|
76
|
+
const needed = needsOf(workflow.jobs, AGGREGATE);
|
|
77
|
+
const outside = Object.keys(workflow.jobs).filter((id) => id !== AGGREGATE && !needed.includes(id));
|
|
78
|
+
for (const id of outside) {
|
|
79
|
+
expect(neverOnPullRequest(workflow.jobs, id), `${id} is outside the aggregate`).toBe(true);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
describe("the classification", () => {
|
|
83
|
+
const parsed = (jobs) => Workflow.parse({ jobs }).jobs;
|
|
84
|
+
it("accepts the exact conditions", () => {
|
|
85
|
+
expect(neverOnPullRequest(parsed({ a: { if: "github.event_name == 'push'" } }), "a")).toBe(true);
|
|
86
|
+
expect(neverOnPullRequest(parsed({
|
|
87
|
+
a: {
|
|
88
|
+
if: "${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}",
|
|
89
|
+
},
|
|
90
|
+
}), "a")).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
it("refuses a condition that can run on a pull request", () => {
|
|
93
|
+
for (const condition of [
|
|
94
|
+
"github.event_name == 'push' || github.event_name == 'pull_request'",
|
|
95
|
+
"always() && github.event_name == 'push'",
|
|
96
|
+
"Always() && github.event_name == 'push'",
|
|
97
|
+
"!(github.event_name == 'push')",
|
|
98
|
+
"github.event_name != 'pull_request'",
|
|
99
|
+
"contains(github.event_name, 'push')",
|
|
100
|
+
]) {
|
|
101
|
+
expect(neverOnPullRequest(parsed({ a: { if: condition } }), "a"), condition).toBe(false);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
it("follows a need on such a job unless the condition runs despite it", () => {
|
|
105
|
+
const release = { if: "github.event_name == 'push'" };
|
|
106
|
+
expect(neverOnPullRequest(parsed({ release, a: { needs: ["release"] } }), "a")).toBe(true);
|
|
107
|
+
expect(neverOnPullRequest(parsed({ release, a: { needs: ["release"], if: "Always()" } }), "a")).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it("runs even when a needed job fails, since a skipped aggregate reads as passing", () => {
|
|
111
|
+
expect(job?.if).toBe("always()");
|
|
112
|
+
});
|
|
113
|
+
describe("its verdict", () => {
|
|
114
|
+
const step = job?.steps?.[0];
|
|
115
|
+
const script = step?.run ?? "";
|
|
116
|
+
function verdict(results) {
|
|
117
|
+
return spawnSync("bash", ["-c", script], {
|
|
118
|
+
env: { PATH: process.env.PATH ?? "", RESULTS: results },
|
|
119
|
+
}).status;
|
|
120
|
+
}
|
|
121
|
+
it("has a script to run", () => {
|
|
122
|
+
expect(script).not.toBe("");
|
|
123
|
+
});
|
|
124
|
+
it("passes on success and on a job skipped on purpose", () => {
|
|
125
|
+
expect(verdict("success success skipped success")).toBe(0);
|
|
126
|
+
});
|
|
127
|
+
it("fails on a failed, cancelled or unrecognised result", () => {
|
|
128
|
+
for (const bad of ["failure", "cancelled", "timed_out", "neutral"]) {
|
|
129
|
+
expect(verdict(`success ${bad}`), bad).toBe(1);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
it("reads the results from the needs context", () => {
|
|
133
|
+
expect(step?.env?.RESULTS).toBe("${{ join(needs.*.result, ' ') }}");
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
//# sourceMappingURL=ci-required-checks.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-required-checks.unit.test.js","sourceRoot":"","sources":["../../src/test/ci-required-checks.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { CLAUDE_COMM_SHELL_PATTERNS
|
|
4
|
-
|
|
5
|
-
it.each([
|
|
6
|
-
"claude",
|
|
7
|
-
"/opt/homebrew/Caskroom/claude-code/2.1.311/claude",
|
|
8
|
-
"/Users/example/.local/bin/claude",
|
|
9
|
-
"/Users/example/.local/share/claude/versions/2.1.322",
|
|
10
|
-
])("accepts %s", (comm) => {
|
|
11
|
-
expect(isClaudeCodeComm(comm)).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
it.each([
|
|
14
|
-
"node",
|
|
15
|
-
"-zsh",
|
|
16
|
-
"tmux",
|
|
17
|
-
"/usr/bin/claude-notification",
|
|
18
|
-
"/Users/example/.local/share/claude/versions/notes",
|
|
19
|
-
"/Users/example/.local/share/claude/versions/2.1.322/helper",
|
|
20
|
-
])("rejects %s", (comm) => {
|
|
21
|
-
expect(isClaudeCodeComm(comm)).toBe(false);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
3
|
+
import { CLAUDE_COMM_SHELL_PATTERNS } from "../bridges/claude-code/claudeCommand.js";
|
|
4
|
+
// The cases for isClaudeCodeComm itself are in the conformance corpus (conformance/function/claude-command.json). This stays here because it reads a file of this repository.
|
|
24
5
|
describe("hooks/drain.sh", () => {
|
|
25
6
|
it("matches the process forms the bridge matches", () => {
|
|
26
7
|
const script = readFileSync(new URL("../../hooks/drain.sh", import.meta.url), "utf8");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-command.unit.test.js","sourceRoot":"","sources":["../../src/test/claude-command.unit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,
|
|
1
|
+
{"version":3,"file":"claude-command.unit.test.js","sourceRoot":"","sources":["../../src/test/claude-command.unit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAErF,8KAA8K;AAC9K,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,MAAM,GAAG,YAAY,CACzB,IAAI,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAChD,MAAM,CACP,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,0BAA0B,QAAQ,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface CorpusSelection {
|
|
2
|
+
/** Corpus files as `/`-separated paths relative to the root, in the order a walk in UTF-16 code unit order finds them. */
|
|
3
|
+
readonly selected: readonly string[];
|
|
4
|
+
/** Entries that are neither a corpus file, a known root file nor a skipped directory, in the same order. */
|
|
5
|
+
readonly problems: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Applies the selection rule to the tree at `root`. A name starting with a dot is ignored at any depth, a directory named `fixtures`, `schema` or `runner` is skipped at any depth, and everything else must be a `.json` file at `<kind>/<name>.json`, with names of lower-case ASCII letters, digits, dots, hyphens and underscores, or one of the root's own files; anything else is returned in `problems`, for the caller to fail on.
|
|
9
|
+
*/
|
|
10
|
+
export declare function selectCorpusFiles(root: string): CorpusSelection;
|
|
11
|
+
//# sourceMappingURL=corpus-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus-files.d.ts","sourceRoot":"","sources":["../../../src/test/conformance/corpus-files.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,eAAe;IAC9B,0HAA0H;IAC1H,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,4GAA4G;IAC5G,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAmDD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAQ/D"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Which files under the corpus root are corpus files, by the one rule `conformance/README.md` states. Every loader of the corpus (this one, the native crates' and the wasm runner's) implements the same rule, and each runs it over the fixture tree in `conformance/fixtures/loader-selection` so the three cannot disagree.
|
|
5
|
+
*/
|
|
6
|
+
/** Directories that hold data cases refer to or the runners themselves, never cases, wherever they appear. */
|
|
7
|
+
const NON_CASE_DIRECTORIES = new Set([
|
|
8
|
+
"fixtures",
|
|
9
|
+
"schema",
|
|
10
|
+
"runner",
|
|
11
|
+
]);
|
|
12
|
+
/** The files the corpus root itself holds. */
|
|
13
|
+
const ROOT_FILES = new Set([
|
|
14
|
+
"README.md",
|
|
15
|
+
"case.schema.json",
|
|
16
|
+
"manifest.json",
|
|
17
|
+
"manifest.schema.json",
|
|
18
|
+
]);
|
|
19
|
+
/** The characters a directory or file name outside the root's own files may use, so every loader orders names the same way. */
|
|
20
|
+
const NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
21
|
+
/** The depth at which a directory holds corpus files: `<kind>/<name>.json`. */
|
|
22
|
+
const AREA_DEPTH = 1;
|
|
23
|
+
function byName(left, right) {
|
|
24
|
+
if (left.name === right.name) {
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
return left.name < right.name ? -1 : 1;
|
|
28
|
+
}
|
|
29
|
+
function scan(root, relative, depth, selection) {
|
|
30
|
+
const entries = readdirSync(join(root, relative), { withFileTypes: true })
|
|
31
|
+
.filter((entry) => !entry.name.startsWith("."))
|
|
32
|
+
.sort(byName);
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
35
|
+
if (entry.isDirectory()) {
|
|
36
|
+
if (NON_CASE_DIRECTORIES.has(entry.name)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (depth < AREA_DEPTH && NAME_PATTERN.test(entry.name)) {
|
|
40
|
+
scan(root, path, depth + 1, selection);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
selection.problems.push(path);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (entry.isFile() && depth === 0 && ROOT_FILES.has(entry.name)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
else if (entry.isFile() &&
|
|
50
|
+
depth === AREA_DEPTH &&
|
|
51
|
+
entry.name.endsWith(".json") &&
|
|
52
|
+
NAME_PATTERN.test(entry.name)) {
|
|
53
|
+
selection.selected.push(path);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
selection.problems.push(path);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Applies the selection rule to the tree at `root`. A name starting with a dot is ignored at any depth, a directory named `fixtures`, `schema` or `runner` is skipped at any depth, and everything else must be a `.json` file at `<kind>/<name>.json`, with names of lower-case ASCII letters, digits, dots, hyphens and underscores, or one of the root's own files; anything else is returned in `problems`, for the caller to fail on.
|
|
62
|
+
*/
|
|
63
|
+
export function selectCorpusFiles(root) {
|
|
64
|
+
const selection = {
|
|
65
|
+
selected: [],
|
|
66
|
+
problems: [],
|
|
67
|
+
};
|
|
68
|
+
scan(root, "", 0, selection);
|
|
69
|
+
return selection;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=corpus-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus-files.js","sourceRoot":"","sources":["../../../src/test/conformance/corpus-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes of the corpus cases the TypeScript runner executes. `conformance/case.schema.json` is the definition; these types state what a document that passed it holds, so a value is trusted only after the schema has accepted it.
|
|
3
|
+
*/
|
|
4
|
+
/** The case kinds the schema defines, and so the names of the corpus directories. */
|
|
5
|
+
export declare const KINDS: readonly ["function", "validate"];
|
|
6
|
+
/** The values of `skip`: the implementation that skips a case, never the host it runs on. */
|
|
7
|
+
export declare const SKIP_VALUES: readonly ["ts", "rust"];
|
|
8
|
+
/** Any JSON value, in which a string may also be written as `{ "codeUnits": [...] }`. */
|
|
9
|
+
export type CorpusValue = null | boolean | number | string | readonly CorpusValue[] | {
|
|
10
|
+
readonly [key: string]: CorpusValue;
|
|
11
|
+
};
|
|
12
|
+
/** A string as UTF-16 code units: a JSON string, or the code units themselves for content a JSON string cannot carry. */
|
|
13
|
+
export type Text = string | {
|
|
14
|
+
readonly codeUnits: readonly number[];
|
|
15
|
+
};
|
|
16
|
+
interface CaseBase {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly from: string;
|
|
19
|
+
readonly skip?: (typeof SKIP_VALUES)[number];
|
|
20
|
+
readonly reason?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface FunctionCase extends CaseBase {
|
|
23
|
+
readonly kind: "function";
|
|
24
|
+
readonly function: string;
|
|
25
|
+
readonly args: readonly CorpusValue[];
|
|
26
|
+
readonly expect?: CorpusValue;
|
|
27
|
+
readonly expectSubset?: CorpusValue;
|
|
28
|
+
readonly expectUndefined?: true;
|
|
29
|
+
readonly throws?: {
|
|
30
|
+
readonly code?: string;
|
|
31
|
+
readonly message?: readonly Text[];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface ValidateIssue {
|
|
35
|
+
readonly path: readonly (string | number)[];
|
|
36
|
+
readonly code?: string;
|
|
37
|
+
readonly message?: readonly Text[];
|
|
38
|
+
}
|
|
39
|
+
export interface ValidateCase extends CaseBase {
|
|
40
|
+
readonly kind: "validate";
|
|
41
|
+
readonly schema: string;
|
|
42
|
+
readonly input: CorpusValue;
|
|
43
|
+
readonly expect: {
|
|
44
|
+
readonly ok: true;
|
|
45
|
+
readonly value?: CorpusValue;
|
|
46
|
+
} | {
|
|
47
|
+
readonly ok: false;
|
|
48
|
+
readonly issues: readonly ValidateIssue[];
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export type CorpusCase = FunctionCase | ValidateCase;
|
|
52
|
+
export interface CorpusFile {
|
|
53
|
+
readonly cases: readonly CorpusCase[];
|
|
54
|
+
}
|
|
55
|
+
export interface Manifest {
|
|
56
|
+
readonly files: Readonly<Record<string, {
|
|
57
|
+
readonly cases: number;
|
|
58
|
+
readonly skip: readonly string[];
|
|
59
|
+
}>>;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=corpus-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus-types.d.ts","sourceRoot":"","sources":["../../../src/test/conformance/corpus-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qFAAqF;AACrF,eAAO,MAAM,KAAK,mCAAoC,CAAC;AAEvD,6FAA6F;AAC7F,eAAO,MAAM,WAAW,yBAA0B,CAAC;AAEnD,yFAAyF;AACzF,MAAM,MAAM,WAAW,GACnB,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,WAAW,EAAE,GACtB;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,CAAC;AAE5C,yHAAyH;AACzH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEtE,UAAU,QAAQ;IAChB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC;IACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;KACpC,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EACX;QAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,GACnD;QAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;KAAE,CAAC;CACvE;AAED,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,YAAY,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CACtB,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC,CAC7E,CAAC;CACH"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes of the corpus cases the TypeScript runner executes. `conformance/case.schema.json` is the definition; these types state what a document that passed it holds, so a value is trusted only after the schema has accepted it.
|
|
3
|
+
*/
|
|
4
|
+
/** The case kinds the schema defines, and so the names of the corpus directories. */
|
|
5
|
+
export const KINDS = ["function", "validate"];
|
|
6
|
+
/** The values of `skip`: the implementation that skips a case, never the host it runs on. */
|
|
7
|
+
export const SKIP_VALUES = ["ts", "rust"];
|
|
8
|
+
//# sourceMappingURL=corpus-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus-types.js","sourceRoot":"","sources":["../../../src/test/conformance/corpus-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qFAAqF;AACrF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,UAAU,EAAE,UAAU,CAAU,CAAC;AAEvD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,CAAU,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CorpusFile, Manifest } from "./corpus-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Loads and checks a whole corpus root by every rule `conformance/README.md` states: the files selected, each file valid, ids unique across files, and the corpus equal to its manifest. The real corpus and every fixture corpus under `conformance/fixtures/corpora` go through this one function, so a rule has one definition.
|
|
4
|
+
*/
|
|
5
|
+
export interface LoadedFile {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly file: CorpusFile;
|
|
8
|
+
}
|
|
9
|
+
export interface CheckedCorpus {
|
|
10
|
+
readonly files: readonly LoadedFile[];
|
|
11
|
+
/** Everything wrong with the corpus; empty when it is sound. */
|
|
12
|
+
readonly problems: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/** The case ids this implementation registers to run and the ones it skips because they are marked for it alone. A case marked for the other implementation is registered and run here. */
|
|
15
|
+
export declare function collect(files: readonly LoadedFile[]): {
|
|
16
|
+
readonly registered: readonly string[];
|
|
17
|
+
readonly skipped: readonly string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The completeness check, per implementation: what this implementation registered and skipped must add up to the manifest's total, and what it skips must be marked in the manifest. Cases marked for the other implementation count as registered here, so they cannot fail it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function completenessProblems(total: number, markedIds: ReadonlySet<string>, registered: readonly string[], skipped: readonly string[]): readonly string[];
|
|
23
|
+
export declare function checkCorpus(root: string): CheckedCorpus;
|
|
24
|
+
/** One case as the runner registered it with the test framework, recorded in the loop that registers it. */
|
|
25
|
+
export interface Registration {
|
|
26
|
+
readonly file: string;
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly skip: "ts" | "rust" | undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* What a run did, against what the manifest says the corpus holds: every file has as many registrations as the manifest has cases, the cases this implementation skips are the ones carrying `skip: ts` and are listed in the manifest, and every other registration ran. `registrations` is filled where the runner registers each case and `ran` where each registered case's body starts, so a runner that skips, filters or mis-keys cases reads as a failure here, and the corpus alone cannot make it pass.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runCompletenessProblems(manifest: Manifest, registrations: readonly Registration[], ran: ReadonlySet<string>): readonly string[];
|
|
34
|
+
//# sourceMappingURL=corpus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus.d.ts","sourceRoot":"","sources":["../../../src/test/conformance/corpus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI9D;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAkBD,2LAA2L;AAC3L,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG;IACrD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC,CAOA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,EAC9B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,SAAS,MAAM,EAAE,CAoBnB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CA2EvD;AAED,4GAA4G;AAC5G,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,SAAS,YAAY,EAAE,EACtC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,GACvB,SAAS,MAAM,EAAE,CAiCnB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { selectCorpusFiles } from "./corpus-files.js";
|
|
3
|
+
import { readJson } from "./read-json.js";
|
|
4
|
+
import { validateCorpusFile, validateManifest } from "./validate.js";
|
|
5
|
+
/** What the files of a corpus say about themselves, in the manifest's own shape. */
|
|
6
|
+
function describeFiles(files) {
|
|
7
|
+
return Object.fromEntries(files.map(({ name, file }) => [
|
|
8
|
+
name,
|
|
9
|
+
JSON.stringify({
|
|
10
|
+
cases: file.cases.length,
|
|
11
|
+
skip: file.cases
|
|
12
|
+
.filter((testCase) => testCase.skip !== undefined)
|
|
13
|
+
.map((testCase) => testCase.id)
|
|
14
|
+
.sort(),
|
|
15
|
+
}),
|
|
16
|
+
]));
|
|
17
|
+
}
|
|
18
|
+
/** The case ids this implementation registers to run and the ones it skips because they are marked for it alone. A case marked for the other implementation is registered and run here. */
|
|
19
|
+
export function collect(files) {
|
|
20
|
+
const cases = files.flatMap(({ file }) => file.cases);
|
|
21
|
+
return {
|
|
22
|
+
registered: cases.filter(({ skip }) => skip !== "ts").map(({ id }) => id),
|
|
23
|
+
skipped: cases.filter(({ skip }) => skip === "ts").map(({ id }) => id),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The completeness check, per implementation: what this implementation registered and skipped must add up to the manifest's total, and what it skips must be marked in the manifest. Cases marked for the other implementation count as registered here, so they cannot fail it.
|
|
28
|
+
*/
|
|
29
|
+
export function completenessProblems(total, markedIds, registered, skipped) {
|
|
30
|
+
const problems = [];
|
|
31
|
+
if (registered.length + skipped.length !== total) {
|
|
32
|
+
problems.push(`registered ${String(registered.length)} plus skipped ${String(skipped.length)} is not the manifest's ${String(total)}`);
|
|
33
|
+
}
|
|
34
|
+
for (const id of skipped) {
|
|
35
|
+
if (!markedIds.has(id)) {
|
|
36
|
+
problems.push(`${id} is skipped here and not marked in the manifest`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (new Set([...registered, ...skipped]).size !==
|
|
40
|
+
registered.length + skipped.length) {
|
|
41
|
+
problems.push("a case is both registered and skipped, or registered twice");
|
|
42
|
+
}
|
|
43
|
+
return problems;
|
|
44
|
+
}
|
|
45
|
+
export function checkCorpus(root) {
|
|
46
|
+
const selection = selectCorpusFiles(root);
|
|
47
|
+
const problems = selection.problems.map((path) => `${path}: not a corpus file and not a recognised root file`);
|
|
48
|
+
const files = [];
|
|
49
|
+
const ids = new Set();
|
|
50
|
+
for (const name of selection.selected) {
|
|
51
|
+
const result = validateCorpusFile(readJson(join(root, name)));
|
|
52
|
+
if (!result.ok) {
|
|
53
|
+
problems.push(...result.problems.map((problem) => `${name}: ${problem}`));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const directory = name.slice(0, name.indexOf("/"));
|
|
57
|
+
for (const { id, kind } of result.value.cases) {
|
|
58
|
+
if (kind !== directory) {
|
|
59
|
+
problems.push(`${name}: case ${id} is of kind ${kind}, and the directory names kind ${directory}`);
|
|
60
|
+
}
|
|
61
|
+
if (ids.has(id)) {
|
|
62
|
+
problems.push(`${name}: case id ${id} is used by another file`);
|
|
63
|
+
}
|
|
64
|
+
ids.add(id);
|
|
65
|
+
}
|
|
66
|
+
files.push({ name, file: result.value });
|
|
67
|
+
}
|
|
68
|
+
let manifest;
|
|
69
|
+
try {
|
|
70
|
+
manifest = validateManifest(readJson(join(root, "manifest.json")));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
manifest = {
|
|
74
|
+
ok: false,
|
|
75
|
+
problems: [error instanceof Error ? error.message : String(error)],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (!manifest.ok) {
|
|
79
|
+
problems.push(...manifest.problems.map((problem) => `manifest.json: ${problem}`));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const recorded = Object.fromEntries(Object.entries(manifest.value.files).map(([name, entry]) => [
|
|
83
|
+
name,
|
|
84
|
+
JSON.stringify({ cases: entry.cases, skip: entry.skip }),
|
|
85
|
+
]));
|
|
86
|
+
const actual = describeFiles(files);
|
|
87
|
+
const counts = collect(files);
|
|
88
|
+
problems.push(...completenessProblems(Object.values(manifest.value.files).reduce((sum, entry) => sum + entry.cases, 0), new Set(Object.values(manifest.value.files).flatMap((entry) => entry.skip)), counts.registered, counts.skipped));
|
|
89
|
+
for (const name of new Set([
|
|
90
|
+
...Object.keys(recorded),
|
|
91
|
+
...Object.keys(actual),
|
|
92
|
+
])) {
|
|
93
|
+
if (recorded[name] !== actual[name]) {
|
|
94
|
+
problems.push(`manifest.json does not match ${name}: the file holds ${actual[name] ?? "nothing"}, the manifest records ${recorded[name] ?? "nothing"}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { files, problems };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What a run did, against what the manifest says the corpus holds: every file has as many registrations as the manifest has cases, the cases this implementation skips are the ones carrying `skip: ts` and are listed in the manifest, and every other registration ran. `registrations` is filled where the runner registers each case and `ran` where each registered case's body starts, so a runner that skips, filters or mis-keys cases reads as a failure here, and the corpus alone cannot make it pass.
|
|
102
|
+
*/
|
|
103
|
+
export function runCompletenessProblems(manifest, registrations, ran) {
|
|
104
|
+
const problems = [];
|
|
105
|
+
const perFile = new Map();
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
for (const { file, id, skip } of registrations) {
|
|
108
|
+
perFile.set(file, (perFile.get(file) ?? 0) + 1);
|
|
109
|
+
if (seen.has(id)) {
|
|
110
|
+
problems.push(`${id} was registered twice`);
|
|
111
|
+
}
|
|
112
|
+
seen.add(id);
|
|
113
|
+
const entry = manifest.files[file];
|
|
114
|
+
if (skip === "ts" && entry?.skip.includes(id) !== true) {
|
|
115
|
+
problems.push(`${id} is skipped here and not listed in the manifest`);
|
|
116
|
+
}
|
|
117
|
+
if (skip !== "ts" && !ran.has(id)) {
|
|
118
|
+
problems.push(`${id} was registered and did not run`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const [file, entry] of Object.entries(manifest.files)) {
|
|
122
|
+
const count = perFile.get(file) ?? 0;
|
|
123
|
+
if (count !== entry.cases) {
|
|
124
|
+
problems.push(`${file}: ${String(count)} cases registered, the manifest has ${String(entry.cases)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const file of perFile.keys()) {
|
|
128
|
+
if (!(file in manifest.files)) {
|
|
129
|
+
problems.push(`${file} was registered and is not in the manifest`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return problems;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=corpus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"corpus.js","sourceRoot":"","sources":["../../../src/test/conformance/corpus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-json.d.ts","sourceRoot":"","sources":["../../../src/test/conformance/read-json.ts"],"names":[],"mappings":"AAKA,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C"}
|