Commit 26f10a3
test: split unit tests into node:test suite, add c8 coverage (#271)
* test: split unit tests into node:test suite and add c8 coverage
Three tests under test/test-XX-*/ were already in-process assertions
(sea-picker, common, config-parse) — they required('../../lib-es5/...')
without ever spawning pkg or producing a binary. Keeping them in the
e2e harness paid the per-directory spawn cost for trivial checks and
mixed "lib regression" with "build regression" under one runner.
Migrate them to test/unit/*.test.ts on Node's built-in node:test runner,
loaded via esbuild-register so the lib/ TS source is imported directly —
no yarn build required. 96 assertions run in ~1.3s.
Coverage is wired through c8 (thin reporter over NODE_V8_COVERAGE).
utils.pkg.sync already spreads process.env into the spawned pkg CLI,
so coverage:e2e captures subprocess coverage with zero code changes.
A second c8 run with --clean=false merges unit + e2e into one report.
Baseline from yarn coverage:unit: lib/ 25.49% statements / 85.78%
branch. Highest per-file: config.ts 66.73%, common.ts 57.60%, sea.ts
25.86%.
CI: yarn test:unit rides the existing build matrix (3 OS × Node 22/24),
giving common.test.ts's win32 branch and Node 24 coverage at ~1s/cell.
Closes #267.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* test(unit): extend coverage — compress_type, detector, esm-transformer, common, config
Add three new test files and extend the two existing ones. Focus is on the
pure-pure modules — AST helpers, path predicates, enum gates, esbuild-based
transformation — without touching the integration-heavy ones (walker,
producer, packer, fabricator, resolver, sea-assets) that inherently need
fixtures pkg already stresses via the e2e suite.
New: test/unit/compress-type.test.ts
CompressType enum layout (bytecode-format stability guard) and
getZstdCompressSync/Stream availability branches.
New: test/unit/detector.test.ts
parse() tolerant flags (decorator-legacy, top-level return,
import.meta), detect() warn-don't-throw + descent control, and every
public visitor (successful / nonLiteral / malformed / useSCWD) with
literal, dynamic-expr, and must/may-exclude hints.
New: test/unit/esm-transformer.test.ts
rewriteMjsRequirePaths regex across quote styles + relative/bare
specifiers; transformESMtoCJS for top-level-await wrapping (with and
without exports), import.meta shim injection, and esbuild-failure
handling.
Extended: test/unit/common.test.ts
isPackageJson / isDot{JS,JSON,NODE} / unlikelyJavascript /
replaceSlashes / isRootPath / isESMPackage / isESMFile (latter two
use an mkdtemp fixture — still ~10ms, no subprocess).
Extended: test/unit/config-parse.test.ts
isConfiguration, stringifyTarget, PKGRC_FILENAMES precedence, and
findPkgrc against an mkdtemp fixture.
Coverage baseline (yarn coverage:unit):
lib/ overall: 25.49% → 35.62% stmts / 79.78% branch
common.ts: 57.60% → 79.20%
detector.ts: 13.64% → 80.53%
esm-transformer: 20.04% → 74.88%
compress_type.ts: 59.09% → 77.27%
Totals: 162 pass / 0 fail in ~1.7s (up from 96 in ~1.3s).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* test(unit): cover target parser, TLA-with-imports, detector + realpath edges
Hot path: parseTargets is called on every pkg invocation. Previously
untestable because it was internal — exported alongside differentParts
and stringifyTargetForOutput with a test-only comment, matching the
pattern already used for pickMatchingHostTargetIndex.
config.ts target parser:
'host' short-circuit, single-token host-fill (platform/arch/
nodeRange), full triple in any token order, repeated-dash empty
tokens, fancy-alias ('windows' → 'win'), unknown-token error with
offending spec in message, list-order preservation, round-trip
with stringifyTarget, differentParts on every dimension combo,
stringifyTargetForOutput with each subset of varying axes.
esm-transformer.ts closes the remaining success branch:
Top-level await + imports but no exports — imports are kept at
top level so esbuild can rewrite them, body goes into the async
IIFE. Also: for-await-of at top level triggers the same wrap;
await inside a function does NOT trigger it.
detector.ts edges:
visitorMalformed on require(dynamicExpr); visitorUseSCWD with
multi-arg reconstruction, zero args (empty alias), 'url.resolve'
(non-path object), bare resolve() call.
common.ts toNormalizedRealPath:
missing-path pass-through, existing-dir realpath, symlink → target
(symlink subtest skipped on Windows — requires admin/Dev Mode).
Coverage baseline: 35.62% → 37.42% stmts / 82.08% branch
esm-transformer.ts: 74.88% → 87.09% (TLA branch closed)
config.ts: 67.69% → 73.51% (target parser exercised)
common.ts: 79.20% → 81.33% (realpath covered)
detector.ts: 80.53% → 81.69%
Totals: 193 pass / 0 fail in ~1.8s (up from 162 in ~1.7s).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* test(unit): address Copilot review — drop unused infoed, clarify coverage docs
- test/unit/config-parse.test.ts: the `infoed` capture buffer was never
asserted. Dropped it and silenced `log.info` directly so
resolveTargetList/parseTargets fallback paths don't print during tests.
- docs-site/development.md: `yarn coverage:e2e` uses `--clean=false` and
therefore accumulates on top of whatever is in `coverage/tmp` — it is
not "e2e-only" in general. Docs now state the actual behavior: fresh →
e2e-only, after `yarn coverage:unit` → merged.
No script change — the `--clean=false` design is intentional per #267
(unit + e2e accumulate via a single temp dir).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* ci(codecov): upload unit-suite lcov on every PR + README badge
Per-PR coverage signal via Codecov. Unit suite only — `yarn coverage` for
the full merged (unit + e2e) report stays a follow-up job (30+ min is too
expensive per PR; issue #267 plans a nightly or on-label job for that).
- Adds `yarn coverage:unit` step to build_artifact (Linux, Node 22) after
lint/build, then uploads coverage/lcov.info via codecov-action@v5.
build_artifact already runs on every PR and produces the distributable
bundle, so coverage fits naturally without adding a separate job.
- codecov.yml: informational status (never fail a PR on coverage swings,
since e2e coverage isn't uploaded yet), ignore compiled output / tests
/ docs-site / dictionary, carryforward disabled on the `unit` flag.
- README: Codecov badge next to CI badge, labeled "Coverage (unit)" so
the scope is obvious at a glance.
Requires: Codecov GitHub App installed on yao-pkg and CODECOV_TOKEN set
as a repo secret (optional on public repos but recommended for PR uploads).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* ci(codecov): add slug + step name, keep unit-flag upload
Pins uploads to the yao-pkg/pkg Codecov project (prevents mis-routing on
forks) and adds a readable step label. Path stays coverage/lcov.info
(what .c8rc.json emits); flags: unit tags this as the unit-suite upload
so a future e2e upload can use a separate flag.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* ci(codecov): add nightly e2e-coverage job, drop "(unit)" from badge alt
Standing up the missing half of #267's Phase 2 acceptance criteria:
"CI runs the unit suite on every PR (cheap) and the full coverage job
on-label or nightly (expensive — full e2e)."
- .github/workflows/coverage-nightly.yml: daily 03:00 UTC job (also
workflow_dispatch) that runs yarn build + yarn coverage:e2e on Node 22
/ Linux and uploads coverage/lcov.info to Codecov with `flags: e2e`.
Reuses the same ~/.pkg-cache key as test.yml so it doesn't refetch all
pkg-fetch binaries each night.
- README: badge alt text is now "Coverage" — once the first nightly
lands, Codecov merges the `unit` (from PR / main pushes) and `e2e`
flag uploads per commit, so main-branch reports reflect the combined
picture. PRs still show unit-only signal via flags.
Codecov's flag-based merge means this doesn't touch the per-PR upload
path — the existing build_artifact step keeps uploading unit with the
fast ~2s feedback, and e2e drifts in once per day.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* ci(codecov): carryforward both flags so PRs report merged unit+e2e totals
Without carryforward, Codecov treats each commit independently. PR commits
only have a `unit` upload (~37% of lib/) because the nightly e2e job runs
on main, not on PR branches — so PR reports and status were showing
unit-only numbers and the user perceived "no merge".
carryforward: true tells Codecov to reuse the most recent upload tagged
with that flag when the current commit doesn't have its own. A PR now sees:
- unit flag: direct from this PR's build_artifact run
- e2e flag: carried forward from the last nightly on main
- Total: union of the two → the merged number we want
Trade-off: carried-forward e2e data is slightly stale for PR-new code,
but e2e covers integration-heavy modules (walker/producer/packer/fabricator)
that PRs rarely touch at the test-contributing level. Close enough to truth
to be useful, and the nightly refresh catches drift within 24 hours.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* docs: refresh stale "follow-up" comments — nightly e2e already landed
ci.yml and codecov.yml were written when the e2e coverage upload was
still a TODO. That job now ships in coverage-nightly.yml and the
flag-merge via carryforward is already live. Update comments to
describe the current state instead of a past plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* ci(codecov): collect e2e coverage from existing Ubuntu cells, drop nightly
We already run the full e2e suite on every PR — 18 cells across
test_22/test_24/test_host × 3 OS × Node 22/24. Instead of re-running it
nightly under c8, wrap two canonical existing cells (ubuntu × matching
host/target) so they double as coverage producers:
test_22 / (22.x, ubuntu-latest) → yarn coverage:e2e → upload `flags: e2e`
test_24 / (24.x, ubuntu-latest) → yarn coverage:e2e:24 → upload `flags: e2e`
Codecov merges same-flag uploads per commit (max per line), so both Node
versions contribute. Windows/macOS cells stay unchanged — they exercise
the same lib/ paths, so piping them through c8 would just duplicate data.
Benefits over the nightly approach:
- e2e coverage is per-PR real-time, no 24h lag.
- Merged unit+e2e shows on every PR natively (both flags on same commit),
no reliance on carryforward to fake the merge.
- No separate workflow to maintain.
Overhead: c8 adds ~10-20% to the wrapped cells' runtime (e.g. 4m → 5m).
carryforward stays enabled as a safety net for transient upload failures,
but it's no longer load-bearing for PR reports.
Changes:
- package.json: add coverage:e2e:24 (Node 24 target variant).
- test.yml: canonical-cell detection via a computed step output; run
yarn coverage:e2e[:24] on canonical cells, yarn <npm_command> elsewhere,
then upload with flags: e2e from canonical only.
- codecov.yml: refreshed comments; both flags keep carryforward as a
safety net rather than the primary merge mechanism.
- Delete .github/workflows/coverage-nightly.yml — redundant.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* ci(codecov): forward secrets to test.yml, fix c8 remap config
Two bugs in the prior run:
1. CODECOV_TOKEN was empty in test.yml — reusable workflows don't inherit
secrets automatically. Codecov rejected the e2e upload with
"Token required because branch is protected". Adding `secrets: inherit`
to the three test_* callers in ci.yml forwards all repo/org secrets.
2. c8 produced an empty coverage table despite seeing 479 V8 dumps. Root
cause: .c8rc.json had "lib-es5/**" in exclude, which (depending on the
c8 version's default for `exclude-after-remap`) can drop V8 dumps for
the executed compiled files BEFORE sourcemap-remapping them back to
lib/*.ts source. Result: everything got filtered, 0 files reported.
Fix: drop "lib-es5/**" from exclude (the `include: ["lib/**"]` already
restricts output to TS source after remap, so this was redundant and
actively harmful) and set `exclude-after-remap: true` explicitly so
we don't depend on the default across c8 versions.
Unit coverage unaffected (still 37.42% locally) — remove+explicit-setting
is a pure no-op for the unit path because unit tests execute lib/*.ts
directly via esbuild-register; lib-es5 was never touched there.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* test(detector): clarify descent-stop test, fix CI comment ref
Copilot review feedback:
- detector.test.ts: visitor returns false on the root File node, not
Program. Update wording, add a second test that descends one level
(returns false at Program) so both branches are documented.
- ci.yml: stale comment referenced coverage-nightly.yml; e2e coverage now
comes from the canonical Ubuntu cells in test.yml.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>1 parent 376e2a4 commit 26f10a3
25 files changed
Lines changed: 2234 additions & 848 deletions
File tree
- .claude/rules
- .github
- workflows
- docs-site
- lib
- test
- test-00-sea-picker
- test-48-common
- test-50-config-parse
- unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
16 | 22 | | |
17 | 23 | | |
18 | 24 | | |
19 | 25 | | |
20 | | - | |
21 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
22 | 33 | | |
23 | 34 | | |
24 | 35 | | |
25 | 36 | | |
26 | 37 | | |
27 | | - | |
28 | | - | |
29 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
30 | 51 | | |
31 | 52 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
107 | | - | |
| 107 | + | |
108 | 108 | | |
109 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
110 | 114 | | |
111 | 115 | | |
112 | 116 | | |
| |||
117 | 121 | | |
118 | 122 | | |
119 | 123 | | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
120 | 129 | | |
121 | 130 | | |
122 | 131 | | |
123 | 132 | | |
124 | | - | |
125 | | - | |
126 | | - | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
127 | 136 | | |
128 | 137 | | |
129 | 138 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
60 | 78 | | |
61 | 79 | | |
62 | 80 | | |
| |||
81 | 99 | | |
82 | 100 | | |
83 | 101 | | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
84 | 108 | | |
85 | 109 | | |
86 | 110 | | |
87 | 111 | | |
| 112 | + | |
88 | 113 | | |
89 | 114 | | |
90 | 115 | | |
91 | 116 | | |
92 | 117 | | |
93 | 118 | | |
94 | 119 | | |
| 120 | + | |
95 | 121 | | |
96 | 122 | | |
97 | 123 | | |
98 | 124 | | |
99 | 125 | | |
100 | 126 | | |
101 | 127 | | |
| 128 | + | |
102 | 129 | | |
103 | 130 | | |
104 | 131 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
51 | 70 | | |
52 | 71 | | |
53 | 72 | | |
54 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
7 | 10 | | |
8 | 11 | | |
9 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
27 | 41 | | |
28 | 42 | | |
29 | 43 | | |
| |||
42 | 56 | | |
43 | 57 | | |
44 | 58 | | |
45 | | - | |
| 59 | + | |
46 | 60 | | |
47 | 61 | | |
48 | 62 | | |
49 | | - | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
50 | 82 | | |
51 | 83 | | |
52 | 84 | | |
| |||
0 commit comments