|
| 1 | +# 15. Release Tools Test Coverage |
| 2 | +**Status: open** |
| 3 | + |
| 4 | +**Priority:** P1 |
| 5 | +**Effort:** S |
| 6 | +**Version impact:** none |
| 7 | +**Depends on:** 03 |
| 8 | +**Touches:** `packages/release-tools/scripts/` |
| 9 | + |
| 10 | +## Context |
| 11 | + |
| 12 | +`verify-changelog.mjs` already has a test file (`verify-changelog.test.mjs`). The other two scripts with non-trivial logic — `bump-version.mjs` and `sync-version.mjs` — have no tests. This spec adds coverage for both, following the same subprocess-based pattern already established by `verify-changelog.test.mjs`. |
| 13 | + |
| 14 | +`tag.mjs` is excluded: its core logic (read name+version, run sync, call `git tag`) is tested indirectly through integration; the git interaction makes unit testing add little value without a real repo context. |
| 15 | + |
| 16 | +## Current behaviour |
| 17 | + |
| 18 | +- `packages/release-tools/scripts/verify-changelog.test.mjs` exists — 9 test cases |
| 19 | +- `packages/release-tools/scripts/bump-version.test.mjs` — does not exist |
| 20 | +- `packages/release-tools/scripts/sync-version.test.mjs` — does not exist |
| 21 | +- `package.json` test script only references `verify-changelog.test.mjs` |
| 22 | + |
| 23 | +## Target behaviour |
| 24 | + |
| 25 | +- `bump-version.test.mjs` covers: invalid type, dirty tree, missing files, malformed version, no real changelog entries, missing [Unreleased] section, patch/minor/major version arithmetic, CHANGELOG promotion |
| 26 | +- `sync-version.test.mjs` covers: version sync to marketplace + package.json, no-op when up to date, error on missing files, malformed version, field mirroring (license, homepage, keywords) |
| 27 | +- `package.json` test script runs all three test files |
| 28 | + |
| 29 | +## Affected files |
| 30 | + |
| 31 | +| File | Change | |
| 32 | +|---|---| |
| 33 | +| `packages/release-tools/scripts/bump-version.test.mjs` | Create | |
| 34 | +| `packages/release-tools/scripts/sync-version.test.mjs` | Create | |
| 35 | +| `packages/release-tools/package.json` | Modify — extend test script | |
| 36 | + |
| 37 | +## Implementation steps |
| 38 | + |
| 39 | +1. Create `packages/release-tools/scripts/bump-version.test.mjs` with test cases listed in Acceptance criteria. Use the same subprocess-based pattern as `verify-changelog.test.mjs`: create a temp dir via `mkdtempSync`, write fixture files, run the script via `spawnSync` with `cwd: tmpDir` (since `bump-version.mjs` resolves paths from `process.cwd()`). For tests that need a clean or dirty git state, inject cross-platform fake `git` stubs into the temp dir: a POSIX shell script (`git`) for macOS/Linux and a CMD batch file (`git.cmd`) for Windows. Prepend the temp dir to PATH using `path.delimiter` (not a hard-coded `:`), matching the approach in `verify-changelog.test.mjs` but extended to cover Windows. Happy-path bump tests also require a `.claude-plugin/marketplace.json` fixture in the temp dir (consumed by `sync-version.mjs`). |
| 40 | + |
| 41 | +2. Create `packages/release-tools/scripts/sync-version.test.mjs` with test cases listed in Acceptance criteria. |
| 42 | + |
| 43 | +3. Update `packages/release-tools/package.json` test script to include all three test files: |
| 44 | + ```json |
| 45 | + "test": "node --test scripts/verify-changelog.test.mjs scripts/bump-version.test.mjs scripts/sync-version.test.mjs" |
| 46 | + ``` |
| 47 | + |
| 48 | +4. Run `pnpm --filter @unic/release-tools test` and verify all tests pass. |
| 49 | + |
| 50 | +5. Commit: `test(release-tools): add bump-version and sync-version tests (spec-15)` |
| 51 | + |
| 52 | +## Verification |
| 53 | + |
| 54 | +```sh |
| 55 | +pnpm --filter @unic/release-tools test |
| 56 | +# All test cases pass; CI validates macOS, Windows, and Linux |
| 57 | +``` |
| 58 | + |
| 59 | +## Acceptance criteria |
| 60 | + |
| 61 | +### bump-version.test.mjs |
| 62 | +- [ ] `exits 1 with usage message when bump type is invalid` |
| 63 | +- [ ] `exits 1 when working tree is dirty` |
| 64 | +- [ ] `exits 1 when plugin.json is missing` |
| 65 | +- [ ] `exits 1 when version in plugin.json is malformed (e.g. "not-semver")` |
| 66 | +- [ ] `exits 1 when CHANGELOG.md is missing` |
| 67 | +- [ ] `exits 1 when CHANGELOG.md has no [Unreleased] section` |
| 68 | +- [ ] `exits 1 when [Unreleased] has no real entries (only "(none)")` |
| 69 | +- [ ] `patch bump: increments patch, resets nothing` |
| 70 | +- [ ] `minor bump: increments minor, resets patch to 0` |
| 71 | +- [ ] `major bump: increments major, resets minor and patch to 0` |
| 72 | +- [ ] `bump promotes [Unreleased] section and leaves a fresh empty [Unreleased] — released section date matches YYYY-MM-DD` |
| 73 | + |
| 74 | +### sync-version.test.mjs |
| 75 | +- [ ] `exits 1 when plugin.json is missing` |
| 76 | +- [ ] `exits 1 when .version is missing or not a string in plugin.json` |
| 77 | +- [ ] `exits 1 when marketplace.json has no plugins[] array` |
| 78 | +- [ ] `syncs version from plugin.json to marketplace.json` |
| 79 | +- [ ] `syncs version to package.json when it exists` |
| 80 | +- [ ] `reports no change when versions already match` |
| 81 | +- [ ] `mirrors license, homepage, and keywords fields from plugin.json into marketplace entry` |
| 82 | + |
| 83 | +### package.json |
| 84 | +- [ ] test script references all three test files |
| 85 | + |
| 86 | +## Out of scope |
| 87 | + |
| 88 | +- Tests for `tag.mjs` (git-heavy, integration-only) |
| 89 | +- Extracting a shared reusable fake-binary helper across test files — each test file can inline its own stubs |
0 commit comments