mandrel 1.59.0 → 1.61.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/.agents/README.md +86 -44
- package/.agents/docs/SDLC.md +135 -141
- package/.agents/docs/configuration.md +77 -20
- package/.agents/docs/quality-gates.md +796 -0
- package/.agents/docs/workflows.md +6 -9
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +2 -2
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/agents-bootstrap-github.js +23 -119
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +6 -6
- package/.agents/scripts/epic-deliver-prepare.js +1 -1
- package/.agents/scripts/epic-execute-record-wave.js +4 -4
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +7 -11
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +47 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +7 -9
- package/.agents/scripts/lib/bootstrap/manifest.js +21 -1
- package/.agents/scripts/lib/bootstrap/merge-methods.js +31 -16
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +32 -11
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/detect-package-manager.js +72 -0
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/errors/index.js +4 -4
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +5 -10
- package/.agents/scripts/lib/onboard/detect-stack.js +10 -10
- package/.agents/scripts/lib/onboard/init-tail.js +218 -0
- package/.agents/scripts/lib/onboard/scaffold-docs.js +18 -3
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +5 -5
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +9 -25
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +26 -13
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +6 -21
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/preflight-cache.js +1 -1
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +2 -2
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +2 -2
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +5 -12
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +8 -8
- package/.agents/scripts/lib/orchestration/ticketing/state.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +2 -2
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +1 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/runtime-deps/preflight.js +6 -6
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +8 -8
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/wave-runner/tick.js +1 -1
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +5 -2
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +4 -4
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +5 -5
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +13 -16
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +1 -1
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +16 -31
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +2 -2
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/agents-sync-config.md +3 -2
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +43 -43
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +13 -13
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +11 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/plan.md +131 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +19 -39
- package/bin/mandrel.js +235 -16
- package/docs/CHANGELOG.md +1173 -0
- package/lib/cli/doctor.js +45 -3
- package/lib/cli/init.js +97 -36
- package/lib/cli/registry.js +41 -145
- package/lib/cli/sync.js +122 -23
- package/lib/cli/uninstall.js +42 -7
- package/lib/cli/update.js +524 -210
- package/lib/cli/version-helpers.js +59 -0
- package/package.json +7 -6
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/workflows/onboard.md +0 -208
- package/lib/cli/__tests__/migrate.test.js +0 -268
- package/lib/cli/__tests__/sync-local-zone.test.js +0 -247
- package/lib/cli/__tests__/sync.test.js +0 -372
- package/lib/cli/__tests__/update-major.test.js +0 -217
- package/lib/cli/__tests__/update.test.js +0 -696
- package/lib/cli/__tests__/version-check.test.js +0 -398
- package/lib/migrations/__tests__/index.test.js +0 -216
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
// lib/cli/__tests__/sync.test.js
|
|
2
|
-
/**
|
|
3
|
-
* Unit tests for lib/cli/sync.js — the `mandrel sync` materializer.
|
|
4
|
-
*
|
|
5
|
-
* Every test drives runSync through injectable seams (resolvePackageRoot,
|
|
6
|
-
* fs, cwd, write, writeErr, exit) backed by an in-memory filesystem fake,
|
|
7
|
-
* so no real package resolution and no real disk I/O occur (testing-standards
|
|
8
|
-
* § Unit: all filesystem I/O MUST be mocked).
|
|
9
|
-
*
|
|
10
|
-
* Coverage contract (per Story #3467 AC):
|
|
11
|
-
* 1. Copies the package .agents/ tree into ./.agents/ as plain files
|
|
12
|
-
* (no symlinks created — symlinkSync is never called).
|
|
13
|
-
* 2. Re-running is idempotent — a second run leaves ./.agents/
|
|
14
|
-
* byte-identical.
|
|
15
|
-
* 3. Exits non-zero with an actionable message when mandrel is
|
|
16
|
-
* not resolvable in node_modules.
|
|
17
|
-
* 4. --dry-run reports planned copies and writes nothing.
|
|
18
|
-
* 5. Module shape: runSync named export + default function export.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import assert from 'node:assert/strict';
|
|
22
|
-
import path from 'node:path';
|
|
23
|
-
import { describe, it } from 'node:test';
|
|
24
|
-
|
|
25
|
-
import sync, { runSync } from '../sync.js';
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// In-memory filesystem fake
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Build an in-memory fs whose `seed` maps absolute file paths → contents.
|
|
33
|
-
* Directories are inferred from the seeded file paths.
|
|
34
|
-
*
|
|
35
|
-
* Tracks writes (copyFileSync) and guards against symlink creation so tests
|
|
36
|
-
* can prove the materializer never produces symlinks.
|
|
37
|
-
*/
|
|
38
|
-
function makeFs(seed = {}) {
|
|
39
|
-
// Live store: absolute path → contents. Seeded entries plus anything the
|
|
40
|
-
// code under test writes via copyFileSync.
|
|
41
|
-
const files = new Map(Object.entries(seed));
|
|
42
|
-
const symlinkCalls = [];
|
|
43
|
-
|
|
44
|
-
function dirEntries(dir) {
|
|
45
|
-
const norm = dir.endsWith(path.sep) ? dir.slice(0, -1) : dir;
|
|
46
|
-
const children = new Map(); // name → isDir
|
|
47
|
-
for (const abs of files.keys()) {
|
|
48
|
-
if (!abs.startsWith(norm + path.sep)) continue;
|
|
49
|
-
const rest = abs.slice(norm.length + 1);
|
|
50
|
-
const segments = rest.split(path.sep);
|
|
51
|
-
const name = segments[0];
|
|
52
|
-
children.set(name, segments.length > 1);
|
|
53
|
-
}
|
|
54
|
-
return [...children.entries()].map(([name, isDir]) => ({
|
|
55
|
-
name,
|
|
56
|
-
isDirectory: () => isDir,
|
|
57
|
-
}));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
files,
|
|
62
|
-
symlinkCalls,
|
|
63
|
-
readdirSync(dir, _opts) {
|
|
64
|
-
return dirEntries(dir);
|
|
65
|
-
},
|
|
66
|
-
existsSync(p) {
|
|
67
|
-
const norm = p.endsWith(path.sep) ? p.slice(0, -1) : p;
|
|
68
|
-
if (files.has(norm)) return true;
|
|
69
|
-
// A directory "exists" if any seeded file lives under it.
|
|
70
|
-
for (const abs of files.keys()) {
|
|
71
|
-
if (abs.startsWith(norm + path.sep)) return true;
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
74
|
-
},
|
|
75
|
-
mkdirSync(_dir, _opts) {
|
|
76
|
-
// No-op: directories are implied by file paths in this fake.
|
|
77
|
-
},
|
|
78
|
-
copyFileSync(src, dest) {
|
|
79
|
-
if (!files.has(src)) {
|
|
80
|
-
throw new Error(`copyFileSync: source missing ${src}`);
|
|
81
|
-
}
|
|
82
|
-
files.set(dest, files.get(src));
|
|
83
|
-
},
|
|
84
|
-
symlinkSync(target, p) {
|
|
85
|
-
symlinkCalls.push({ target, path: p });
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Capture stdout/stderr writes and the exit code. */
|
|
91
|
-
function makeCapture() {
|
|
92
|
-
const out = [];
|
|
93
|
-
const err = [];
|
|
94
|
-
let exitCode = null;
|
|
95
|
-
return {
|
|
96
|
-
out,
|
|
97
|
-
err,
|
|
98
|
-
get exitCode() {
|
|
99
|
-
return exitCode;
|
|
100
|
-
},
|
|
101
|
-
write: (s) => out.push(s),
|
|
102
|
-
writeErr: (s) => err.push(s),
|
|
103
|
-
exit: (code) => {
|
|
104
|
-
exitCode = code;
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const PROJECT = path.join(path.sep, 'proj');
|
|
110
|
-
const PKG_ROOT = path.join(PROJECT, 'node_modules', 'mandrel');
|
|
111
|
-
|
|
112
|
-
/** Seed a package payload of two files under <pkg>/.agents/. */
|
|
113
|
-
function seedPackagePayload() {
|
|
114
|
-
const agentsDir = path.join(PKG_ROOT, '.agents');
|
|
115
|
-
return {
|
|
116
|
-
[path.join(agentsDir, 'instructions.md')]: '# instructions\n',
|
|
117
|
-
[path.join(agentsDir, 'rules', 'security-baseline.md')]: '# security\n',
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const resolveToPkg = () => PKG_ROOT;
|
|
122
|
-
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
// Module shape
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
|
|
127
|
-
describe('sync module exports', () => {
|
|
128
|
-
it('exports runSync as a named export', () => {
|
|
129
|
-
assert.equal(typeof runSync, 'function');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('exports a default function for bin/mandrel.js dispatch', () => {
|
|
133
|
-
assert.equal(typeof sync, 'function');
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// ---------------------------------------------------------------------------
|
|
138
|
-
// AC1 — copies the tree as plain files, no symlinks
|
|
139
|
-
// ---------------------------------------------------------------------------
|
|
140
|
-
|
|
141
|
-
describe('runSync — copies .agents/ payload as plain files', () => {
|
|
142
|
-
it('copies every package file into ./.agents/', () => {
|
|
143
|
-
const fs = makeFs(seedPackagePayload());
|
|
144
|
-
const cap = makeCapture();
|
|
145
|
-
const result = runSync({
|
|
146
|
-
argv: [],
|
|
147
|
-
resolvePackageRoot: resolveToPkg,
|
|
148
|
-
fs,
|
|
149
|
-
cwd: () => PROJECT,
|
|
150
|
-
write: cap.write,
|
|
151
|
-
writeErr: cap.writeErr,
|
|
152
|
-
exit: cap.exit,
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const destInstr = path.join(PROJECT, '.agents', 'instructions.md');
|
|
156
|
-
const destRule = path.join(
|
|
157
|
-
PROJECT,
|
|
158
|
-
'.agents',
|
|
159
|
-
'rules',
|
|
160
|
-
'security-baseline.md',
|
|
161
|
-
);
|
|
162
|
-
assert.equal(fs.files.get(destInstr), '# instructions\n');
|
|
163
|
-
assert.equal(fs.files.get(destRule), '# security\n');
|
|
164
|
-
assert.equal(result.copied, 2);
|
|
165
|
-
assert.equal(cap.exitCode, null);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('never creates a symlink', () => {
|
|
169
|
-
const fs = makeFs(seedPackagePayload());
|
|
170
|
-
const cap = makeCapture();
|
|
171
|
-
runSync({
|
|
172
|
-
argv: [],
|
|
173
|
-
resolvePackageRoot: resolveToPkg,
|
|
174
|
-
fs,
|
|
175
|
-
cwd: () => PROJECT,
|
|
176
|
-
write: cap.write,
|
|
177
|
-
writeErr: cap.writeErr,
|
|
178
|
-
exit: cap.exit,
|
|
179
|
-
});
|
|
180
|
-
assert.equal(fs.symlinkCalls.length, 0);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('reports the materialized file count on stdout', () => {
|
|
184
|
-
const fs = makeFs(seedPackagePayload());
|
|
185
|
-
const cap = makeCapture();
|
|
186
|
-
runSync({
|
|
187
|
-
argv: [],
|
|
188
|
-
resolvePackageRoot: resolveToPkg,
|
|
189
|
-
fs,
|
|
190
|
-
cwd: () => PROJECT,
|
|
191
|
-
write: cap.write,
|
|
192
|
-
writeErr: cap.writeErr,
|
|
193
|
-
exit: cap.exit,
|
|
194
|
-
});
|
|
195
|
-
assert.match(cap.out.join(''), /Materialized 2 file\(s\)/);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// ---------------------------------------------------------------------------
|
|
200
|
-
// AC2 — idempotency
|
|
201
|
-
// ---------------------------------------------------------------------------
|
|
202
|
-
|
|
203
|
-
describe('runSync — idempotent re-run', () => {
|
|
204
|
-
it('leaves ./.agents/ byte-identical after a second run', () => {
|
|
205
|
-
const fs = makeFs(seedPackagePayload());
|
|
206
|
-
const opts = {
|
|
207
|
-
argv: [],
|
|
208
|
-
resolvePackageRoot: resolveToPkg,
|
|
209
|
-
fs,
|
|
210
|
-
cwd: () => PROJECT,
|
|
211
|
-
write: () => {},
|
|
212
|
-
writeErr: () => {},
|
|
213
|
-
exit: () => {},
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
runSync(opts);
|
|
217
|
-
// Snapshot the destination tree after the first run.
|
|
218
|
-
const destPrefix = path.join(PROJECT, '.agents') + path.sep;
|
|
219
|
-
const snapshot = JSON.stringify(
|
|
220
|
-
[...fs.files.entries()].filter(([k]) => k.startsWith(destPrefix)).sort(),
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
runSync(opts);
|
|
224
|
-
const after = JSON.stringify(
|
|
225
|
-
[...fs.files.entries()].filter(([k]) => k.startsWith(destPrefix)).sort(),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
assert.equal(after, snapshot);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// ---------------------------------------------------------------------------
|
|
233
|
-
// AC3 — missing package → non-zero exit with actionable message
|
|
234
|
-
// ---------------------------------------------------------------------------
|
|
235
|
-
|
|
236
|
-
describe('runSync — mandrel not resolvable', () => {
|
|
237
|
-
function resolveThrows() {
|
|
238
|
-
const err = new Error("Cannot find module 'mandrel/package.json'");
|
|
239
|
-
err.code = 'MODULE_NOT_FOUND';
|
|
240
|
-
throw err;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
it('exits non-zero', () => {
|
|
244
|
-
const fs = makeFs();
|
|
245
|
-
const cap = makeCapture();
|
|
246
|
-
runSync({
|
|
247
|
-
argv: [],
|
|
248
|
-
resolvePackageRoot: resolveThrows,
|
|
249
|
-
fs,
|
|
250
|
-
cwd: () => PROJECT,
|
|
251
|
-
write: cap.write,
|
|
252
|
-
writeErr: cap.writeErr,
|
|
253
|
-
exit: cap.exit,
|
|
254
|
-
});
|
|
255
|
-
assert.equal(cap.exitCode, 1);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it('emits an actionable message naming the package and install command', () => {
|
|
259
|
-
const fs = makeFs();
|
|
260
|
-
const cap = makeCapture();
|
|
261
|
-
runSync({
|
|
262
|
-
argv: [],
|
|
263
|
-
resolvePackageRoot: resolveThrows,
|
|
264
|
-
fs,
|
|
265
|
-
cwd: () => PROJECT,
|
|
266
|
-
write: cap.write,
|
|
267
|
-
writeErr: cap.writeErr,
|
|
268
|
-
exit: cap.exit,
|
|
269
|
-
});
|
|
270
|
-
const joined = cap.err.join('');
|
|
271
|
-
assert.match(joined, /mandrel/);
|
|
272
|
-
assert.match(joined, /npm install mandrel/);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('writes nothing to the destination', () => {
|
|
276
|
-
const fs = makeFs();
|
|
277
|
-
const cap = makeCapture();
|
|
278
|
-
runSync({
|
|
279
|
-
argv: [],
|
|
280
|
-
resolvePackageRoot: resolveThrows,
|
|
281
|
-
fs,
|
|
282
|
-
cwd: () => PROJECT,
|
|
283
|
-
write: cap.write,
|
|
284
|
-
writeErr: cap.writeErr,
|
|
285
|
-
exit: cap.exit,
|
|
286
|
-
});
|
|
287
|
-
assert.equal(fs.files.size, 0);
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// ---------------------------------------------------------------------------
|
|
292
|
-
// AC3b — package resolvable but ships no .agents/ payload
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
|
|
295
|
-
describe('runSync — package present but no .agents/ payload', () => {
|
|
296
|
-
it('exits non-zero with an actionable message', () => {
|
|
297
|
-
// Seed a package whose only file is its package.json (no .agents/ tree).
|
|
298
|
-
const fs = makeFs({
|
|
299
|
-
[path.join(PKG_ROOT, 'package.json')]: '{}',
|
|
300
|
-
});
|
|
301
|
-
const cap = makeCapture();
|
|
302
|
-
runSync({
|
|
303
|
-
argv: [],
|
|
304
|
-
resolvePackageRoot: resolveToPkg,
|
|
305
|
-
fs,
|
|
306
|
-
cwd: () => PROJECT,
|
|
307
|
-
write: cap.write,
|
|
308
|
-
writeErr: cap.writeErr,
|
|
309
|
-
exit: cap.exit,
|
|
310
|
-
});
|
|
311
|
-
assert.equal(cap.exitCode, 1);
|
|
312
|
-
assert.match(cap.err.join(''), /no .agents\/ payload/);
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// ---------------------------------------------------------------------------
|
|
317
|
-
// AC4 — --dry-run reports planned copies, writes nothing
|
|
318
|
-
// ---------------------------------------------------------------------------
|
|
319
|
-
|
|
320
|
-
describe('runSync — --dry-run', () => {
|
|
321
|
-
it('writes nothing to the destination', () => {
|
|
322
|
-
const fs = makeFs(seedPackagePayload());
|
|
323
|
-
const cap = makeCapture();
|
|
324
|
-
const before = fs.files.size;
|
|
325
|
-
runSync({
|
|
326
|
-
argv: ['--dry-run'],
|
|
327
|
-
resolvePackageRoot: resolveToPkg,
|
|
328
|
-
fs,
|
|
329
|
-
cwd: () => PROJECT,
|
|
330
|
-
write: cap.write,
|
|
331
|
-
writeErr: cap.writeErr,
|
|
332
|
-
exit: cap.exit,
|
|
333
|
-
});
|
|
334
|
-
// No new files written: store size is unchanged from the seed.
|
|
335
|
-
assert.equal(fs.files.size, before);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it('reports the planned copies and a dry-run summary', () => {
|
|
339
|
-
const fs = makeFs(seedPackagePayload());
|
|
340
|
-
const cap = makeCapture();
|
|
341
|
-
const result = runSync({
|
|
342
|
-
argv: ['--dry-run'],
|
|
343
|
-
resolvePackageRoot: resolveToPkg,
|
|
344
|
-
fs,
|
|
345
|
-
cwd: () => PROJECT,
|
|
346
|
-
write: cap.write,
|
|
347
|
-
writeErr: cap.writeErr,
|
|
348
|
-
exit: cap.exit,
|
|
349
|
-
});
|
|
350
|
-
const joined = cap.out.join('');
|
|
351
|
-
assert.match(joined, /would copy/);
|
|
352
|
-
assert.match(joined, /instructions\.md/);
|
|
353
|
-
assert.match(joined, /Dry run: 2 file\(s\)/);
|
|
354
|
-
assert.equal(result.planned, 2);
|
|
355
|
-
assert.equal(result.copied, 0);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it('does not call exit on the dry-run happy path', () => {
|
|
359
|
-
const fs = makeFs(seedPackagePayload());
|
|
360
|
-
const cap = makeCapture();
|
|
361
|
-
runSync({
|
|
362
|
-
argv: ['--dry-run'],
|
|
363
|
-
resolvePackageRoot: resolveToPkg,
|
|
364
|
-
fs,
|
|
365
|
-
cwd: () => PROJECT,
|
|
366
|
-
write: cap.write,
|
|
367
|
-
writeErr: cap.writeErr,
|
|
368
|
-
exit: cap.exit,
|
|
369
|
-
});
|
|
370
|
-
assert.equal(cap.exitCode, null);
|
|
371
|
-
});
|
|
372
|
-
});
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
// lib/cli/__tests__/update-major.test.js
|
|
2
|
-
/**
|
|
3
|
-
* Unit tests for the major-version gate in lib/cli/update.js.
|
|
4
|
-
*
|
|
5
|
-
* Every test drives runUpdate through injectable seams; no real npm process,
|
|
6
|
-
* filesystem I/O, or network call occurs (testing-standards § Unit).
|
|
7
|
-
*
|
|
8
|
-
* Coverage contract (Story #3503 AC — major gate):
|
|
9
|
-
* - When the newest version crosses a major boundary and `--major` is
|
|
10
|
-
* absent, run declines, prints the docs/upgrade-major.md runbook pointer,
|
|
11
|
-
* exits non-zero, and invokes NO npm-update / sync / migration / doctor
|
|
12
|
-
* seam.
|
|
13
|
-
* - When `--major` is passed, run applies the major target and prints the
|
|
14
|
-
* runbook inline.
|
|
15
|
-
* - --dry-run on a major target prints the plan without applying.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import assert from 'node:assert/strict';
|
|
19
|
-
import { describe, it } from 'node:test';
|
|
20
|
-
|
|
21
|
-
import { runUpdate } from '../update.js';
|
|
22
|
-
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Capture + seam helpers
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
/** Capture stdout/stderr writes and the exit code. */
|
|
28
|
-
function makeCapture() {
|
|
29
|
-
const out = [];
|
|
30
|
-
const err = [];
|
|
31
|
-
let exitCode = null;
|
|
32
|
-
return {
|
|
33
|
-
out,
|
|
34
|
-
err,
|
|
35
|
-
get exitCode() {
|
|
36
|
-
return exitCode;
|
|
37
|
-
},
|
|
38
|
-
write: (s) => out.push(s),
|
|
39
|
-
writeErr: (s) => err.push(s),
|
|
40
|
-
exit: (code) => {
|
|
41
|
-
exitCode = code;
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Build recording seams for a MAJOR crossing (1.43.0 → 2.0.0). Every effectful
|
|
48
|
-
* seam records into `calls` so a test can assert that none of them ran when
|
|
49
|
-
* the gate refuses.
|
|
50
|
-
*/
|
|
51
|
-
function makeMajorSeams({ target = '2.0.0', doctorOk = true } = {}) {
|
|
52
|
-
const calls = [];
|
|
53
|
-
return {
|
|
54
|
-
calls,
|
|
55
|
-
currentVersion: '1.43.0',
|
|
56
|
-
resolveTargetVersion: async () => {
|
|
57
|
-
calls.push('resolveTargetVersion');
|
|
58
|
-
return target;
|
|
59
|
-
},
|
|
60
|
-
npmUpdate: async (version) => {
|
|
61
|
-
calls.push(`npmUpdate:${version}`);
|
|
62
|
-
},
|
|
63
|
-
runSync: (_opts) => {
|
|
64
|
-
calls.push('runSync');
|
|
65
|
-
return { copied: 0, planned: 0, dryRun: false };
|
|
66
|
-
},
|
|
67
|
-
runMigrations: ({ fromVersion, toVersion }) => {
|
|
68
|
-
calls.push(`runMigrations:${fromVersion}->${toVersion}`);
|
|
69
|
-
return { applied: [], skipped: [] };
|
|
70
|
-
},
|
|
71
|
-
runDoctor: async () => {
|
|
72
|
-
calls.push('runDoctor');
|
|
73
|
-
return {
|
|
74
|
-
ok: doctorOk,
|
|
75
|
-
results: [{ name: 'node-version', ok: doctorOk }],
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
surfaceChangelog: async (version) => {
|
|
79
|
-
calls.push(`surfaceChangelog:${version}`);
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
// AC — major boundary without --major is refused
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
|
|
88
|
-
describe('runUpdate — major gate without --major', () => {
|
|
89
|
-
it('declines, exits non-zero, and invokes no effectful seam', async () => {
|
|
90
|
-
const seams = makeMajorSeams({ target: '2.0.0' });
|
|
91
|
-
const cap = makeCapture();
|
|
92
|
-
|
|
93
|
-
const result = await runUpdate({
|
|
94
|
-
argv: [],
|
|
95
|
-
...seams,
|
|
96
|
-
write: cap.write,
|
|
97
|
-
writeErr: cap.writeErr,
|
|
98
|
-
exit: cap.exit,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
assert.equal(result.ok, false);
|
|
102
|
-
assert.equal(result.action, 'declined-major');
|
|
103
|
-
assert.equal(result.major, true);
|
|
104
|
-
assert.deepEqual(result.stepsRun, []);
|
|
105
|
-
assert.equal(cap.exitCode, 1);
|
|
106
|
-
|
|
107
|
-
// No npm-update / sync / migration / doctor seam fired — only the resolve.
|
|
108
|
-
assert.deepEqual(seams.calls, ['resolveTargetVersion']);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('prints the docs/upgrade-major.md runbook pointer and the available version', async () => {
|
|
112
|
-
const seams = makeMajorSeams({ target: '2.0.0' });
|
|
113
|
-
const cap = makeCapture();
|
|
114
|
-
|
|
115
|
-
await runUpdate({
|
|
116
|
-
argv: [],
|
|
117
|
-
...seams,
|
|
118
|
-
write: cap.write,
|
|
119
|
-
writeErr: cap.writeErr,
|
|
120
|
-
exit: cap.exit,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const joined = cap.err.join('');
|
|
124
|
-
assert.match(joined, /docs\/upgrade-major\.md/);
|
|
125
|
-
assert.match(joined, /2\.0\.0/);
|
|
126
|
-
assert.match(joined, /--major/);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('gates a 1.x → 3.0 leap the same way', async () => {
|
|
130
|
-
const seams = makeMajorSeams({ target: '3.0.0' });
|
|
131
|
-
const cap = makeCapture();
|
|
132
|
-
|
|
133
|
-
const result = await runUpdate({
|
|
134
|
-
argv: [],
|
|
135
|
-
...seams,
|
|
136
|
-
write: cap.write,
|
|
137
|
-
writeErr: cap.writeErr,
|
|
138
|
-
exit: cap.exit,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
assert.equal(result.action, 'declined-major');
|
|
142
|
-
assert.equal(cap.exitCode, 1);
|
|
143
|
-
assert.deepEqual(seams.calls, ['resolveTargetVersion']);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// ---------------------------------------------------------------------------
|
|
148
|
-
// AC — --major applies the major target and prints the runbook inline
|
|
149
|
-
// ---------------------------------------------------------------------------
|
|
150
|
-
|
|
151
|
-
describe('runUpdate — major gate with --major', () => {
|
|
152
|
-
it('applies the major target, driving the ordered steps', async () => {
|
|
153
|
-
const seams = makeMajorSeams({ target: '2.0.0', doctorOk: true });
|
|
154
|
-
const cap = makeCapture();
|
|
155
|
-
|
|
156
|
-
const result = await runUpdate({
|
|
157
|
-
argv: ['--major'],
|
|
158
|
-
...seams,
|
|
159
|
-
write: cap.write,
|
|
160
|
-
writeErr: cap.writeErr,
|
|
161
|
-
exit: cap.exit,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
assert.equal(result.ok, true);
|
|
165
|
-
assert.equal(result.action, 'updated');
|
|
166
|
-
assert.equal(result.major, true);
|
|
167
|
-
assert.deepEqual(result.stepsRun, [
|
|
168
|
-
'npm-update',
|
|
169
|
-
'runSync',
|
|
170
|
-
'runMigrations',
|
|
171
|
-
'doctor',
|
|
172
|
-
]);
|
|
173
|
-
assert.ok(seams.calls.includes('npmUpdate:2.0.0'));
|
|
174
|
-
assert.ok(seams.calls.includes('runMigrations:1.43.0->2.0.0'));
|
|
175
|
-
assert.equal(cap.exitCode, null);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('prints the runbook pointer inline on the applied-major path', async () => {
|
|
179
|
-
const seams = makeMajorSeams({ target: '2.0.0' });
|
|
180
|
-
const cap = makeCapture();
|
|
181
|
-
|
|
182
|
-
await runUpdate({
|
|
183
|
-
argv: ['--major'],
|
|
184
|
-
...seams,
|
|
185
|
-
write: cap.write,
|
|
186
|
-
writeErr: cap.writeErr,
|
|
187
|
-
exit: cap.exit,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
assert.match(cap.out.join(''), /docs\/upgrade-major\.md/);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
// --dry-run on a major target previews without applying
|
|
196
|
-
// ---------------------------------------------------------------------------
|
|
197
|
-
|
|
198
|
-
describe('runUpdate — --major --dry-run', () => {
|
|
199
|
-
it('prints the plan and invokes no effectful seam', async () => {
|
|
200
|
-
const seams = makeMajorSeams({ target: '2.0.0' });
|
|
201
|
-
const cap = makeCapture();
|
|
202
|
-
|
|
203
|
-
const result = await runUpdate({
|
|
204
|
-
argv: ['--major', '--dry-run'],
|
|
205
|
-
...seams,
|
|
206
|
-
write: cap.write,
|
|
207
|
-
writeErr: cap.writeErr,
|
|
208
|
-
exit: cap.exit,
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
assert.equal(result.action, 'dry-run');
|
|
212
|
-
assert.match(cap.out.join(''), /1\.43\.0 → v2\.0\.0/);
|
|
213
|
-
assert.match(cap.out.join(''), /major upgrade/);
|
|
214
|
-
assert.deepEqual(seams.calls, ['resolveTargetVersion']);
|
|
215
|
-
assert.equal(cap.exitCode, null);
|
|
216
|
-
});
|
|
217
|
-
});
|