akm-cli 0.7.0 → 0.7.1
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/package.json +8 -8
- package/dist/tests/add-website-source.test.js +0 -119
- package/dist/tests/agent/agent-config-loader.test.js +0 -70
- package/dist/tests/agent/agent-config.test.js +0 -221
- package/dist/tests/agent/agent-detect.test.js +0 -100
- package/dist/tests/agent/agent-spawn.test.js +0 -234
- package/dist/tests/agent-output.test.js +0 -186
- package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
- package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
- package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
- package/dist/tests/asset-ref.test.js +0 -192
- package/dist/tests/asset-registry.test.js +0 -103
- package/dist/tests/asset-spec.test.js +0 -241
- package/dist/tests/bench/attribution.test.js +0 -996
- package/dist/tests/bench/cleanup-sigint.test.js +0 -83
- package/dist/tests/bench/cleanup.js +0 -234
- package/dist/tests/bench/cleanup.test.js +0 -166
- package/dist/tests/bench/cli.js +0 -1018
- package/dist/tests/bench/cli.test.js +0 -445
- package/dist/tests/bench/compare.test.js +0 -556
- package/dist/tests/bench/corpus.js +0 -317
- package/dist/tests/bench/corpus.test.js +0 -258
- package/dist/tests/bench/doctor.js +0 -525
- package/dist/tests/bench/driver.js +0 -401
- package/dist/tests/bench/driver.test.js +0 -584
- package/dist/tests/bench/environment.js +0 -233
- package/dist/tests/bench/environment.test.js +0 -199
- package/dist/tests/bench/evolve-metrics.js +0 -179
- package/dist/tests/bench/evolve-metrics.test.js +0 -187
- package/dist/tests/bench/evolve.js +0 -647
- package/dist/tests/bench/evolve.test.js +0 -624
- package/dist/tests/bench/failure-modes.test.js +0 -349
- package/dist/tests/bench/feedback-integrity.test.js +0 -457
- package/dist/tests/bench/leakage.test.js +0 -228
- package/dist/tests/bench/learning-curve.test.js +0 -134
- package/dist/tests/bench/metrics.js +0 -2395
- package/dist/tests/bench/metrics.test.js +0 -1150
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
- package/dist/tests/bench/opencode-config.js +0 -194
- package/dist/tests/bench/opencode-config.test.js +0 -370
- package/dist/tests/bench/report.js +0 -1885
- package/dist/tests/bench/report.test.js +0 -1038
- package/dist/tests/bench/run-config.js +0 -355
- package/dist/tests/bench/run-config.test.js +0 -298
- package/dist/tests/bench/run-curate-test.js +0 -32
- package/dist/tests/bench/run-failing-tasks.js +0 -56
- package/dist/tests/bench/run-full-bench.js +0 -51
- package/dist/tests/bench/run-items36-targeted.js +0 -69
- package/dist/tests/bench/run-nano-quick.js +0 -42
- package/dist/tests/bench/run-waveg-targeted.js +0 -62
- package/dist/tests/bench/runner.js +0 -699
- package/dist/tests/bench/runner.test.js +0 -958
- package/dist/tests/bench/search-bridge.test.js +0 -331
- package/dist/tests/bench/tmp.js +0 -131
- package/dist/tests/bench/trajectory.js +0 -116
- package/dist/tests/bench/trajectory.test.js +0 -127
- package/dist/tests/bench/verifier.js +0 -114
- package/dist/tests/bench/verifier.test.js +0 -118
- package/dist/tests/bench/workflow-evaluator.js +0 -557
- package/dist/tests/bench/workflow-evaluator.test.js +0 -421
- package/dist/tests/bench/workflow-spec.js +0 -345
- package/dist/tests/bench/workflow-spec.test.js +0 -363
- package/dist/tests/bench/workflow-trace.js +0 -472
- package/dist/tests/bench/workflow-trace.test.js +0 -254
- package/dist/tests/benchmark-search-quality.js +0 -536
- package/dist/tests/benchmark-suite.js +0 -1441
- package/dist/tests/capture-cli.test.js +0 -112
- package/dist/tests/cli-errors.test.js +0 -204
- package/dist/tests/commands/events.test.js +0 -370
- package/dist/tests/commands/history.test.js +0 -418
- package/dist/tests/commands/import.test.js +0 -103
- package/dist/tests/commands/proposal-cli.test.js +0 -209
- package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
- package/dist/tests/commands/remember.test.js +0 -97
- package/dist/tests/commands/scope-flags.test.js +0 -300
- package/dist/tests/commands/search.test.js +0 -537
- package/dist/tests/commands/show-indexer-parity.test.js +0 -117
- package/dist/tests/commands/show.test.js +0 -294
- package/dist/tests/common.test.js +0 -266
- package/dist/tests/completions.test.js +0 -142
- package/dist/tests/config-cli.test.js +0 -193
- package/dist/tests/config-llm-features.test.js +0 -139
- package/dist/tests/config.test.js +0 -569
- package/dist/tests/contracts/migration-baseline.test.js +0 -43
- package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
- package/dist/tests/contracts/spec-helpers.js +0 -46
- package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
- package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
- package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
- package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
- package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
- package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
- package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
- package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
- package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
- package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
- package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
- package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
- package/dist/tests/core/write-source.test.js +0 -366
- package/dist/tests/curate-command.test.js +0 -87
- package/dist/tests/db-scoring.test.js +0 -201
- package/dist/tests/db.test.js +0 -654
- package/dist/tests/distill-cli-flag.test.js +0 -208
- package/dist/tests/distill.test.js +0 -515
- package/dist/tests/docker-install.test.js +0 -120
- package/dist/tests/e2e.test.js +0 -1419
- package/dist/tests/embedder.test.js +0 -340
- package/dist/tests/embedding-model-config.test.js +0 -379
- package/dist/tests/feedback-command.test.js +0 -172
- package/dist/tests/file-context.test.js +0 -552
- package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
- package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
- package/dist/tests/fixtures/stashes/load.js +0 -166
- package/dist/tests/fixtures/stashes/load.test.js +0 -97
- package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
- package/dist/tests/frontmatter.test.js +0 -190
- package/dist/tests/fts-field-weighting.test.js +0 -254
- package/dist/tests/fuzzy-search.test.js +0 -230
- package/dist/tests/git-provider-clone.test.js +0 -45
- package/dist/tests/github.test.js +0 -161
- package/dist/tests/graph-boost-ranking.test.js +0 -305
- package/dist/tests/graph-extraction.test.js +0 -282
- package/dist/tests/helpers/usage-events.js +0 -8
- package/dist/tests/index-pass-llm.test.js +0 -161
- package/dist/tests/indexer.test.js +0 -570
- package/dist/tests/info-command.test.js +0 -166
- package/dist/tests/init.test.js +0 -69
- package/dist/tests/install-script.test.js +0 -246
- package/dist/tests/integration/agent-real-profile.test.js +0 -94
- package/dist/tests/issue-36-repro.test.js +0 -304
- package/dist/tests/issues-191-194.test.js +0 -160
- package/dist/tests/lesson-lint.test.js +0 -111
- package/dist/tests/llm-client.test.js +0 -115
- package/dist/tests/llm-feature-gate.test.js +0 -151
- package/dist/tests/llm.test.js +0 -139
- package/dist/tests/lockfile.test.js +0 -216
- package/dist/tests/manifest.test.js +0 -205
- package/dist/tests/markdown.test.js +0 -126
- package/dist/tests/matchers-unit.test.js +0 -189
- package/dist/tests/memory-inference.test.js +0 -299
- package/dist/tests/merge-scoring.test.js +0 -136
- package/dist/tests/metadata.test.js +0 -313
- package/dist/tests/migration-help.test.js +0 -89
- package/dist/tests/origin-resolve.test.js +0 -124
- package/dist/tests/output-baseline.test.js +0 -218
- package/dist/tests/output-shapes-unit.test.js +0 -478
- package/dist/tests/parallel-search.test.js +0 -272
- package/dist/tests/parameter-metadata.test.js +0 -365
- package/dist/tests/paths.test.js +0 -177
- package/dist/tests/progressive-disclosure.test.js +0 -280
- package/dist/tests/proposals.test.js +0 -279
- package/dist/tests/proposed-quality.test.js +0 -271
- package/dist/tests/provider-registry.test.js +0 -32
- package/dist/tests/ranking-regression.test.js +0 -548
- package/dist/tests/reflect-propose.test.js +0 -455
- package/dist/tests/registry-build-index.test.js +0 -394
- package/dist/tests/registry-cli.test.js +0 -290
- package/dist/tests/registry-index-v2.test.js +0 -430
- package/dist/tests/registry-install.test.js +0 -728
- package/dist/tests/registry-providers/parity.test.js +0 -189
- package/dist/tests/registry-providers/skills-sh.test.js +0 -309
- package/dist/tests/registry-providers/static-index.test.js +0 -238
- package/dist/tests/registry-resolve.test.js +0 -126
- package/dist/tests/registry-search.test.js +0 -923
- package/dist/tests/remember-frontmatter.test.js +0 -378
- package/dist/tests/remember-unit.test.js +0 -123
- package/dist/tests/ripgrep-install.test.js +0 -251
- package/dist/tests/ripgrep-resolve.test.js +0 -108
- package/dist/tests/ripgrep.test.js +0 -163
- package/dist/tests/save-command.test.js +0 -94
- package/dist/tests/save-trust-qa-fixes.test.js +0 -270
- package/dist/tests/scoring-pipeline.test.js +0 -648
- package/dist/tests/search-include-proposed-cli.test.js +0 -118
- package/dist/tests/self-update.test.js +0 -442
- package/dist/tests/semantic-search-e2e.test.js +0 -512
- package/dist/tests/semantic-status.test.js +0 -471
- package/dist/tests/setup-run.integration.js +0 -877
- package/dist/tests/setup-wizard.test.js +0 -198
- package/dist/tests/setup.test.js +0 -131
- package/dist/tests/source-add.test.js +0 -11
- package/dist/tests/source-clone.test.js +0 -254
- package/dist/tests/source-manage.test.js +0 -366
- package/dist/tests/source-providers/filesystem.test.js +0 -82
- package/dist/tests/source-providers/git.test.js +0 -252
- package/dist/tests/source-providers/website.test.js +0 -128
- package/dist/tests/source-qa-fixes.test.js +0 -286
- package/dist/tests/source-registry.test.js +0 -350
- package/dist/tests/source-resolve.test.js +0 -100
- package/dist/tests/source-source.test.js +0 -281
- package/dist/tests/source.test.js +0 -533
- package/dist/tests/tar-utils-scan.test.js +0 -73
- package/dist/tests/toggle-components.test.js +0 -73
- package/dist/tests/usage-telemetry.test.js +0 -265
- package/dist/tests/utility-scoring.test.js +0 -558
- package/dist/tests/vault-load-error.test.js +0 -78
- package/dist/tests/vault-qa-fixes.test.js +0 -194
- package/dist/tests/vault.test.js +0 -429
- package/dist/tests/vector-search.test.js +0 -608
- package/dist/tests/walker.test.js +0 -252
- package/dist/tests/wave2-cluster-bc.test.js +0 -228
- package/dist/tests/wave2-cluster-d.test.js +0 -180
- package/dist/tests/wave2-cluster-e.test.js +0 -179
- package/dist/tests/wiki-qa-fixes.test.js +0 -270
- package/dist/tests/wiki.test.js +0 -529
- package/dist/tests/workflow-cli.test.js +0 -271
- package/dist/tests/workflow-markdown.test.js +0 -171
- package/dist/tests/workflow-path-escape.test.js +0 -132
- package/dist/tests/workflow-qa-fixes.test.js +0 -395
- package/dist/tests/workflows/indexer-rejection.test.js +0 -213
- /package/dist/{src/cli.js → cli.js} +0 -0
- /package/dist/{src/commands → commands}/completions.js +0 -0
- /package/dist/{src/commands → commands}/config-cli.js +0 -0
- /package/dist/{src/commands → commands}/curate.js +0 -0
- /package/dist/{src/commands → commands}/distill.js +0 -0
- /package/dist/{src/commands → commands}/events.js +0 -0
- /package/dist/{src/commands → commands}/history.js +0 -0
- /package/dist/{src/commands → commands}/info.js +0 -0
- /package/dist/{src/commands → commands}/init.js +0 -0
- /package/dist/{src/commands → commands}/install-audit.js +0 -0
- /package/dist/{src/commands → commands}/installed-stashes.js +0 -0
- /package/dist/{src/commands → commands}/migration-help.js +0 -0
- /package/dist/{src/commands → commands}/proposal.js +0 -0
- /package/dist/{src/commands → commands}/propose.js +0 -0
- /package/dist/{src/commands → commands}/reflect.js +0 -0
- /package/dist/{src/commands → commands}/registry-search.js +0 -0
- /package/dist/{src/commands → commands}/remember.js +0 -0
- /package/dist/{src/commands → commands}/search.js +0 -0
- /package/dist/{src/commands → commands}/self-update.js +0 -0
- /package/dist/{src/commands → commands}/show.js +0 -0
- /package/dist/{src/commands → commands}/source-add.js +0 -0
- /package/dist/{src/commands → commands}/source-clone.js +0 -0
- /package/dist/{src/commands → commands}/source-manage.js +0 -0
- /package/dist/{src/commands → commands}/vault.js +0 -0
- /package/dist/{src/core → core}/asset-ref.js +0 -0
- /package/dist/{src/core → core}/asset-registry.js +0 -0
- /package/dist/{src/core → core}/asset-spec.js +0 -0
- /package/dist/{src/core → core}/common.js +0 -0
- /package/dist/{src/core → core}/config.js +0 -0
- /package/dist/{src/core → core}/errors.js +0 -0
- /package/dist/{src/core → core}/events.js +0 -0
- /package/dist/{src/core → core}/frontmatter.js +0 -0
- /package/dist/{src/core → core}/lesson-lint.js +0 -0
- /package/dist/{src/core → core}/markdown.js +0 -0
- /package/dist/{src/core → core}/paths.js +0 -0
- /package/dist/{src/core → core}/proposals.js +0 -0
- /package/dist/{src/core → core}/warn.js +0 -0
- /package/dist/{src/core → core}/write-source.js +0 -0
- /package/dist/{src/indexer → indexer}/db-search.js +0 -0
- /package/dist/{src/indexer → indexer}/db.js +0 -0
- /package/dist/{src/indexer → indexer}/file-context.js +0 -0
- /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
- /package/dist/{src/indexer → indexer}/graph-extraction.js +0 -0
- /package/dist/{src/indexer → indexer}/indexer.js +0 -0
- /package/dist/{src/indexer → indexer}/manifest.js +0 -0
- /package/dist/{src/indexer → indexer}/matchers.js +0 -0
- /package/dist/{src/indexer → indexer}/memory-inference.js +0 -0
- /package/dist/{src/indexer → indexer}/metadata.js +0 -0
- /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
- /package/dist/{src/indexer → indexer}/search-source.js +0 -0
- /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
- /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
- /package/dist/{src/indexer → indexer}/walker.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/profiles.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/spawn.js +0 -0
- /package/dist/{src/integrations → integrations}/github.js +0 -0
- /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
- /package/dist/{src/llm → llm}/client.js +0 -0
- /package/dist/{src/llm → llm}/embedder.js +0 -0
- /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
- /package/dist/{src/llm → llm}/embedders/local.js +0 -0
- /package/dist/{src/llm → llm}/embedders/remote.js +0 -0
- /package/dist/{src/llm → llm}/embedders/types.js +0 -0
- /package/dist/{src/llm → llm}/feature-gate.js +0 -0
- /package/dist/{src/llm → llm}/graph-extract.js +0 -0
- /package/dist/{src/llm → llm}/index-passes.js +0 -0
- /package/dist/{src/llm → llm}/memory-infer.js +0 -0
- /package/dist/{src/llm → llm}/metadata-enhance.js +0 -0
- /package/dist/{src/output → output}/cli-hints.js +0 -0
- /package/dist/{src/output → output}/context.js +0 -0
- /package/dist/{src/output → output}/renderers.js +0 -0
- /package/dist/{src/output → output}/shapes.js +0 -0
- /package/dist/{src/output → output}/text.js +0 -0
- /package/dist/{src/registry → registry}/build-index.js +0 -0
- /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
- /package/dist/{src/registry → registry}/factory.js +0 -0
- /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
- /package/dist/{src/registry → registry}/providers/index.js +0 -0
- /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
- /package/dist/{src/registry → registry}/providers/static-index.js +0 -0
- /package/dist/{src/registry → registry}/providers/types.js +0 -0
- /package/dist/{src/registry → registry}/resolve.js +0 -0
- /package/dist/{src/registry → registry}/types.js +0 -0
- /package/dist/{src/setup → setup}/detect.js +0 -0
- /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
- /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
- /package/dist/{src/setup → setup}/setup.js +0 -0
- /package/dist/{src/setup → setup}/steps.js +0 -0
- /package/dist/{src/sources → sources}/include.js +0 -0
- /package/dist/{src/sources → sources}/provider-factory.js +0 -0
- /package/dist/{src/sources → sources}/provider.js +0 -0
- /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
- /package/dist/{src/sources → sources}/providers/git.js +0 -0
- /package/dist/{src/sources → sources}/providers/index.js +0 -0
- /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
- /package/dist/{src/sources → sources}/providers/npm.js +0 -0
- /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
- /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
- /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
- /package/dist/{src/sources → sources}/providers/website.js +0 -0
- /package/dist/{src/sources → sources}/resolve.js +0 -0
- /package/dist/{src/sources → sources}/types.js +0 -0
- /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
- /package/dist/{src/version.js → version.js} +0 -0
- /package/dist/{src/wiki → wiki}/wiki.js +0 -0
- /package/dist/{src/workflows → workflows}/authoring.js +0 -0
- /package/dist/{src/workflows → workflows}/cli.js +0 -0
- /package/dist/{src/workflows → workflows}/db.js +0 -0
- /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
- /package/dist/{src/workflows → workflows}/parser.js +0 -0
- /package/dist/{src/workflows → workflows}/renderer.js +0 -0
- /package/dist/{src/workflows → workflows}/runs.js +0 -0
- /package/dist/{src/workflows → workflows}/schema.js +0 -0
- /package/dist/{src/workflows → workflows}/validator.js +0 -0
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subprocess test: real SIGINT delivery cleans up registered fns before
|
|
3
|
-
* exit (#267).
|
|
4
|
-
*
|
|
5
|
-
* The real handler calls `process.exit(130)` — fatal inside the test
|
|
6
|
-
* runner. So we drive it from a child Bun process and assert via side
|
|
7
|
-
* effects (touchstones written to a tmpdir) that every cleanup fn ran.
|
|
8
|
-
*/
|
|
9
|
-
import { afterAll, describe, expect, test } from "bun:test";
|
|
10
|
-
import { spawnSync } from "node:child_process";
|
|
11
|
-
import fs from "node:fs";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
import { benchMkdtemp } from "./tmp";
|
|
14
|
-
const tempDirs = [];
|
|
15
|
-
function makeTempDir(prefix = "akm-bench-cleanup-sigint-") {
|
|
16
|
-
const dir = benchMkdtemp(prefix);
|
|
17
|
-
tempDirs.push(dir);
|
|
18
|
-
return dir;
|
|
19
|
-
}
|
|
20
|
-
afterAll(() => {
|
|
21
|
-
for (const dir of tempDirs) {
|
|
22
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
const repoRoot = path.resolve(import.meta.dir, "..", "..");
|
|
26
|
-
describe("SIGINT delivery → registered cleanups run (#267)", () => {
|
|
27
|
-
test("subprocess: SIGINT runs every registered cleanup fn before exit", () => {
|
|
28
|
-
const sigDir = makeTempDir();
|
|
29
|
-
const a = path.join(sigDir, "a.touchstone");
|
|
30
|
-
const b = path.join(sigDir, "b.touchstone");
|
|
31
|
-
const c = path.join(sigDir, "c.touchstone");
|
|
32
|
-
// Inline driver script. It registers three cleanup fns that each touch
|
|
33
|
-
// a unique file, then `process.kill(pid, 'SIGINT')` and waits long
|
|
34
|
-
// enough for the handler to fire `process.exit(130)`.
|
|
35
|
-
const driverScript = `
|
|
36
|
-
import { registerCleanup } from ${JSON.stringify(path.join(repoRoot, "tests", "bench", "cleanup.ts"))};
|
|
37
|
-
import fs from "node:fs";
|
|
38
|
-
|
|
39
|
-
registerCleanup(() => fs.writeFileSync(${JSON.stringify(a)}, "a"));
|
|
40
|
-
registerCleanup(() => fs.writeFileSync(${JSON.stringify(b)}, "b"));
|
|
41
|
-
registerCleanup(async () => {
|
|
42
|
-
await new Promise((r) => setTimeout(r, 5));
|
|
43
|
-
fs.writeFileSync(${JSON.stringify(c)}, "c");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
process.kill(process.pid, "SIGINT");
|
|
47
|
-
// Stay alive until the signal handler fires + exits.
|
|
48
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
49
|
-
`;
|
|
50
|
-
const scriptPath = path.join(sigDir, "driver.mjs");
|
|
51
|
-
fs.writeFileSync(scriptPath, driverScript);
|
|
52
|
-
const result = spawnSync("bun", ["run", scriptPath], {
|
|
53
|
-
encoding: "utf8",
|
|
54
|
-
timeout: 10_000,
|
|
55
|
-
});
|
|
56
|
-
// Exit code 130 = signalled exit (POSIX convention 128 + SIGINT(2)).
|
|
57
|
-
expect(result.status).toBe(130);
|
|
58
|
-
// All three cleanup fns ran before exit.
|
|
59
|
-
expect(fs.existsSync(a)).toBe(true);
|
|
60
|
-
expect(fs.existsSync(b)).toBe(true);
|
|
61
|
-
expect(fs.existsSync(c)).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
test("subprocess: SIGTERM also triggers cleanup", () => {
|
|
64
|
-
const sigDir = makeTempDir();
|
|
65
|
-
const a = path.join(sigDir, "term.touchstone");
|
|
66
|
-
const driverScript = `
|
|
67
|
-
import { registerCleanup } from ${JSON.stringify(path.join(repoRoot, "tests", "bench", "cleanup.ts"))};
|
|
68
|
-
import fs from "node:fs";
|
|
69
|
-
|
|
70
|
-
registerCleanup(() => fs.writeFileSync(${JSON.stringify(a)}, "term"));
|
|
71
|
-
process.kill(process.pid, "SIGTERM");
|
|
72
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
73
|
-
`;
|
|
74
|
-
const scriptPath = path.join(sigDir, "driver-term.mjs");
|
|
75
|
-
fs.writeFileSync(scriptPath, driverScript);
|
|
76
|
-
const result = spawnSync("bun", ["run", scriptPath], {
|
|
77
|
-
encoding: "utf8",
|
|
78
|
-
timeout: 10_000,
|
|
79
|
-
});
|
|
80
|
-
expect(result.status).toBe(130);
|
|
81
|
-
expect(fs.existsSync(a)).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared cleanup registry for the bench harness (#267).
|
|
3
|
-
*
|
|
4
|
-
* The bench creates many tmp directories — per (task, arm, seed) workspace,
|
|
5
|
-
* per-task fixture stash, per-fixture evolveStash + preStash. Each of these
|
|
6
|
-
* is wrapped in a try/finally so happy-path runs leave nothing behind. But
|
|
7
|
-
* an external SIGINT/SIGTERM (operator hits Ctrl-C, CI cancels the job)
|
|
8
|
-
* bypasses `finally` blocks entirely on Bun, leaving orphan tmp dirs under
|
|
9
|
-
* the bench tmp root (#276 redirected this from the OS temp dir to
|
|
10
|
-
* `${AKM_CACHE_DIR}/bench/`) that nothing reaps.
|
|
11
|
-
*
|
|
12
|
-
* `registerCleanup(fn)` captures the cleanup intent on a process-wide
|
|
13
|
-
* registry and returns a deregister function. The first `registerCleanup`
|
|
14
|
-
* call also installs ONE pair of SIGINT/SIGTERM handlers — subsequent calls
|
|
15
|
-
* never re-install. On signal we walk every registered fn (swallowing
|
|
16
|
-
* errors), remove our own listeners (so a second Ctrl-C force-exits), and
|
|
17
|
-
* `process.exit(130)`.
|
|
18
|
-
*
|
|
19
|
-
* The handler is idempotent: re-entrant signals while cleanup is in flight
|
|
20
|
-
* are dropped. Per-tmp `try/finally` callers should:
|
|
21
|
-
* 1. Register the cleanup at the top of `try`.
|
|
22
|
-
* 2. Deregister it in `finally` *before* running cleanup themselves so the
|
|
23
|
-
* handler doesn't double-fire.
|
|
24
|
-
*
|
|
25
|
-
* Garbage-collection of orphan dirs (#276): the FIRST `registerCleanup` call
|
|
26
|
-
* also sweeps `${AKM_CACHE_DIR}/bench/*` entries older than 6h. This catches
|
|
27
|
-
* orphans from prior crashed runs that bypassed `finally`. Subsequent calls
|
|
28
|
-
* never re-sweep — the GC is install-once.
|
|
29
|
-
*/
|
|
30
|
-
import * as fs from "node:fs";
|
|
31
|
-
import * as path from "node:path";
|
|
32
|
-
import { warn } from "../../src/core/warn";
|
|
33
|
-
import { benchTmpRoot } from "./tmp";
|
|
34
|
-
/**
|
|
35
|
-
* Register a process-group kill for a spawned opencode PID.
|
|
36
|
-
*
|
|
37
|
-
* On SIGINT/SIGTERM the bench driver must kill the entire opencode process
|
|
38
|
-
* group (not just the node wrapper) so .opencode children don't become orphans
|
|
39
|
-
* that keep pipes open and block subsequent runs.
|
|
40
|
-
*
|
|
41
|
-
* Call this immediately after spawning opencode. Returns a deregister thunk
|
|
42
|
-
* that should be called once the process has exited (in the run's finally
|
|
43
|
-
* block).
|
|
44
|
-
*
|
|
45
|
-
* The SIGKILL is sent to the process group (`-pid`) if available, falling back
|
|
46
|
-
* to the individual PID for environments where group-kill is unavailable.
|
|
47
|
-
*/
|
|
48
|
-
export function registerProcessGroupCleanup(pid) {
|
|
49
|
-
const fn = () => {
|
|
50
|
-
try {
|
|
51
|
-
process.kill(-pid, "SIGKILL");
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Process group may not exist (process already exited or pid unavailable).
|
|
55
|
-
try {
|
|
56
|
-
process.kill(pid, "SIGKILL");
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
/* already gone */
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
return registerCleanup(fn);
|
|
64
|
-
}
|
|
65
|
-
const registry = {
|
|
66
|
-
fns: new Set(),
|
|
67
|
-
installed: false,
|
|
68
|
-
running: false,
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Register a cleanup function. Returns a deregister thunk that removes the
|
|
72
|
-
* function from the registry. Calling deregister after the function has
|
|
73
|
-
* already run is a no-op.
|
|
74
|
-
*/
|
|
75
|
-
export function registerCleanup(fn) {
|
|
76
|
-
registry.fns.add(fn);
|
|
77
|
-
installSignalHandlers();
|
|
78
|
-
return () => {
|
|
79
|
-
registry.fns.delete(fn);
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
/** GC threshold for orphan bench tmp dirs: 6 hours in milliseconds. */
|
|
83
|
-
const BENCH_TMP_GC_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
|
84
|
-
/**
|
|
85
|
-
* Sweep `${AKM_CACHE_DIR}/bench/*` entries whose mtime is older than 6h.
|
|
86
|
-
* Best-effort: any individual rmSync failure is swallowed (warned in
|
|
87
|
-
* verbose mode) so a permission-bound entry does not kill the install.
|
|
88
|
-
*
|
|
89
|
-
* Idempotent because it only runs from the first-installer path in
|
|
90
|
-
* `installSignalHandlers` — gated by `registry.installed`.
|
|
91
|
-
*/
|
|
92
|
-
function gcOrphanBenchTmp() {
|
|
93
|
-
let root;
|
|
94
|
-
try {
|
|
95
|
-
root = benchTmpRoot();
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// If the cache dir cannot be resolved (e.g. HOME unset in a sandboxed
|
|
99
|
-
// CI shell), skip GC silently — the bench will fail later on its own.
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
let entries;
|
|
103
|
-
try {
|
|
104
|
-
entries = fs.readdirSync(root);
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
// Root does not yet exist or is unreadable — nothing to reap.
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const cutoff = Date.now() - BENCH_TMP_GC_MAX_AGE_MS;
|
|
111
|
-
for (const name of entries) {
|
|
112
|
-
const full = path.join(root, name);
|
|
113
|
-
let stat;
|
|
114
|
-
try {
|
|
115
|
-
stat = fs.lstatSync(full);
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
if (stat.mtimeMs > cutoff)
|
|
121
|
-
continue;
|
|
122
|
-
try {
|
|
123
|
-
fs.rmSync(full, { recursive: true, force: true });
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
warn(`bench tmp GC: could not remove ${full}: ${err.message}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function installSignalHandlers() {
|
|
131
|
-
if (registry.installed)
|
|
132
|
-
return;
|
|
133
|
-
registry.installed = true;
|
|
134
|
-
// First-installer GC sweep: reap orphan bench tmp dirs older than 6h.
|
|
135
|
-
// Subsequent registerCleanup() calls never re-trigger this — the
|
|
136
|
-
// `registry.installed` guard above ensures install-once semantics.
|
|
137
|
-
gcOrphanBenchTmp();
|
|
138
|
-
const handler = () => {
|
|
139
|
-
// Re-entrant signals are dropped — a second Ctrl-C will hit our
|
|
140
|
-
// already-removed listeners and the runtime's default handler will
|
|
141
|
-
// force-exit. That is the documented escape hatch.
|
|
142
|
-
if (registry.running)
|
|
143
|
-
return;
|
|
144
|
-
registry.running = true;
|
|
145
|
-
// Snapshot then drop registrations. We invoke synchronously where
|
|
146
|
-
// possible; async fns get fired-and-forget but we still await them so
|
|
147
|
-
// the exit doesn't beat the rmdir on slow filesystems.
|
|
148
|
-
const fns = [...registry.fns];
|
|
149
|
-
registry.fns.clear();
|
|
150
|
-
void runAllAndExit(fns);
|
|
151
|
-
};
|
|
152
|
-
registry.handlerSigint = handler;
|
|
153
|
-
registry.handlerSigterm = handler;
|
|
154
|
-
process.on("SIGINT", handler);
|
|
155
|
-
process.on("SIGTERM", handler);
|
|
156
|
-
}
|
|
157
|
-
async function runAllAndExit(fns) {
|
|
158
|
-
// BUG-H5: wrap the body in try/finally so a synchronous throw outside the
|
|
159
|
-
// per-fn try/catch (e.g. an exception thrown by `process.off` on a
|
|
160
|
-
// pathological listener list) does not leave `registry.running = true`.
|
|
161
|
-
// Without this guard, a subsequent registerCleanup() call would re-install
|
|
162
|
-
// listeners but the new handler would short-circuit on the stale flag and
|
|
163
|
-
// skip cleanup on the next signal.
|
|
164
|
-
try {
|
|
165
|
-
for (const fn of fns) {
|
|
166
|
-
try {
|
|
167
|
-
await fn();
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
// Best-effort: cleanup must never throw out of the signal path.
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// Remove our listeners so a second Ctrl-C force-exits via the default.
|
|
174
|
-
if (registry.handlerSigint)
|
|
175
|
-
process.off("SIGINT", registry.handlerSigint);
|
|
176
|
-
if (registry.handlerSigterm)
|
|
177
|
-
process.off("SIGTERM", registry.handlerSigterm);
|
|
178
|
-
registry.installed = false;
|
|
179
|
-
registry.handlerSigint = undefined;
|
|
180
|
-
registry.handlerSigterm = undefined;
|
|
181
|
-
}
|
|
182
|
-
finally {
|
|
183
|
-
registry.running = false;
|
|
184
|
-
// 128 + SIGINT(2) — POSIX convention for signal-induced exits.
|
|
185
|
-
process.exit(130);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// ── Test-only seam ──────────────────────────────────────────────────────────
|
|
189
|
-
/**
|
|
190
|
-
* Test-only: drive the cleanup path as if a signal arrived, *without*
|
|
191
|
-
* calling `process.exit`. Returns a promise that resolves once every
|
|
192
|
-
* registered fn has settled. Used by the unit test to assert ordering
|
|
193
|
-
* without killing the test process.
|
|
194
|
-
*
|
|
195
|
-
* Resets the registry to an uninstalled state on completion so subsequent
|
|
196
|
-
* tests can re-install handlers cleanly.
|
|
197
|
-
*/
|
|
198
|
-
export async function _drainForTest() {
|
|
199
|
-
const fns = [...registry.fns];
|
|
200
|
-
registry.fns.clear();
|
|
201
|
-
registry.running = true;
|
|
202
|
-
for (const fn of fns) {
|
|
203
|
-
try {
|
|
204
|
-
await fn();
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
/* swallow */
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (registry.handlerSigint)
|
|
211
|
-
process.off("SIGINT", registry.handlerSigint);
|
|
212
|
-
if (registry.handlerSigterm)
|
|
213
|
-
process.off("SIGTERM", registry.handlerSigterm);
|
|
214
|
-
registry.installed = false;
|
|
215
|
-
registry.running = false;
|
|
216
|
-
registry.handlerSigint = undefined;
|
|
217
|
-
registry.handlerSigterm = undefined;
|
|
218
|
-
}
|
|
219
|
-
/** Test-only: reset the registry without firing cleanups (for unit setup). */
|
|
220
|
-
export function _resetForTest() {
|
|
221
|
-
registry.fns.clear();
|
|
222
|
-
if (registry.handlerSigint)
|
|
223
|
-
process.off("SIGINT", registry.handlerSigint);
|
|
224
|
-
if (registry.handlerSigterm)
|
|
225
|
-
process.off("SIGTERM", registry.handlerSigterm);
|
|
226
|
-
registry.installed = false;
|
|
227
|
-
registry.running = false;
|
|
228
|
-
registry.handlerSigint = undefined;
|
|
229
|
-
registry.handlerSigterm = undefined;
|
|
230
|
-
}
|
|
231
|
-
/** Test-only: peek at the current registration count. */
|
|
232
|
-
export function _registeredCountForTest() {
|
|
233
|
-
return registry.fns.size;
|
|
234
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the bench cleanup registry (#267).
|
|
3
|
-
*
|
|
4
|
-
* The shared registry installs ONE pair of SIGINT/SIGTERM handlers on first
|
|
5
|
-
* registration and runs every registered fn when a signal fires. We use the
|
|
6
|
-
* `_drainForTest` test seam so the assertions don't have to actually kill
|
|
7
|
-
* the test process — but we also exercise the real handler installation
|
|
8
|
-
* path to make sure it's idempotent.
|
|
9
|
-
*/
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
-
import fs from "node:fs";
|
|
12
|
-
import { _drainForTest, _registeredCountForTest, _resetForTest, registerCleanup } from "./cleanup";
|
|
13
|
-
import { benchMkdtemp, benchTmpRoot } from "./tmp";
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
_resetForTest();
|
|
16
|
-
});
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
_resetForTest();
|
|
19
|
-
});
|
|
20
|
-
describe("registerCleanup (#267)", () => {
|
|
21
|
-
test("registers a cleanup fn and increments the count", () => {
|
|
22
|
-
expect(_registeredCountForTest()).toBe(0);
|
|
23
|
-
registerCleanup(() => { });
|
|
24
|
-
expect(_registeredCountForTest()).toBe(1);
|
|
25
|
-
});
|
|
26
|
-
test("returns a deregister thunk that drops the registration", () => {
|
|
27
|
-
const deregister = registerCleanup(() => { });
|
|
28
|
-
expect(_registeredCountForTest()).toBe(1);
|
|
29
|
-
deregister();
|
|
30
|
-
expect(_registeredCountForTest()).toBe(0);
|
|
31
|
-
});
|
|
32
|
-
test("drainForTest runs every registered cleanup once", async () => {
|
|
33
|
-
const calls = [];
|
|
34
|
-
registerCleanup(() => {
|
|
35
|
-
calls.push("a");
|
|
36
|
-
});
|
|
37
|
-
registerCleanup(() => {
|
|
38
|
-
calls.push("b");
|
|
39
|
-
});
|
|
40
|
-
registerCleanup(() => {
|
|
41
|
-
calls.push("c");
|
|
42
|
-
});
|
|
43
|
-
await _drainForTest();
|
|
44
|
-
expect(calls.sort()).toEqual(["a", "b", "c"]);
|
|
45
|
-
// Registry is empty after drain.
|
|
46
|
-
expect(_registeredCountForTest()).toBe(0);
|
|
47
|
-
});
|
|
48
|
-
test("drainForTest swallows errors so one bad fn doesn't block the rest", async () => {
|
|
49
|
-
const calls = [];
|
|
50
|
-
registerCleanup(() => {
|
|
51
|
-
calls.push("first");
|
|
52
|
-
});
|
|
53
|
-
registerCleanup(() => {
|
|
54
|
-
throw new Error("boom");
|
|
55
|
-
});
|
|
56
|
-
registerCleanup(() => {
|
|
57
|
-
calls.push("third");
|
|
58
|
-
});
|
|
59
|
-
await _drainForTest();
|
|
60
|
-
expect(calls.sort()).toEqual(["first", "third"]);
|
|
61
|
-
});
|
|
62
|
-
test("drainForTest awaits async cleanup fns", async () => {
|
|
63
|
-
const calls = [];
|
|
64
|
-
registerCleanup(async () => {
|
|
65
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
66
|
-
calls.push("async-done");
|
|
67
|
-
});
|
|
68
|
-
await _drainForTest();
|
|
69
|
-
expect(calls).toEqual(["async-done"]);
|
|
70
|
-
});
|
|
71
|
-
test("idempotent installer: repeated registers do not multiply listeners", () => {
|
|
72
|
-
const initialSigint = process.listenerCount("SIGINT");
|
|
73
|
-
const initialSigterm = process.listenerCount("SIGTERM");
|
|
74
|
-
registerCleanup(() => { });
|
|
75
|
-
registerCleanup(() => { });
|
|
76
|
-
registerCleanup(() => { });
|
|
77
|
-
// Exactly one listener each, no matter how many cleanup fns we add.
|
|
78
|
-
expect(process.listenerCount("SIGINT")).toBe(initialSigint + 1);
|
|
79
|
-
expect(process.listenerCount("SIGTERM")).toBe(initialSigterm + 1);
|
|
80
|
-
});
|
|
81
|
-
test("deregister-all leaves the listeners installed (registry is sticky)", () => {
|
|
82
|
-
// The contract is: once installed, the handlers stay installed for the
|
|
83
|
-
// process lifetime. Subsequent register calls reuse them.
|
|
84
|
-
registerCleanup(() => { })();
|
|
85
|
-
expect(_registeredCountForTest()).toBe(0);
|
|
86
|
-
// Re-register: this MUST NOT add a second pair of listeners.
|
|
87
|
-
const initialSigint = process.listenerCount("SIGINT");
|
|
88
|
-
registerCleanup(() => { });
|
|
89
|
-
expect(process.listenerCount("SIGINT")).toBe(initialSigint);
|
|
90
|
-
});
|
|
91
|
-
test("deregistered fns do not run on drain", async () => {
|
|
92
|
-
const calls = [];
|
|
93
|
-
const dereg = registerCleanup(() => {
|
|
94
|
-
calls.push("kept");
|
|
95
|
-
});
|
|
96
|
-
const _wasted = registerCleanup(() => {
|
|
97
|
-
calls.push("dropped");
|
|
98
|
-
});
|
|
99
|
-
_wasted();
|
|
100
|
-
void dereg; // keep referenced
|
|
101
|
-
await _drainForTest();
|
|
102
|
-
expect(calls).toEqual(["kept"]);
|
|
103
|
-
});
|
|
104
|
-
test("simulated SIGINT path: handler runs cleanup before exit (via drain seam)", async () => {
|
|
105
|
-
// We can't actually `process.exit(130)` inside a unit test, so we use the
|
|
106
|
-
// drain seam (which mirrors the runAllAndExit path minus the exit call).
|
|
107
|
-
// This verifies the OBSERVABLE behaviour the brief asked for: signal →
|
|
108
|
-
// every registered cleanup fn ran.
|
|
109
|
-
const ran = [];
|
|
110
|
-
registerCleanup(() => {
|
|
111
|
-
ran.push("rmsync(workspace)");
|
|
112
|
-
});
|
|
113
|
-
registerCleanup(() => {
|
|
114
|
-
ran.push("rmsync(stash)");
|
|
115
|
-
});
|
|
116
|
-
await _drainForTest();
|
|
117
|
-
expect(ran.length).toBe(2);
|
|
118
|
-
expect(ran).toContain("rmsync(workspace)");
|
|
119
|
-
expect(ran).toContain("rmsync(stash)");
|
|
120
|
-
});
|
|
121
|
-
test("first registerCleanup sweeps bench tmp entries older than 6h (#276)", () => {
|
|
122
|
-
// Ensure root exists before populating.
|
|
123
|
-
benchTmpRoot();
|
|
124
|
-
// Stale entry: mtime backdated 7h.
|
|
125
|
-
const stale = benchMkdtemp("akm-bench-gc-stale-");
|
|
126
|
-
const sevenHoursAgo = (Date.now() - 7 * 60 * 60 * 1000) / 1000;
|
|
127
|
-
fs.utimesSync(stale, sevenHoursAgo, sevenHoursAgo);
|
|
128
|
-
// Fresh entry: untouched mtime (now).
|
|
129
|
-
const fresh = benchMkdtemp("akm-bench-gc-fresh-");
|
|
130
|
-
expect(fs.existsSync(stale)).toBe(true);
|
|
131
|
-
expect(fs.existsSync(fresh)).toBe(true);
|
|
132
|
-
// Trigger first-installer GC.
|
|
133
|
-
registerCleanup(() => { });
|
|
134
|
-
expect(fs.existsSync(stale)).toBe(false);
|
|
135
|
-
expect(fs.existsSync(fresh)).toBe(true);
|
|
136
|
-
// Cleanup the fresh entry ourselves.
|
|
137
|
-
fs.rmSync(fresh, { recursive: true, force: true });
|
|
138
|
-
});
|
|
139
|
-
test("GC is idempotent — second registerCleanup does not re-sweep", () => {
|
|
140
|
-
// Install once with a sentinel registered.
|
|
141
|
-
registerCleanup(() => { });
|
|
142
|
-
// Now create a stale entry AFTER install. Second registerCleanup must
|
|
143
|
-
// NOT sweep it because the GC only runs on first install.
|
|
144
|
-
const stale = benchMkdtemp("akm-bench-gc-postinstall-");
|
|
145
|
-
const sevenHoursAgo = (Date.now() - 7 * 60 * 60 * 1000) / 1000;
|
|
146
|
-
fs.utimesSync(stale, sevenHoursAgo, sevenHoursAgo);
|
|
147
|
-
registerCleanup(() => { });
|
|
148
|
-
expect(fs.existsSync(stale)).toBe(true);
|
|
149
|
-
fs.rmSync(stale, { recursive: true, force: true });
|
|
150
|
-
});
|
|
151
|
-
test("re-entrant signals during running cleanup are dropped", async () => {
|
|
152
|
-
// Drive the registered handler twice in sequence. The second drain
|
|
153
|
-
// should observe the cleared registry (running flag flipped) and run
|
|
154
|
-
// nothing extra. This protects against a Ctrl-C double-press
|
|
155
|
-
// interrupting cleanup mid-run.
|
|
156
|
-
const calls = [];
|
|
157
|
-
registerCleanup(() => {
|
|
158
|
-
calls.push("first");
|
|
159
|
-
});
|
|
160
|
-
await _drainForTest();
|
|
161
|
-
expect(calls).toEqual(["first"]);
|
|
162
|
-
// No further fns registered → second drain is a no-op.
|
|
163
|
-
await _drainForTest();
|
|
164
|
-
expect(calls).toEqual(["first"]);
|
|
165
|
-
});
|
|
166
|
-
});
|