forgedock 1.27.0 → 1.28.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/README.md +4 -1
- package/bin/bootstrap.mjs +55 -0
- package/bin/engine/outcome-codes.mjs +10 -0
- package/bin/engine/telemetry-seam.mjs +37 -8
- package/bin/engine/telemetry-seam.test.mjs +31 -0
- package/bin/engine-cli.mjs +542 -38
- package/bin/engine.mjs +8 -4
- package/bin/forgedock.mjs +75 -9
- package/bin/gc.mjs +580 -0
- package/bin/lean/admission.mjs +33 -3
- package/bin/lean/batch.mjs +22 -4
- package/bin/lean/batch.test.mjs +5 -3
- package/bin/lean/checks.mjs +199 -0
- package/bin/lean/checks.test.mjs +183 -0
- package/bin/lean/conformance.mjs +190 -58
- package/bin/lean/conformance.test.mjs +116 -7
- package/bin/lean/credentials.mjs +29 -0
- package/bin/lean/depcache.mjs +312 -0
- package/bin/lean/depcache.test.mjs +298 -0
- package/bin/lean/drift.mjs +198 -0
- package/bin/lean/drift.test.mjs +223 -0
- package/bin/lean/effects.mjs +85 -8
- package/bin/lean/execution.e2e.test.mjs +81 -0
- package/bin/lean/execution.mjs +431 -0
- package/bin/lean/execution.test.mjs +583 -0
- package/bin/lean/heavy.mjs +289 -0
- package/bin/lean/heavy.test.mjs +207 -0
- package/bin/lean/lean.mjs +572 -139
- package/bin/lean/lean.test.mjs +1193 -31
- package/bin/lean/ledger.mjs +27 -2
- package/bin/lean/ledger.test.mjs +36 -1
- package/bin/lean/limits.mjs +58 -0
- package/bin/lean/limits.test.mjs +150 -0
- package/bin/lean/orchestrate.mjs +312 -19
- package/bin/lean/prompts.mjs +89 -15
- package/bin/lean/refactor.mjs +276 -0
- package/bin/lean/refactor.test.mjs +359 -0
- package/bin/lean/render.mjs +24 -5
- package/bin/lean/review-trigger.mjs +134 -0
- package/bin/lean/review-trigger.test.mjs +104 -0
- package/bin/lean/sandbox-provider.contract.test.mjs +257 -0
- package/bin/lean/sandbox-remote.mjs +142 -0
- package/bin/lean/sched.mjs +221 -0
- package/bin/lean/sched.test.mjs +310 -0
- package/bin/lean/schedd.mjs +247 -0
- package/bin/lean/schedd.test.mjs +180 -0
- package/bin/lean/toolchain.mjs +228 -0
- package/bin/lean/toolchain.test.mjs +154 -0
- package/bin/lean/verify.mjs +20 -9
- package/bin/lean/verify.test.mjs +26 -3
- package/bin/lean/worktree.mjs +69 -0
- package/bin/lean/worktree.test.mjs +141 -0
- package/bin/runner.mjs +53 -10
- package/bin/stats/github-source.mjs +1 -1
- package/bin/stats/index.mjs +14 -2
- package/bin/stats/query.mjs +72 -0
- package/bin/stats/stats.test.mjs +54 -0
- package/commands/audit.md +2 -0
- package/commands/autopilot.md +2 -0
- package/commands/bootstrap.md +35 -0
- package/commands/cleanup.md +7 -22
- package/commands/forge-stats.md +2 -1
- package/commands/forgedock-init.md +7 -0
- package/commands/issue.md +89 -830
- package/commands/milestone.md +55 -6
- package/commands/orchestrate.md +10 -2
- package/commands/upgrade-deps.md +3 -3
- package/commands/work-on/build/validate.md +4 -0
- package/commands/work-on/close.md +2 -0
- package/commands/work-on/decompose.md +24 -6
- package/docs/CI.md +3 -3
- package/docs/CONFIG.md +177 -2
- package/docs/OUTCOME-CODES.md +8 -0
- package/docs/TELEMETRY.md +1 -1
- package/docs/site/command-reference.md +22 -0
- package/docs/site/getting-started.md +2 -0
- package/docs/site/start-a-new-project.md +327 -0
- package/package.json +1 -1
- package/scripts/affected-files-check.sh +91 -0
- package/scripts/affected-files-check.test.sh +80 -0
- package/scripts/bootstrap-create.mjs +393 -0
- package/scripts/bootstrap-validate.mjs +36 -2
- package/scripts/calibration.mjs +116 -0
- package/scripts/check-devdocs-freshness.sh +9 -4
- package/scripts/check-devdocs-freshness.test.sh +11 -1
- package/scripts/check-spec-markers.sh +3 -0
- package/scripts/check-spec-size.sh +29 -11
- package/scripts/check-spec-size.test.sh +27 -4
- package/scripts/ci-local/lib.mjs +8 -1
- package/scripts/ci-local/parallelism.test.mjs +27 -0
- package/scripts/ci-local/run.mjs +3 -3
- package/scripts/ci-local/scoping.mjs +82 -0
- package/scripts/ci-local.test.sh +1 -1
- package/scripts/fixtures/symbol-index/other/sample.rb +3 -0
- package/scripts/fixtures/symbol-index/py/pkg/__init__.py +1 -0
- package/scripts/fixtures/symbol-index/py/pkg/a.py +5 -0
- package/scripts/fixtures/symbol-index/py/pkg/b.py +14 -0
- package/scripts/fixtures/symbol-index/py/pkg/c.py +1 -0
- package/scripts/fixtures/symbol-index/py/sample.py +41 -0
- package/scripts/fixtures/symbol-index/ts/sample.ts +13 -0
- package/scripts/fixtures/symbol-move/gen_big.py +201 -0
- package/scripts/fixtures/symbol-move-ts/gen_big.mjs +56 -0
- package/scripts/forge-root.test.sh +1 -1
- package/scripts/hotspots.sh +100 -0
- package/scripts/hotspots.test.sh +101 -0
- package/scripts/issue-create.sh +197 -0
- package/scripts/issue-create.test.sh +166 -0
- package/scripts/maintenance-scan.sh +185 -0
- package/scripts/maintenance-scan.test.sh +89 -0
- package/scripts/milestone-drift.sh +206 -0
- package/scripts/milestone-drift.test.sh +120 -0
- package/scripts/refactor-verify.sh +368 -0
- package/scripts/refactor-verify.test.sh +202 -0
- package/scripts/structure-delta.sh +10 -5
- package/scripts/structure-delta.test.sh +3 -2
- package/scripts/symbol-index.mjs +253 -0
- package/scripts/symbol-index.py +354 -0
- package/scripts/symbol-index.test.sh +119 -0
- package/scripts/symbol-move-ts.test.sh +161 -0
- package/scripts/symbol-move.mjs +379 -0
- package/scripts/symbol-move.py +383 -0
- package/scripts/symbol-move.test.sh +168 -0
- package/templates/bootstrap/interview.md +34 -0
- package/templates/bootstrap/issue-body.md +42 -0
- package/templates/bootstrap/needs-human-issue.md +45 -0
- package/templates/bootstrap/stack-profiles.md +34 -0
- package/templates/bootstrap/technical-spike.md +75 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# symbol-index.test.sh — cases for scripts/symbol-index.{py,mjs} (forge#4208).
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
# Optional: SYMBOL_INDEX_TS_NODE_MODULES=<node_modules holding typescript@5> also runs the TS-parser cases.
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PY="$HERE/symbol-index.py"; MJS="$HERE/symbol-index.mjs"; FX="$HERE/fixtures/symbol-index"
|
|
9
|
+
PASS=0; FAILN=0
|
|
10
|
+
TMP="$(mktemp -d "${TMPDIR:-/tmp}/symbol-index-test.XXXXXX")"; trap 'rm -rf "$TMP"' EXIT
|
|
11
|
+
ok() { PASS=$((PASS+1)); }
|
|
12
|
+
bad() { FAILN=$((FAILN+1)); echo "FAIL: $1"; }
|
|
13
|
+
eq() { if [ "$2" = "$3" ]; then ok; else bad "$1 (got: $2 want: $3)"; fi }
|
|
14
|
+
q() { printf '%s' "$OUT" | jq -c "$1"; }
|
|
15
|
+
run() { OUT="$("$@" 2>"$TMP/err")"; RC=$?; }
|
|
16
|
+
|
|
17
|
+
# ---- 1. Python file: schema, symbols, spans, public flags ----
|
|
18
|
+
run python3 "$PY" "$FX/py/sample.py"
|
|
19
|
+
eq "py file rc" "$RC" 0
|
|
20
|
+
eq "py top-level keys" "$(q 'keys')" '["cycles","edges","imports","symbols"]'
|
|
21
|
+
eq "symbol keys" "$(q '[.symbols[]|keys]|unique')" '[["end","kind","lines","name","public","start"]]'
|
|
22
|
+
eq "symbol names" "$(q '[.symbols[].name]')" \
|
|
23
|
+
'["__all__","LIMIT","_cache","_normalise","greet","fetch","helper","Greeter","Greeter.__init__","Greeter.text","Greeter._shout","Greeter.shout"]'
|
|
24
|
+
eq "kinds" "$(q '[.symbols[]|select(.name=="LIMIT" or .name=="greet" or .name=="fetch" or .name=="Greeter" or .name=="Greeter.shout")|.kind]')" \
|
|
25
|
+
'["const","function","async_function","class","method"]'
|
|
26
|
+
eq "greet span" "$(q '.symbols[]|select(.name=="greet")|[.start,.end,.lines]')" '[15,16,2]'
|
|
27
|
+
eq "class span" "$(q '.symbols[]|select(.name=="Greeter")|[.start,.end,.lines]')" '[27,41,15]'
|
|
28
|
+
eq "decorator in span" "$(q '.symbols[]|select(.name=="Greeter.text")|[.start,.end,.lines]')" '[33,35,3]'
|
|
29
|
+
eq "public set" "$(q '[.symbols[]|select(.public)|.name]')" \
|
|
30
|
+
'["LIMIT","greet","fetch","Greeter","Greeter.__init__","Greeter.text","Greeter.shout"]'
|
|
31
|
+
eq "helper not in __all__" "$(q '.symbols[]|select(.name=="helper")|.public')" 'false'
|
|
32
|
+
|
|
33
|
+
# ---- 2. edges and imports ----
|
|
34
|
+
eq "edges" "$(q '[.edges[]|"\(.from)>\(.to)"]')" \
|
|
35
|
+
'["Greeter._shout>Greeter.text","Greeter.shout>Greeter._shout","Greeter.text>greet","fetch>LIMIT","fetch>_cache","greet>_normalise"]'
|
|
36
|
+
eq "imports" "$(q '.imports')" \
|
|
37
|
+
'[{"line":2,"module":"os","names":[]},{"line":3,"module":"collections","names":["OrderedDict"]}]'
|
|
38
|
+
eq "single file has no cycles" "$(q '.cycles')" '[]'
|
|
39
|
+
|
|
40
|
+
# ---- 3. never includes file bodies ----
|
|
41
|
+
for needle in SECRET_DOCSTRING_MARKER SECRET_CLASS_DOC_MARKER 'hi ' 'strip()' 'getcwd' 'return'; do
|
|
42
|
+
case "$OUT" in *"$needle"*) bad "output leaks source text: $needle" ;; *) ok ;; esac
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
# ---- 4. import cycles across modules ----
|
|
46
|
+
run python3 "$PY" "$FX/py/pkg"
|
|
47
|
+
eq "pkg rc" "$RC" 0
|
|
48
|
+
eq "pkg cycles (TYPE_CHECKING + function-local imports ignored)" "$(q '.cycles')" '[["pkg.a","pkg.b"]]'
|
|
49
|
+
eq "pkg relative import resolved" "$(q '[.imports[]|select(.file|endswith("b.py"))|.module]')" '["typing","pkg","pkg.a"]'
|
|
50
|
+
eq "pkg cross-module edges" "$(q '[.edges[]|"\(.from)>\(.to)"]')" '["pkg.a:run>pkg.b:step","pkg.b:step>pkg.c:value"]'
|
|
51
|
+
eq "pkg symbols carry file" "$(q '[.symbols[]|has("file")]|unique')" '[true]'
|
|
52
|
+
BEFORE="$(q '.cycles')"
|
|
53
|
+
cp -R "$FX/py/pkg" "$TMP/pkg"
|
|
54
|
+
printf 'from . import a\n' > "$TMP/pkg/d.py"
|
|
55
|
+
printf 'from . import d\n' >> "$TMP/pkg/c.py"
|
|
56
|
+
run python3 "$PY" "$TMP/pkg"
|
|
57
|
+
AFTER="$(q '.cycles')"
|
|
58
|
+
eq "after: cycle set grows" "$AFTER" '[["pkg.a","pkg.b","pkg.c","pkg.d"]]'
|
|
59
|
+
eq "before/after diff finds the new cycle" \
|
|
60
|
+
"$(jq -nc --argjson b "$BEFORE" --argjson a "$AFTER" '$a - $b')" '[["pkg.a","pkg.b","pkg.c","pkg.d"]]'
|
|
61
|
+
printf 'import pkg.e\n' > "$TMP/pkg/e.py"
|
|
62
|
+
run python3 "$PY" "$TMP/pkg"
|
|
63
|
+
eq "self-import is a cycle" "$(q '.cycles|map(select(.==["pkg.e"]))|length')" '1'
|
|
64
|
+
|
|
65
|
+
# ---- 5. deterministic ----
|
|
66
|
+
A1="$(python3 "$PY" "$FX/py/pkg")"; A2="$(python3 "$PY" "$FX/py/pkg")"
|
|
67
|
+
eq "byte-identical reruns" "$([ "$A1" = "$A2" ] && echo same)" same
|
|
68
|
+
|
|
69
|
+
# ---- 6. unsupported: exit 0, only {unsupported, reason} ----
|
|
70
|
+
for t in "$FX/other/sample.rb" "$FX/ts/sample.ts"; do
|
|
71
|
+
run python3 "$PY" "$t"
|
|
72
|
+
eq "py unsupported rc $(basename "$t")" "$RC" 0
|
|
73
|
+
eq "py unsupported keys $(basename "$t")" "$(q 'keys')" '["reason","unsupported"]'
|
|
74
|
+
done
|
|
75
|
+
eq "py unsupported lang" "$(python3 "$PY" "$FX/other/sample.rb" | jq -r .unsupported)" ruby
|
|
76
|
+
run python3 "$PY" "$FX/py/sample.py" "$FX/other/sample.rb"
|
|
77
|
+
eq "mixed input: no partial output" "$(q 'keys')" '["reason","unsupported"]'
|
|
78
|
+
mkdir -p "$TMP/empty"; run python3 "$PY" "$TMP/empty"
|
|
79
|
+
eq "dir without .py" "$(q '.')" '{"reason":"no-python-files","unsupported":"unknown"}'
|
|
80
|
+
mkdir -p "$TMP/broken"; printf 'def ok():\n return 1\n' > "$TMP/broken/good.py"; printf 'def broken(:\n' > "$TMP/broken/bad.py"
|
|
81
|
+
run python3 "$PY" "$TMP/broken"
|
|
82
|
+
eq "parse error rc" "$RC" 0
|
|
83
|
+
eq "parse error: no partial symbols" "$(q 'keys')" '["reason","unsupported"]'
|
|
84
|
+
eq "parse error reason" "$(q '.reason|startswith("parse-error: ")')" 'true'
|
|
85
|
+
|
|
86
|
+
run node "$MJS" "$FX/other/sample.rb"
|
|
87
|
+
eq "mjs ruby rc" "$RC" 0
|
|
88
|
+
eq "mjs ruby" "$(q '.')" '{"reason":"unknown-language","unsupported":"ruby"}'
|
|
89
|
+
mkdir -p "$TMP/ts"; cp "$FX/ts/sample.ts" "$TMP/ts/"
|
|
90
|
+
run node "$MJS" "$TMP/ts/sample.ts"
|
|
91
|
+
eq "mjs ts without parser rc" "$RC" 0
|
|
92
|
+
eq "mjs ts without parser" "$(q '.')" '{"reason":"no-parser","unsupported":"ts"}'
|
|
93
|
+
run node "$MJS" "$FX/py/sample.py"
|
|
94
|
+
eq "mjs delegates .py" "$(q '.symbols|length')" '12'
|
|
95
|
+
|
|
96
|
+
# ---- 7. usage ----
|
|
97
|
+
python3 "$PY" >/dev/null 2>&1; eq "py no args" "$?" 2
|
|
98
|
+
python3 "$PY" "$TMP/missing.py" >/dev/null 2>&1; eq "py missing path" "$?" 2
|
|
99
|
+
node "$MJS" >/dev/null 2>&1; eq "mjs no args" "$?" 2
|
|
100
|
+
|
|
101
|
+
# ---- 8. optional: TS parser path ----
|
|
102
|
+
if [ -n "${SYMBOL_INDEX_TS_NODE_MODULES:-}" ] && [ -d "$SYMBOL_INDEX_TS_NODE_MODULES/typescript" ]; then
|
|
103
|
+
ln -s "$SYMBOL_INDEX_TS_NODE_MODULES" "$TMP/ts/node_modules"
|
|
104
|
+
run node "$MJS" "$TMP/ts/sample.ts"
|
|
105
|
+
eq "ts symbols" "$(q '[.symbols[]|"\(.kind):\(.name):\(.start)-\(.end):\(.public)"]')" \
|
|
106
|
+
'["const:LIMIT:3-3:true","function:load:5-7:true","class:Loader:9-13:true","method:Loader.read:10-12:true"]'
|
|
107
|
+
eq "ts edges" "$(q '[.edges[]|"\(.from)>\(.to)"]')" '["Loader.read>load","load>LIMIT"]'
|
|
108
|
+
mkdir -p "$TMP/ts/lib"
|
|
109
|
+
printf 'import { b } from "./b";\nimport type { T } from "./c";\nexport function a(): number { return b(); }\n' > "$TMP/ts/lib/a.ts"
|
|
110
|
+
printf 'import * as A from "./a.js";\nexport const b = () => 2;\nexport function useA() { return A.a(); }\n' > "$TMP/ts/lib/b.ts"
|
|
111
|
+
printf 'import { a } from "./a";\nexport type T = number;\n' > "$TMP/ts/lib/c.ts"
|
|
112
|
+
run node "$MJS" "$TMP/ts/lib"
|
|
113
|
+
eq "ts cycles (import type ignored)" "$(q '.cycles')" '[["a","b"]]'
|
|
114
|
+
else
|
|
115
|
+
echo "symbol-index.test.sh: TS-parser cases skipped (set SYMBOL_INDEX_TS_NODE_MODULES)"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
echo "symbol-index.test.sh: $PASS passed, $FAILN failed"
|
|
119
|
+
[ "$FAILN" -eq 0 ]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# symbol-move-ts.test.sh — cases for scripts/symbol-move.mjs, the ts-morph mover (forge#4212).
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
# Builds a throwaway git repo from scripts/fixtures/symbol-move-ts/gen_big.mjs (~700-line src/big.ts + node:test
|
|
6
|
+
# suite), moves four symbol groups into src/big_parts/ and proves the move with scripts/refactor-verify.sh; then
|
|
7
|
+
# checks the refusals, the --extend chain step and the no-ts-morph refusal (no partial edits).
|
|
8
|
+
# ts-morph: SYMBOL_MOVE_TS_NODE_MODULES=<node_modules holding ts-morph> (put on NODE_PATH for the mover, the symbol
|
|
9
|
+
# index and refactor-verify). Without it the ts-morph cases are skipped; SYMBOL_MOVE_REQUIRE_TS_MORPH=1 turns that
|
|
10
|
+
# skip into a failure.
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
SM="$HERE/symbol-move.mjs"; RV="$HERE/refactor-verify.sh"; GEN="$HERE/fixtures/symbol-move-ts/gen_big.mjs"
|
|
14
|
+
PASS=0; FAILN=0
|
|
15
|
+
TMP="$(mktemp -d "${TMPDIR:-/tmp}/symbol-move-ts-test.XXXXXX")"; trap 'rm -rf "$TMP"' EXIT
|
|
16
|
+
ok() { PASS=$((PASS+1)); }
|
|
17
|
+
bad() { FAILN=$((FAILN+1)); echo "FAIL: $1"; }
|
|
18
|
+
eq() { if [ "$2" = "$3" ]; then ok; else bad "$1 (got: $2 want: $3)"; fi }
|
|
19
|
+
has() { if grep -qF -- "$2" "$3"; then ok; else bad "$1 (no '$2' in $3)"; fi }
|
|
20
|
+
q() { printf '%s' "$OUT" | jq -c "$1"; }
|
|
21
|
+
TSM="${SYMBOL_MOVE_TS_NODE_MODULES:-}"
|
|
22
|
+
[ -n "$TSM" ] && [ -d "$TSM/ts-morph" ] || TSM=""
|
|
23
|
+
# without: no NODE_PATH and a HOME holding no node_modules, so ts-morph cannot resolve from anywhere
|
|
24
|
+
without() { OUT="$(cd "$R" && env -u NODE_PATH HOME="$TMP/home" node "$SM" "$@" 2>"$TMP/err")"; RC=$?; }
|
|
25
|
+
mover() { OUT="$(cd "$R" && NODE_PATH="$TSM" node "$SM" "$@" 2>"$TMP/err")"; RC=$?; }
|
|
26
|
+
mkdir -p "$TMP/home"
|
|
27
|
+
|
|
28
|
+
R="$TMP/repo"; B="$TMP/baseline.json"; P="$TMP/plan.json"
|
|
29
|
+
TEST_CMD='node --test test/big.test.ts'
|
|
30
|
+
g() { git -C "$R" -c user.name=t -c user.email=t@example.invalid "$@"; }
|
|
31
|
+
mkdir -p "$R"; git init -q "$R"
|
|
32
|
+
node "$GEN" "$R" || bad "generator failed"
|
|
33
|
+
g add -A && g commit -qm base && g tag base
|
|
34
|
+
eq "fixture size >= 600 lines" "$([ "$(wc -l < "$R/src/big.ts")" -ge 600 ] && echo yes || echo no)" yes
|
|
35
|
+
SHA0="$(git -C "$R" hash-object src/big.ts)"
|
|
36
|
+
reset() { g reset -q --hard base; g clean -fdq; }
|
|
37
|
+
|
|
38
|
+
seqp() { local f="$1" s=""; for i in $(seq -w 0 19); do s="$s\"$(printf "$f" "$i")\","; done; printf '%s' "${s%,}"; }
|
|
39
|
+
plan() { printf '%s' "$1" | jq -c . > "$2" || bad "bad plan json"; }
|
|
40
|
+
CONSTS="[$(seqp 'LIMIT_%s')]"
|
|
41
|
+
HELPERS="[$(for i in $(seq -w 0 19); do printf '"_scale_%s","compute_%s",' "$i" "$i"; done | sed 's/,$//')]"
|
|
42
|
+
MODELS="[$(seqp 'Item_%s'),$(seqp 'Store_%s')]"
|
|
43
|
+
AIO="[$(seqp 'fetch_%s')]"
|
|
44
|
+
FULL="{\"consts\":$CONSTS,\"helpers\":$HELPERS,\"models\":$MODELS,\"aio\":$AIO}"
|
|
45
|
+
plan "$FULL" "$P"
|
|
46
|
+
|
|
47
|
+
# refused <label>: rc 3, status refused, a reason, and nothing changed in the repo
|
|
48
|
+
refused() {
|
|
49
|
+
eq "$1: rc" "$RC" 3
|
|
50
|
+
eq "$1: status" "$(q '.status')" '"refused"'
|
|
51
|
+
eq "$1: has reason" "$(q '.reason|length>0')" true
|
|
52
|
+
eq "$1: tree untouched" "$(git -C "$R" status --porcelain)" ""
|
|
53
|
+
eq "$1: barrel untouched" "$(git -C "$R" hash-object src/big.ts)" "$SHA0"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# ---- 1. no codemod tool: ts-morph unresolvable -> clean refusal before anything is read or written ----
|
|
57
|
+
without --src src/big.ts --plan "$P"
|
|
58
|
+
refused "no ts-morph"
|
|
59
|
+
eq "no ts-morph: reason" "$(q '.reason|startswith("no codemod tool for ts: ts-morph is not resolvable")')" true
|
|
60
|
+
without --probe --src src/big.ts
|
|
61
|
+
eq "no ts-morph probe: rc" "$RC" 3
|
|
62
|
+
eq "no ts-morph probe: reason" "$(q '.reason|startswith("no codemod tool for ts:")')" true
|
|
63
|
+
eq "no ts-morph: no package dir" "$([ -e "$R/src/big_parts" ] && echo yes || echo no)" no
|
|
64
|
+
|
|
65
|
+
# ---- 2. usage ----
|
|
66
|
+
OUT="$(node "$SM" --plan "$P" 2>/dev/null)"; eq "usage: rc" "$?" 2
|
|
67
|
+
OUT="$(node "$SM" --src "$R/src/nope.ts" --plan "$P" 2>/dev/null)"; eq "missing src: rc" "$?" 2
|
|
68
|
+
|
|
69
|
+
if [ -z "$TSM" ]; then
|
|
70
|
+
if [ "${SYMBOL_MOVE_REQUIRE_TS_MORPH:-0}" = "1" ]; then bad "ts-morph required (SYMBOL_MOVE_REQUIRE_TS_MORPH=1) but SYMBOL_MOVE_TS_NODE_MODULES has no ts-morph"; fi
|
|
71
|
+
echo "symbol-move-ts.test.sh: ts-morph cases skipped (set SYMBOL_MOVE_TS_NODE_MODULES)"
|
|
72
|
+
echo "symbol-move-ts.test.sh: $PASS passed, $FAILN failed"
|
|
73
|
+
[ "$FAILN" -eq 0 ]; exit
|
|
74
|
+
fi
|
|
75
|
+
export NODE_PATH="$TSM"
|
|
76
|
+
|
|
77
|
+
# ---- 3. probe and dry run ----
|
|
78
|
+
mover --probe --src src/big.ts
|
|
79
|
+
eq "probe: rc" "$RC" 0
|
|
80
|
+
eq "probe: tool" "$(q '.tool')" '"ts-morph"'
|
|
81
|
+
mover --src src/big.ts --plan "$P" --dry-run
|
|
82
|
+
eq "dry-run: rc" "$RC" 0
|
|
83
|
+
eq "dry-run: tree untouched" "$(git -C "$R" status --porcelain)" ""
|
|
84
|
+
|
|
85
|
+
# ---- 4. the move: four groups into src/big_parts/, barrel at the old path, refactor-verify ok ----
|
|
86
|
+
bash "$RV" baseline --root "$R" --target src/big.ts --test-cmd "$TEST_CMD" --out "$B" >/dev/null 2>"$TMP/err" || { cat "$TMP/err"; bad "baseline failed"; }
|
|
87
|
+
eq "baseline counts" "$(jq -c .test "$B")" '{"exit":0,"passed":5,"skipped":1}'
|
|
88
|
+
eq "baseline flags erased types" "$(jq -c '[.symbols[]|select(.type)]|length' "$B")" 20
|
|
89
|
+
mover --src src/big.ts --plan "$P"
|
|
90
|
+
eq "move: rc" "$RC" 0
|
|
91
|
+
eq "move: status" "$(q '.status')" '"ok"'
|
|
92
|
+
eq "move: package" "$(q '.package')" '"src/big_parts"'
|
|
93
|
+
eq "move: modules" "$(q '.modules|keys')" '["aio","consts","helpers","models"]'
|
|
94
|
+
for m in consts helpers models aio; do
|
|
95
|
+
eq "module $m exists" "$([ -f "$R/src/big_parts/$m.ts" ] && echo yes || echo no)" yes
|
|
96
|
+
has "barrel re-exports $m" "} from \"./big_parts/$m.ts\";" "$R/src/big.ts"
|
|
97
|
+
done
|
|
98
|
+
has "barrel re-exports types with type" 'export { type Item_00, Store_00,' "$R/src/big.ts"
|
|
99
|
+
has "barrel imports names kept code uses" 'import { compute_00 } from "./big_parts/helpers.ts";' "$R/src/big.ts"
|
|
100
|
+
has "barrel keeps unmoved code" 'function _shared(x: number): number {' "$R/src/big.ts"
|
|
101
|
+
has "barrel keeps the header" '// SPDX-License-Identifier: MIT' "$R/src/big.ts"
|
|
102
|
+
has "relative import re-based" 'import { clamp } from "../util.ts";' "$R/src/big_parts/helpers.ts"
|
|
103
|
+
has "type import pruned and re-based" 'import { type Unit } from "../util.ts";' "$R/src/big_parts/models.ts"
|
|
104
|
+
has "sibling import" 'import { compute_00, compute_01,' "$R/src/big_parts/models.ts"
|
|
105
|
+
has "private helper moved verbatim" 'function _scale_07(x: number): number {' "$R/src/big_parts/helpers.ts"
|
|
106
|
+
has "doc comment moved" '/** compute 7: scaled and clamped. */' "$R/src/big_parts/helpers.ts"
|
|
107
|
+
eq "no moved declaration left" "$(grep -c '^export function compute_\|^function _scale_\|^export class Store_\|^export interface Item_' "$R/src/big.ts")" 0
|
|
108
|
+
g add -A && g commit -qm move
|
|
109
|
+
OUT="$(bash "$RV" verify --baseline "$B" --base base 2>"$TMP/err")"; RC=$?
|
|
110
|
+
eq "verify: rc" "$RC" 0
|
|
111
|
+
eq "verify: ok" "$(q '.ok')" true
|
|
112
|
+
# a dropped type re-export is caught statically
|
|
113
|
+
sed -i.bak 's/export { type Item_00, Store_00, /export { Store_00, /' "$R/src/big.ts"; rm -f "$R/src/big.ts.bak"
|
|
114
|
+
g commit -qam drop-type
|
|
115
|
+
OUT="$(bash "$RV" verify --baseline "$B" --base base 2>"$TMP/err")"; RC=$?
|
|
116
|
+
eq "dropped type: rc" "$RC" 1
|
|
117
|
+
eq "dropped type: missing" "$(q '[.failures[]|select(.kind=="missing-symbol")|.name]')" '["Item_00"]'
|
|
118
|
+
reset
|
|
119
|
+
|
|
120
|
+
# ---- 5. refusals: nothing touched ----
|
|
121
|
+
plan '{"a":["nope"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "unknown symbol"
|
|
122
|
+
plan '{"a":["LIMIT_00"],"b":["LIMIT_00"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "listed twice"
|
|
123
|
+
plan '{"a":["Store_00.add"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "method"
|
|
124
|
+
plan '{"index":["LIMIT_00"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "bad module name"
|
|
125
|
+
plan '{"a":["compute_00"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "needs a kept name"
|
|
126
|
+
eq "needs a kept name: reason" "$(q '.reason|test("needs \"_scale_00\", which stays")')" true
|
|
127
|
+
plan '{"a":["_shared"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "private used by kept code"
|
|
128
|
+
eq "private used by kept code: reason" "$(q '.reason|test("not exported but kept code uses it")')" true
|
|
129
|
+
plan '{"a":["_scale_00","LIMIT_00"],"b":["compute_00"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "private used by a sibling"
|
|
130
|
+
eq "private used by a sibling: reason" "$(q '.reason|test("not exported")')" true
|
|
131
|
+
plan '{"a":["ping"],"b":["pong"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "cycle"
|
|
132
|
+
eq "cycle: reason" "$(q '.reason|test("import cycle")')" true
|
|
133
|
+
mkdir -p "$R/src/big_parts"; plan "{\"consts\":$CONSTS}" "$P"; mover --src src/big.ts --plan "$P"
|
|
134
|
+
eq "existing package: rc" "$RC" 3
|
|
135
|
+
eq "existing package: reason" "$(q '.reason|test("already exists")')" true
|
|
136
|
+
rmdir "$R/src/big_parts"
|
|
137
|
+
printf 'export default function main(): number {\n return 1;\n}\n' >> "$R/src/big.ts"; g commit -qam default; SHA0="$(git -C "$R" hash-object src/big.ts)"
|
|
138
|
+
plan '{"a":["main"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "export default"
|
|
139
|
+
printf 'export const HERE = import.meta.url;\n' >> "$R/src/big.ts"; g commit -qam meta; SHA0="$(git -C "$R" hash-object src/big.ts)"
|
|
140
|
+
plan '{"a":["HERE"]}' "$P"; mover --src src/big.ts --plan "$P"; refused "import.meta"
|
|
141
|
+
reset; SHA0="$(git -C "$R" hash-object src/big.ts)"
|
|
142
|
+
|
|
143
|
+
# ---- 6. chain: a second step adds modules to the existing package with --extend ----
|
|
144
|
+
plan "{\"consts\":$CONSTS,\"helpers\":$HELPERS}" "$P"; mover --src src/big.ts --plan "$P"
|
|
145
|
+
eq "chain step 1: rc" "$RC" 0
|
|
146
|
+
g add -A && g commit -qm step1
|
|
147
|
+
plan "{\"models\":$MODELS}" "$P"; mover --src src/big.ts --plan "$P" --package big_parts
|
|
148
|
+
eq "chain without --extend: rc" "$RC" 3
|
|
149
|
+
plan "{\"models\":$MODELS}" "$P"; mover --src src/big.ts --plan "$P" --package big_parts --extend
|
|
150
|
+
eq "chain step 2: rc" "$RC" 0
|
|
151
|
+
has "chain step 2: barrel" '} from "./big_parts/models.ts";' "$R/src/big.ts"
|
|
152
|
+
has "chain step 2: earlier module imported in-package" '} from "./helpers.ts";' "$R/src/big_parts/models.ts"
|
|
153
|
+
g add -A && g commit -qm step2
|
|
154
|
+
OUT="$(bash "$RV" verify --baseline "$B" --base base 2>"$TMP/err")"; RC=$?
|
|
155
|
+
eq "chain verify: ok" "$(q '.ok')" true
|
|
156
|
+
plan "{\"models\":[\"fetch_00\"]}" "$P"; mover --src src/big.ts --plan "$P" --package big_parts --extend
|
|
157
|
+
eq "chain existing module: rc" "$RC" 3
|
|
158
|
+
eq "chain existing module: reason" "$(q '.reason|test("module already exists")')" true
|
|
159
|
+
|
|
160
|
+
echo "symbol-move-ts.test.sh: $PASS passed, $FAILN failed"
|
|
161
|
+
[ "$FAILN" -eq 0 ]
|