forgedock 1.29.0 → 1.30.0
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/bin/engine/outcome-codes.mjs +4 -2
- package/bin/engine/phases.mjs +20 -3
- package/bin/engine-cli.mjs +102 -38
- package/bin/engine.mjs +3 -1
- package/bin/lean/batch.test.mjs +62 -1
- package/bin/lean/conformance.mjs +129 -8
- package/bin/lean/conformance.test.mjs +77 -3
- package/bin/lean/context.mjs +32 -4
- package/bin/lean/context.test.mjs +71 -12
- package/bin/lean/effects.mjs +20 -6
- package/bin/lean/enumerating.mjs +189 -0
- package/bin/lean/enumerating.test.mjs +97 -0
- package/bin/lean/lean.mjs +207 -56
- package/bin/lean/lean.test.mjs +336 -9
- package/bin/lean/orchestrate.mjs +132 -83
- package/bin/lean/precheck.mjs +55 -0
- package/bin/lean/precheck.test.mjs +93 -0
- package/bin/lean/prefilter.mjs +96 -14
- package/bin/lean/prefilter.test.mjs +111 -2
- package/bin/lean/preflight.mjs +133 -0
- package/bin/lean/preflight.test.mjs +61 -0
- package/bin/lean/prompts.mjs +37 -3
- package/bin/lean/recovery.test.mjs +271 -7
- package/bin/lean/render.mjs +13 -0
- package/bin/lean/sequence.mjs +102 -0
- package/bin/lean/sequence.test.mjs +112 -0
- package/bin/lean/testmap.mjs +57 -3
- package/bin/lean/testmap.test.mjs +53 -1
- package/bin/lean/verify.mjs +7 -2
- package/bin/lean/verify.test.mjs +8 -2
- package/commands/deploy-pr.md +5 -4
- package/commands/orchestrate.md +7 -4
- package/commands/review-pr-staging.md +6 -1
- package/docs/CONFIG.md +8 -1
- package/docs/OUTCOME-CODES.md +6 -4
- package/docs/RELEASE-NOTES.md +10 -0
- package/package.json +1 -1
- package/scripts/acceptance-red-green.sh +13 -4
- package/scripts/acceptance-red-green.test.sh +14 -0
- package/scripts/code-index.sh +33 -33
- package/scripts/code-index.test.sh +21 -2
- package/scripts/fixtures/test-map/go/.github/workflows/ci.yml +2 -0
- package/scripts/repo-facts.test.sh +5 -5
- package/scripts/review-prepare.sh +28 -6
- package/scripts/review-prepare.test.sh +22 -2
- package/scripts/test-map.sh +186 -34
- package/scripts/test-map.test.sh +250 -7
- package/scripts/verify-phase-trail.test.sh +44 -44
- package/scripts/wait-ci-green.sh +1 -1
- package/scripts/wait-ci-green.test.sh +4 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
import { describe, it } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { tmpDir } from "../tests/_tmp.mjs";
|
|
9
|
+
import { exportNames, enumeratingTests, missingEntries, unjustified } from "./enumerating.mjs";
|
|
10
|
+
import { enumeratingPlanNote, enumeratingBuildRule, missingEntriesPrompt, builderPlanPrompt } from "./prompts.mjs";
|
|
11
|
+
|
|
12
|
+
const sh = (cwd, ...a) => execFileSync("git", a, { cwd, encoding: "utf8", env: { ...process.env, GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "t@t", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "t@t" } });
|
|
13
|
+
const gitIn = (cwd) => async (a) => sh(cwd, ...a);
|
|
14
|
+
const put = (root, files) => { for (const [p, t] of Object.entries(files)) { mkdirSync(dirname(join(root, p)), { recursive: true }); writeFileSync(join(root, p), t); } };
|
|
15
|
+
function repo(files) {
|
|
16
|
+
const d = tmpDir("enum-");
|
|
17
|
+
sh(d, "init", "-q", "-b", "main");
|
|
18
|
+
put(d, files);
|
|
19
|
+
sh(d, "add", "-A"); sh(d, "commit", "-qm", "base");
|
|
20
|
+
return d;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const CORE = "export async function getUser() {}\nexport const listUsers = () => [];\nexport function deleteUser() {}\nexport { helperA as renamedHelper, type Foo } from './x';\n";
|
|
24
|
+
const FILES = {
|
|
25
|
+
"packages/core/package.json": JSON.stringify({ name: "@acme/core" }),
|
|
26
|
+
"packages/core/src/index.ts": CORE,
|
|
27
|
+
"packages/core/test/registry.test.ts": "import * as core from '../src/index';\nconst SERVICE_TESTS = { getUser: 1, listUsers: 1 };\nconst NOT_A_SERVICE = ['deleteUser', 'renamedHelper'];\nexpect(Object.keys(core).filter((k) => !(k in SERVICE_TESTS) && !NOT_A_SERVICE.includes(k))).toEqual([]);\n",
|
|
28
|
+
"packages/web/test/pkg.test.ts": "import * as c from '@acme/core';\nit('all', () => expect(Object.keys(c).length).toBe(4));\n",
|
|
29
|
+
"packages/core/test/unrelated.test.ts": "import { getUser } from '../src/index';\nit('x', () => getUser());\n",
|
|
30
|
+
"locales/en/common.json": "{}", "locales/en/auth.json": "{}", "locales/en/billing.json": "{}",
|
|
31
|
+
"tests/catalog.test.ts": "const NAMESPACES = ['common', 'auth', 'billing'];\n",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe("enumerating tests (#4322)", () => {
|
|
35
|
+
it("reads JS/TS and Python exports", () => {
|
|
36
|
+
assert.deepEqual(exportNames(CORE, "a.ts").sort(), ["deleteUser", "getUser", "listUsers", "renamedHelper"]);
|
|
37
|
+
assert.deepEqual(exportNames("__all__ = ['a', \"bb\"]\ndef c(): pass", "m.py"), ["a", "bb"]);
|
|
38
|
+
assert.deepEqual(exportNames("def run():\n pass\nclass Job: pass\ndef _private(): pass", "m.py"), ["run", "Job"]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("plan: lists every test enumerating the exports of a module the issue names (literal list, Object.keys of a relative or package import)", async () => {
|
|
42
|
+
const d = repo(FILES);
|
|
43
|
+
const list = await enumeratingTests({ git: gitIn(d), cwd: d, paths: ["packages/core/src/index.ts"] });
|
|
44
|
+
const tests = list.filter((e) => e.module === "packages/core/src/index.ts").map((e) => e.test).sort();
|
|
45
|
+
assert.deepEqual(tests, ["packages/core/test/registry.test.ts", "packages/web/test/pkg.test.ts"]);
|
|
46
|
+
assert.ok(!list.some((e) => e.test.endsWith("unrelated.test.ts")), "a test importing one export does not enumerate them");
|
|
47
|
+
assert.match(list.find((e) => e.test.endsWith("registry.test.ts")).why, /literal list of [34] sibling export names/);
|
|
48
|
+
assert.match(list.find((e) => e.test.endsWith("pkg.test.ts")).why, /^Object\.keys\(c\)$/);
|
|
49
|
+
const files = await enumeratingTests({ git: gitIn(d), cwd: d, paths: ["locales/en"] });
|
|
50
|
+
assert.deepEqual(files.map((e) => [e.module, e.test, e.line]), [["locales/en", "tests/catalog.test.ts", 1]]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("plan note and build rule cite the list; no enumerating tests means no new plan step (prompt unchanged)", async () => {
|
|
54
|
+
const d = repo(FILES);
|
|
55
|
+
assert.deepEqual(await enumeratingTests({ git: gitIn(d), cwd: d, paths: ["README.md", "packages/core/package.json"] }), []);
|
|
56
|
+
assert.equal(enumeratingPlanNote([]), ""); assert.equal(enumeratingPlanNote(null), "");
|
|
57
|
+
assert.equal(enumeratingBuildRule([]), "");
|
|
58
|
+
const list = [{ module: "packages/core/src/index.ts", test: "packages/core/test/registry.test.ts", line: 2, why: "literal list of 4 sibling export names" }];
|
|
59
|
+
assert.match(enumeratingPlanNote(list), /Enumerating tests[\s\S]*Affected Files[\s\S]*validate command[\s\S]*- packages\/core\/src\/index\.ts -> packages\/core\/test\/registry\.test\.ts:2/);
|
|
60
|
+
const rule = enumeratingBuildRule(list);
|
|
61
|
+
assert.match(rule, /one test-list edit per new symbol/);
|
|
62
|
+
assert.match(rule, /"not_members"/);
|
|
63
|
+
assert.match(rule, /registry\.test\.ts:2/);
|
|
64
|
+
assert.ok(!builderPlanPrompt(7).includes("Enumerating tests"));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("after implement: a new export missing from an enumerating test is reported; a listed one or a justified not-member is not", async () => {
|
|
68
|
+
const d = repo(FILES);
|
|
69
|
+
sh(d, "checkout", "-qb", "lane");
|
|
70
|
+
put(d, { "packages/core/src/index.ts": `${CORE}export function getProfile() {}\nexport function updateProfile() {}\n` });
|
|
71
|
+
put(d, { "packages/core/test/registry.test.ts": FILES["packages/core/test/registry.test.ts"].replace("listUsers: 1", "listUsers: 1, updateProfile: 1") });
|
|
72
|
+
put(d, { "locales/en/profile.json": "{}" });
|
|
73
|
+
sh(d, "add", "-A"); sh(d, "commit", "-qm", "lane");
|
|
74
|
+
const miss = await missingEntries({ git: gitIn(d), cwd: d, base: "main" });
|
|
75
|
+
const keys = miss.map((m) => `${m.symbol} -> ${m.test}`).sort();
|
|
76
|
+
assert.deepEqual(keys, [
|
|
77
|
+
"getProfile -> packages/core/test/registry.test.ts",
|
|
78
|
+
"getProfile -> packages/web/test/pkg.test.ts",
|
|
79
|
+
"profile -> tests/catalog.test.ts",
|
|
80
|
+
"updateProfile -> packages/web/test/pkg.test.ts",
|
|
81
|
+
]);
|
|
82
|
+
const left = unjustified(miss, [{ symbol: "getProfile", test: "packages/web/test/pkg.test.ts", why: "count test, not a list" }, { symbol: "profile", test: "tests/catalog.test.ts", why: "" }]);
|
|
83
|
+
assert.equal(left.length, 3, "a not-member needs a non-empty why");
|
|
84
|
+
assert.match(missingEntriesPrompt(miss), /getProfile \(packages\/core\/src\/index\.ts\) -> packages\/core\/test\/registry\.test\.ts:\d+[\s\S]*not_members/);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("a lane that adds no enumerable symbol gets nothing; a broken git never throws", async () => {
|
|
88
|
+
const d = repo(FILES);
|
|
89
|
+
sh(d, "checkout", "-qb", "lane");
|
|
90
|
+
put(d, { "packages/core/src/index.ts": `${CORE}// comment only\n` });
|
|
91
|
+
sh(d, "add", "-A"); sh(d, "commit", "-qm", "lane");
|
|
92
|
+
assert.deepEqual(await missingEntries({ git: gitIn(d), cwd: d, base: "main" }), []);
|
|
93
|
+
const bad = async () => { throw new Error("boom"); };
|
|
94
|
+
assert.deepEqual(await missingEntries({ git: bad, cwd: d, base: "main" }), []);
|
|
95
|
+
assert.deepEqual(await enumeratingTests({ git: bad, cwd: d, paths: ["x"] }), []);
|
|
96
|
+
});
|
|
97
|
+
});
|