ppef 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/bin/ppef.mjs +20 -0
- package/dist/__tests__/framework-pipeline.integration.test.d.ts +7 -0
- package/dist/__tests__/framework-pipeline.integration.test.d.ts.map +1 -0
- package/dist/__tests__/framework-pipeline.integration.test.js +415 -0
- package/dist/__tests__/framework-pipeline.integration.test.js.map +1 -0
- package/dist/__tests__/index-exports.unit.test.d.ts +8 -0
- package/dist/__tests__/index-exports.unit.test.d.ts.map +1 -0
- package/dist/__tests__/index-exports.unit.test.js +127 -0
- package/dist/__tests__/index-exports.unit.test.js.map +1 -0
- package/dist/__tests__/registry-executor.integration.test.d.ts +5 -0
- package/dist/__tests__/registry-executor.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-executor.integration.test.js +352 -0
- package/dist/__tests__/registry-executor.integration.test.js.map +1 -0
- package/dist/__tests__/test-helpers.d.ts +94 -0
- package/dist/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/__tests__/test-helpers.js +271 -0
- package/dist/__tests__/test-helpers.js.map +1 -0
- package/dist/aggregation/__tests__/aggregators.unit.test.d.ts +7 -0
- package/dist/aggregation/__tests__/aggregators.unit.test.d.ts.map +1 -0
- package/dist/aggregation/__tests__/aggregators.unit.test.js +350 -0
- package/dist/aggregation/__tests__/aggregators.unit.test.js.map +1 -0
- package/dist/aggregation/__tests__/pipeline.unit.test.d.ts +7 -0
- package/dist/aggregation/__tests__/pipeline.unit.test.d.ts.map +1 -0
- package/dist/aggregation/__tests__/pipeline.unit.test.js +213 -0
- package/dist/aggregation/__tests__/pipeline.unit.test.js.map +1 -0
- package/dist/aggregation/aggregators.d.ts +63 -0
- package/dist/aggregation/aggregators.d.ts.map +1 -0
- package/dist/aggregation/aggregators.js +228 -0
- package/dist/aggregation/aggregators.js.map +1 -0
- package/dist/aggregation/index.d.ts +8 -0
- package/dist/aggregation/index.d.ts.map +1 -0
- package/dist/aggregation/index.js +8 -0
- package/dist/aggregation/index.js.map +1 -0
- package/dist/aggregation/pipeline.d.ts +38 -0
- package/dist/aggregation/pipeline.d.ts.map +1 -0
- package/dist/aggregation/pipeline.js +198 -0
- package/dist/aggregation/pipeline.js.map +1 -0
- package/dist/claims/__tests__/evaluator.unit.test.d.ts +12 -0
- package/dist/claims/__tests__/evaluator.unit.test.d.ts.map +1 -0
- package/dist/claims/__tests__/evaluator.unit.test.js +801 -0
- package/dist/claims/__tests__/evaluator.unit.test.js.map +1 -0
- package/dist/claims/evaluator.d.ts +33 -0
- package/dist/claims/evaluator.d.ts.map +1 -0
- package/dist/claims/evaluator.js +174 -0
- package/dist/claims/evaluator.js.map +1 -0
- package/dist/claims/index.d.ts +7 -0
- package/dist/claims/index.d.ts.map +1 -0
- package/dist/claims/index.js +7 -0
- package/dist/claims/index.js.map +1 -0
- package/dist/cli/__tests__/aggregate.command.unit.test.d.ts +7 -0
- package/dist/cli/__tests__/aggregate.command.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/aggregate.command.unit.test.js +396 -0
- package/dist/cli/__tests__/aggregate.command.unit.test.js.map +1 -0
- package/dist/cli/__tests__/commands.unit.test.d.ts +10 -0
- package/dist/cli/__tests__/commands.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/commands.unit.test.js +217 -0
- package/dist/cli/__tests__/commands.unit.test.js.map +1 -0
- package/dist/cli/__tests__/index.unit.test.d.ts +10 -0
- package/dist/cli/__tests__/index.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/index.unit.test.js +65 -0
- package/dist/cli/__tests__/index.unit.test.js.map +1 -0
- package/dist/cli/__tests__/logger.unit.test.d.ts +11 -0
- package/dist/cli/__tests__/logger.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/logger.unit.test.js +180 -0
- package/dist/cli/__tests__/logger.unit.test.js.map +1 -0
- package/dist/cli/__tests__/module-loader.unit.test.d.ts +11 -0
- package/dist/cli/__tests__/module-loader.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/module-loader.unit.test.js +262 -0
- package/dist/cli/__tests__/module-loader.unit.test.js.map +1 -0
- package/dist/cli/__tests__/output-writer.unit.test.d.ts +10 -0
- package/dist/cli/__tests__/output-writer.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/output-writer.unit.test.js +216 -0
- package/dist/cli/__tests__/output-writer.unit.test.js.map +1 -0
- package/dist/cli/__tests__/plan.command.unit.test.d.ts +7 -0
- package/dist/cli/__tests__/plan.command.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/plan.command.unit.test.js +289 -0
- package/dist/cli/__tests__/plan.command.unit.test.js.map +1 -0
- package/dist/cli/__tests__/run.command.unit.test.d.ts +7 -0
- package/dist/cli/__tests__/run.command.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/run.command.unit.test.js +422 -0
- package/dist/cli/__tests__/run.command.unit.test.js.map +1 -0
- package/dist/cli/__tests__/validate.command.unit.test.d.ts +7 -0
- package/dist/cli/__tests__/validate.command.unit.test.d.ts.map +1 -0
- package/dist/cli/__tests__/validate.command.unit.test.js +226 -0
- package/dist/cli/__tests__/validate.command.unit.test.js.map +1 -0
- package/dist/cli/command-deps.d.ts +125 -0
- package/dist/cli/command-deps.d.ts.map +1 -0
- package/dist/cli/command-deps.js +7 -0
- package/dist/cli/command-deps.js.map +1 -0
- package/dist/cli/commands/aggregate.d.ts +35 -0
- package/dist/cli/commands/aggregate.d.ts.map +1 -0
- package/dist/cli/commands/aggregate.js +121 -0
- package/dist/cli/commands/aggregate.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +36 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +109 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/run.d.ts +33 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +185 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +27 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +88 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/config-loader.d.ts +30 -0
- package/dist/cli/config-loader.d.ts.map +1 -0
- package/dist/cli/config-loader.js +181 -0
- package/dist/cli/config-loader.js.map +1 -0
- package/dist/cli/index.d.ts +26 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +58 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/logger.d.ts +75 -0
- package/dist/cli/logger.d.ts.map +1 -0
- package/dist/cli/logger.js +131 -0
- package/dist/cli/logger.js.map +1 -0
- package/dist/cli/module-loader.d.ts +46 -0
- package/dist/cli/module-loader.d.ts.map +1 -0
- package/dist/cli/module-loader.js +116 -0
- package/dist/cli/module-loader.js.map +1 -0
- package/dist/cli/output-writer.d.ts +51 -0
- package/dist/cli/output-writer.d.ts.map +1 -0
- package/dist/cli/output-writer.js +65 -0
- package/dist/cli/output-writer.js.map +1 -0
- package/dist/cli/types.d.ts +174 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +7 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/collector/__tests__/result-collector.unit.test.d.ts +7 -0
- package/dist/collector/__tests__/result-collector.unit.test.d.ts.map +1 -0
- package/dist/collector/__tests__/result-collector.unit.test.js +1021 -0
- package/dist/collector/__tests__/result-collector.unit.test.js.map +1 -0
- package/dist/collector/__tests__/schema.unit.test.d.ts +7 -0
- package/dist/collector/__tests__/schema.unit.test.d.ts.map +1 -0
- package/dist/collector/__tests__/schema.unit.test.js +360 -0
- package/dist/collector/__tests__/schema.unit.test.js.map +1 -0
- package/dist/collector/index.d.ts +8 -0
- package/dist/collector/index.d.ts.map +1 -0
- package/dist/collector/index.js +8 -0
- package/dist/collector/index.js.map +1 -0
- package/dist/collector/result-collector.d.ts +159 -0
- package/dist/collector/result-collector.d.ts.map +1 -0
- package/dist/collector/result-collector.js +213 -0
- package/dist/collector/result-collector.js.map +1 -0
- package/dist/collector/schema.d.ts +34 -0
- package/dist/collector/schema.d.ts.map +1 -0
- package/dist/collector/schema.js +145 -0
- package/dist/collector/schema.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.d.ts +10 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.js +122 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.d.ts +7 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.js +330 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.js +531 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.d.ts +8 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.js +493 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.d.ts +8 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.js +164 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.js +490 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.js.map +1 -0
- package/dist/executor/__tests__/executor.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/executor.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/executor.unit.test.js +202 -0
- package/dist/executor/__tests__/executor.unit.test.js.map +1 -0
- package/dist/executor/__tests__/memory-monitor.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/memory-monitor.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/memory-monitor.unit.test.js +285 -0
- package/dist/executor/__tests__/memory-monitor.unit.test.js.map +1 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.d.ts +12 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.js +196 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.js.map +1 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.d.ts +7 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.js +249 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.js.map +1 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.d.ts +8 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.js +473 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.js.map +1 -0
- package/dist/executor/__tests__/run-id.unit.test.d.ts +8 -0
- package/dist/executor/__tests__/run-id.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/run-id.unit.test.js +156 -0
- package/dist/executor/__tests__/run-id.unit.test.js.map +1 -0
- package/dist/executor/__tests__/worker-entry.integration.test.d.ts +24 -0
- package/dist/executor/__tests__/worker-entry.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/worker-entry.integration.test.js +82 -0
- package/dist/executor/__tests__/worker-entry.integration.test.js.map +1 -0
- package/dist/executor/__tests__/worker-entry.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/worker-entry.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/worker-entry.unit.test.js +364 -0
- package/dist/executor/__tests__/worker-entry.unit.test.js.map +1 -0
- package/dist/executor/checkpoint-manager.d.ts +231 -0
- package/dist/executor/checkpoint-manager.d.ts.map +1 -0
- package/dist/executor/checkpoint-manager.js +395 -0
- package/dist/executor/checkpoint-manager.js.map +1 -0
- package/dist/executor/checkpoint-storage.d.ts +230 -0
- package/dist/executor/checkpoint-storage.d.ts.map +1 -0
- package/dist/executor/checkpoint-storage.js +370 -0
- package/dist/executor/checkpoint-storage.js.map +1 -0
- package/dist/executor/checkpoint-types.d.ts +48 -0
- package/dist/executor/checkpoint-types.d.ts.map +1 -0
- package/dist/executor/checkpoint-types.js +8 -0
- package/dist/executor/checkpoint-types.js.map +1 -0
- package/dist/executor/executor.d.ts +164 -0
- package/dist/executor/executor.d.ts.map +1 -0
- package/dist/executor/executor.js +408 -0
- package/dist/executor/executor.js.map +1 -0
- package/dist/executor/index.d.ts +11 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +11 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/memory-monitor.d.ts +115 -0
- package/dist/executor/memory-monitor.d.ts.map +1 -0
- package/dist/executor/memory-monitor.js +168 -0
- package/dist/executor/memory-monitor.js.map +1 -0
- package/dist/executor/parallel-executor.d.ts +239 -0
- package/dist/executor/parallel-executor.d.ts.map +1 -0
- package/dist/executor/parallel-executor.js +329 -0
- package/dist/executor/parallel-executor.js.map +1 -0
- package/dist/executor/run-id.d.ts +71 -0
- package/dist/executor/run-id.d.ts.map +1 -0
- package/dist/executor/run-id.js +74 -0
- package/dist/executor/run-id.js.map +1 -0
- package/dist/executor/worker-entry.d.ts +10 -0
- package/dist/executor/worker-entry.d.ts.map +1 -0
- package/dist/executor/worker-entry.js +42 -0
- package/dist/executor/worker-entry.js.map +1 -0
- package/dist/executor/worker-executor.d.ts +156 -0
- package/dist/executor/worker-executor.d.ts.map +1 -0
- package/dist/executor/worker-executor.js +88 -0
- package/dist/executor/worker-executor.js.map +1 -0
- package/dist/index.cjs +11 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/case-registry.d.ts +113 -0
- package/dist/registry/case-registry.d.ts.map +1 -0
- package/dist/registry/case-registry.js +160 -0
- package/dist/registry/case-registry.js.map +1 -0
- package/dist/registry/index.d.ts +8 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/sut-registry.d.ts +96 -0
- package/dist/registry/sut-registry.d.ts.map +1 -0
- package/dist/registry/sut-registry.js +126 -0
- package/dist/registry/sut-registry.js.map +1 -0
- package/dist/renderers/index.d.ts +10 -0
- package/dist/renderers/index.d.ts.map +1 -0
- package/dist/renderers/index.js +9 -0
- package/dist/renderers/index.js.map +1 -0
- package/dist/renderers/latex-renderer.d.ts +84 -0
- package/dist/renderers/latex-renderer.d.ts.map +1 -0
- package/dist/renderers/latex-renderer.js +208 -0
- package/dist/renderers/latex-renderer.js.map +1 -0
- package/dist/renderers/types.d.ts +106 -0
- package/dist/renderers/types.d.ts.map +1 -0
- package/dist/renderers/types.js +23 -0
- package/dist/renderers/types.js.map +1 -0
- package/dist/robustness/__tests__/analyzer.unit.test.d.ts +11 -0
- package/dist/robustness/__tests__/analyzer.unit.test.d.ts.map +1 -0
- package/dist/robustness/__tests__/analyzer.unit.test.js +455 -0
- package/dist/robustness/__tests__/analyzer.unit.test.js.map +1 -0
- package/dist/robustness/__tests__/perturbations.unit.test.d.ts +11 -0
- package/dist/robustness/__tests__/perturbations.unit.test.d.ts.map +1 -0
- package/dist/robustness/__tests__/perturbations.unit.test.js +284 -0
- package/dist/robustness/__tests__/perturbations.unit.test.js.map +1 -0
- package/dist/robustness/analyzer.d.ts +61 -0
- package/dist/robustness/analyzer.d.ts.map +1 -0
- package/dist/robustness/analyzer.js +191 -0
- package/dist/robustness/analyzer.js.map +1 -0
- package/dist/robustness/index.d.ts +8 -0
- package/dist/robustness/index.d.ts.map +1 -0
- package/dist/robustness/index.js +8 -0
- package/dist/robustness/index.js.map +1 -0
- package/dist/robustness/perturbations.d.ts +46 -0
- package/dist/robustness/perturbations.d.ts.map +1 -0
- package/dist/robustness/perturbations.js +184 -0
- package/dist/robustness/perturbations.js.map +1 -0
- package/dist/statistical/__tests__/mann-whitney-u.unit.test.d.ts +7 -0
- package/dist/statistical/__tests__/mann-whitney-u.unit.test.d.ts.map +1 -0
- package/dist/statistical/__tests__/mann-whitney-u.unit.test.js +185 -0
- package/dist/statistical/__tests__/mann-whitney-u.unit.test.js.map +1 -0
- package/dist/statistical/index.d.ts +8 -0
- package/dist/statistical/index.d.ts.map +1 -0
- package/dist/statistical/index.js +8 -0
- package/dist/statistical/index.js.map +1 -0
- package/dist/statistical/mann-whitney-u.d.ts +62 -0
- package/dist/statistical/mann-whitney-u.d.ts.map +1 -0
- package/dist/statistical/mann-whitney-u.js +127 -0
- package/dist/statistical/mann-whitney-u.js.map +1 -0
- package/dist/types/aggregate.d.ts +124 -0
- package/dist/types/aggregate.d.ts.map +1 -0
- package/dist/types/aggregate.js +9 -0
- package/dist/types/aggregate.js.map +1 -0
- package/dist/types/case.d.ts +105 -0
- package/dist/types/case.d.ts.map +1 -0
- package/dist/types/case.js +10 -0
- package/dist/types/case.js.map +1 -0
- package/dist/types/claims.d.ts +122 -0
- package/dist/types/claims.d.ts.map +1 -0
- package/dist/types/claims.js +14 -0
- package/dist/types/claims.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/perturbation.d.ts +105 -0
- package/dist/types/perturbation.d.ts.map +1 -0
- package/dist/types/perturbation.js +9 -0
- package/dist/types/perturbation.js.map +1 -0
- package/dist/types/result.d.ts +150 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +12 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/sut.d.ts +128 -0
- package/dist/types/sut.d.ts.map +1 -0
- package/dist/types/sut.js +12 -0
- package/dist/types/sut.js.map +1 -0
- package/package.json +290 -7
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostic Unit Tests for Checkpoint Merge Bug
|
|
3
|
+
*
|
|
4
|
+
* Tests to reproduce the data loss bug where mergeShards() overwrites
|
|
5
|
+
* the main checkpoint instead of combining it with shard results.
|
|
6
|
+
*/
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { CheckpointManager } from "../checkpoint-manager.js";
|
|
14
|
+
import { FileStorage } from "../checkpoint-storage.js";
|
|
15
|
+
describe("Checkpoint Merge Bug Diagnostics", () => {
|
|
16
|
+
let testDir;
|
|
17
|
+
let mainCheckpoint;
|
|
18
|
+
let mainPath;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
testDir = join(tmpdir(), `checkpoint-merge-test-${randomBytes(8).toString("hex")}`);
|
|
21
|
+
mainPath = join(testDir, "checkpoint.json");
|
|
22
|
+
mainCheckpoint = new CheckpointManager({ storage: new FileStorage(mainPath) });
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Helper to read checkpoint data directly from file
|
|
26
|
+
* @param path
|
|
27
|
+
*/
|
|
28
|
+
const readCheckpointFile = (path) => {
|
|
29
|
+
const data = JSON.parse(readFileSync(path, "utf-8"));
|
|
30
|
+
return data;
|
|
31
|
+
};
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
34
|
+
});
|
|
35
|
+
it("diagnostic-1: mergeShards should combine main checkpoint with shards", async () => {
|
|
36
|
+
// Setup: Create main checkpoint with 3 completed runs
|
|
37
|
+
const run1 = createMockResult("run-001", "sut-1", "case-1");
|
|
38
|
+
const run2 = createMockResult("run-002", "sut-1", "case-2");
|
|
39
|
+
const run3 = createMockResult("run-003", "sut-1", "case-3");
|
|
40
|
+
await mainCheckpoint.saveIncremental(run1);
|
|
41
|
+
await mainCheckpoint.saveIncremental(run2);
|
|
42
|
+
await mainCheckpoint.saveIncremental(run3);
|
|
43
|
+
const mainData = readCheckpointFile(mainPath);
|
|
44
|
+
assert.strictEqual(mainData.completedRunIds.length, 3);
|
|
45
|
+
// Create worker shard with 1 additional run
|
|
46
|
+
const shardPath = join(testDir, "checkpoint-worker-00.json");
|
|
47
|
+
const shardStorage = new FileStorage(shardPath);
|
|
48
|
+
await shardStorage.save({
|
|
49
|
+
configHash: "test-hash",
|
|
50
|
+
createdAt: new Date().toISOString(),
|
|
51
|
+
updatedAt: new Date().toISOString(),
|
|
52
|
+
completedRunIds: ["run-004"],
|
|
53
|
+
results: {
|
|
54
|
+
"run-004": createMockResult("run-004", "sut-1", "case-4"),
|
|
55
|
+
},
|
|
56
|
+
totalPlanned: 4,
|
|
57
|
+
});
|
|
58
|
+
// Load checkpoint to reset internal state
|
|
59
|
+
await mainCheckpoint.load();
|
|
60
|
+
// BUG: mergeShards will lose the 3 runs from main checkpoint!
|
|
61
|
+
const merged = await mainCheckpoint.mergeShards([shardPath]);
|
|
62
|
+
const mainAfterMerge = readCheckpointFile(mainPath);
|
|
63
|
+
console.log("Main checkpoint runs:", mainAfterMerge.completedRunIds.length);
|
|
64
|
+
console.log("Merged runs:", merged.completedRunIds.length);
|
|
65
|
+
console.log("Expected: 4 runs (3 from main + 1 from shard)");
|
|
66
|
+
// This should be 4, but will fail showing the bug
|
|
67
|
+
assert.strictEqual(merged.completedRunIds.length, 4);
|
|
68
|
+
});
|
|
69
|
+
it("diagnostic-2: demonstrates mergeShards creates new checkpoint instead of combining", async () => {
|
|
70
|
+
// Setup main checkpoint with data
|
|
71
|
+
await mainCheckpoint.saveIncremental(createMockResult("main-001", "sut-1", "case-1"));
|
|
72
|
+
const mainBeforeMerge = readCheckpointFile(mainPath);
|
|
73
|
+
console.log("Main checkpoint before merge:", mainBeforeMerge.completedRunIds.length);
|
|
74
|
+
// Create empty shard (simulating worker that completed no runs)
|
|
75
|
+
const shardPath = join(testDir, "checkpoint-worker-00.json");
|
|
76
|
+
const shardStorage = new FileStorage(shardPath);
|
|
77
|
+
await shardStorage.save({
|
|
78
|
+
configHash: "test-hash",
|
|
79
|
+
createdAt: new Date().toISOString(),
|
|
80
|
+
updatedAt: new Date().toISOString(),
|
|
81
|
+
completedRunIds: [],
|
|
82
|
+
results: {},
|
|
83
|
+
totalPlanned: 132,
|
|
84
|
+
});
|
|
85
|
+
await mainCheckpoint.load();
|
|
86
|
+
// Merge empty shard
|
|
87
|
+
await mainCheckpoint.mergeShards([shardPath]);
|
|
88
|
+
const mainAfterMerge = readCheckpointFile(mainPath);
|
|
89
|
+
console.log("Main checkpoint after merge:", mainAfterMerge.completedRunIds.length);
|
|
90
|
+
// BUG: The main checkpoint's run is lost!
|
|
91
|
+
assert.ok(mainAfterMerge.completedRunIds.length > 0);
|
|
92
|
+
});
|
|
93
|
+
it("diagnostic-3: shows mergeShards doesn't preserve main checkpoint data", async () => {
|
|
94
|
+
// Create main checkpoint
|
|
95
|
+
await mainCheckpoint.saveIncremental(createMockResult("main-001", "sut-1", "case-1"));
|
|
96
|
+
await mainCheckpoint.saveIncremental(createMockResult("main-002", "sut-2", "case-2"));
|
|
97
|
+
const mainDataBefore = readCheckpointFile(mainPath);
|
|
98
|
+
const mainRuns = mainDataBefore.completedRunIds;
|
|
99
|
+
console.log("Main checkpoint has runs:", mainRuns);
|
|
100
|
+
// Create worker shard
|
|
101
|
+
const shardPath = join(testDir, "checkpoint-worker-00.json");
|
|
102
|
+
const shardStorage = new FileStorage(shardPath);
|
|
103
|
+
await shardStorage.save({
|
|
104
|
+
configHash: "test-hash",
|
|
105
|
+
createdAt: new Date().toISOString(),
|
|
106
|
+
updatedAt: new Date().toISOString(),
|
|
107
|
+
completedRunIds: ["shard-001"],
|
|
108
|
+
results: {
|
|
109
|
+
"shard-001": createMockResult("shard-001", "sut-3", "case-3"),
|
|
110
|
+
},
|
|
111
|
+
totalPlanned: 132,
|
|
112
|
+
});
|
|
113
|
+
// Reload main checkpoint
|
|
114
|
+
await mainCheckpoint.load();
|
|
115
|
+
// Merge
|
|
116
|
+
const merged = await mainCheckpoint.mergeShards([shardPath]);
|
|
117
|
+
const mainDataAfter = readCheckpointFile(mainPath);
|
|
118
|
+
console.log("After merge:");
|
|
119
|
+
console.log(" Main checkpoint runs:", mainDataAfter.completedRunIds);
|
|
120
|
+
console.log(" Merged runs:", merged.completedRunIds);
|
|
121
|
+
console.log(" Expected: 3 runs (2 from main + 1 from shard)");
|
|
122
|
+
console.log(" Actual:", merged.completedRunIds.length, "runs");
|
|
123
|
+
// Should have both main runs AND shard run
|
|
124
|
+
const expectedRunIds = new Set([...mainRuns, "shard-001"]);
|
|
125
|
+
const actualRunIds = new Set(merged.completedRunIds);
|
|
126
|
+
console.log(" Missing runs:", [...expectedRunIds].filter((x) => !actualRunIds.has(x)));
|
|
127
|
+
// Check all expected runs are present
|
|
128
|
+
for (const runId of expectedRunIds) {
|
|
129
|
+
assert.ok(actualRunIds.has(runId), `Expected run ${runId} to be in merged results`);
|
|
130
|
+
}
|
|
131
|
+
// Check counts match
|
|
132
|
+
assert.strictEqual(actualRunIds.size, expectedRunIds.size);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
/**
|
|
136
|
+
* Create a mock evaluation result for testing
|
|
137
|
+
* @param runId
|
|
138
|
+
* @param sut
|
|
139
|
+
* @param caseId
|
|
140
|
+
*/
|
|
141
|
+
const createMockResult = (runId, sut, caseId) => ({
|
|
142
|
+
run: {
|
|
143
|
+
runId,
|
|
144
|
+
sut,
|
|
145
|
+
sutRole: "primary",
|
|
146
|
+
sutVersion: "1.0.0",
|
|
147
|
+
caseId,
|
|
148
|
+
caseClass: "test-class",
|
|
149
|
+
seed: 42,
|
|
150
|
+
repetition: 0,
|
|
151
|
+
},
|
|
152
|
+
correctness: {
|
|
153
|
+
expectedExists: false,
|
|
154
|
+
producedOutput: true,
|
|
155
|
+
valid: true,
|
|
156
|
+
matchesExpected: null,
|
|
157
|
+
},
|
|
158
|
+
outputs: { summary: {} },
|
|
159
|
+
metrics: { numeric: { test: 1 } },
|
|
160
|
+
provenance: {
|
|
161
|
+
runtime: { platform: "linux", arch: "x64", nodeVersion: "v22.0.0" },
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=checkpoint-merge-bug.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-merge-bug.unit.test.js","sourceRoot":"","sources":["../../../src/executor/__tests__/checkpoint-merge-bug.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-storage.unit.test.d.ts","sourceRoot":"","sources":["../../../src/executor/__tests__/checkpoint-storage.unit.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CheckpointStorage
|
|
3
|
+
*
|
|
4
|
+
* Tests FileStorage, GitStorage, FileSystem, and Lock implementations.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, beforeEach } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { FileStorage, GitStorage, InMemoryLock, NodeFileSystem, createCheckpointStorage, getGitNamespace, } from "../checkpoint-storage.js";
|
|
9
|
+
/**
|
|
10
|
+
* Mock file system for testing.
|
|
11
|
+
* Handles both relative and absolute paths.
|
|
12
|
+
*/
|
|
13
|
+
class MockFileSystem {
|
|
14
|
+
data = new Map();
|
|
15
|
+
throwsOnRead = new Set();
|
|
16
|
+
throwsOnWrite = new Set();
|
|
17
|
+
// Try multiple possible path resolutions
|
|
18
|
+
resolvePath(path) {
|
|
19
|
+
// Direct match
|
|
20
|
+
if (this.data.has(path)) {
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
// Try with leading slash if not present
|
|
24
|
+
if (!path.startsWith("/") && this.data.has(`/${path}`)) {
|
|
25
|
+
return `/${path}`;
|
|
26
|
+
}
|
|
27
|
+
// Try without leading slash if present
|
|
28
|
+
if (path.startsWith("/") && this.data.has(path.slice(1))) {
|
|
29
|
+
return path.slice(1);
|
|
30
|
+
}
|
|
31
|
+
return path;
|
|
32
|
+
}
|
|
33
|
+
async readFile(path) {
|
|
34
|
+
// Try multiple path resolutions
|
|
35
|
+
const pathsToTry = [
|
|
36
|
+
path,
|
|
37
|
+
path.startsWith("/") ? path : `/${path}`,
|
|
38
|
+
path.startsWith("/") ? path.slice(1) : path,
|
|
39
|
+
];
|
|
40
|
+
for (const tryPath of pathsToTry) {
|
|
41
|
+
if (this.data.has(tryPath) && this.throwsOnRead.has(tryPath)) {
|
|
42
|
+
throw new Error(`Mock read error: ${tryPath}`);
|
|
43
|
+
}
|
|
44
|
+
const content = this.data.get(tryPath);
|
|
45
|
+
if (content !== undefined) {
|
|
46
|
+
return content;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`File not found: ${path}`);
|
|
50
|
+
}
|
|
51
|
+
async writeFile(path, content) {
|
|
52
|
+
// Store with multiple path variants
|
|
53
|
+
this.data.set(path, content);
|
|
54
|
+
if (!path.startsWith("/")) {
|
|
55
|
+
this.data.set(`/${path}`, content);
|
|
56
|
+
}
|
|
57
|
+
this.data.set(`/${path}`, content);
|
|
58
|
+
const resolvedPath = this.resolvePath(path) ?? path;
|
|
59
|
+
if (this.throwsOnWrite.has(resolvedPath)) {
|
|
60
|
+
throw new Error(`Mock write error: ${resolvedPath}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async mkdir(_path, _options) {
|
|
64
|
+
// No-op for mock
|
|
65
|
+
}
|
|
66
|
+
async unlink(path) {
|
|
67
|
+
// Delete all variants
|
|
68
|
+
this.data.delete(path);
|
|
69
|
+
if (!path.startsWith("/")) {
|
|
70
|
+
this.data.delete(`/${path}`);
|
|
71
|
+
}
|
|
72
|
+
this.data.delete(`/${path}`);
|
|
73
|
+
const resolvedPath = this.resolvePath(path) ?? path;
|
|
74
|
+
this.data.delete(resolvedPath);
|
|
75
|
+
}
|
|
76
|
+
async access(_path) {
|
|
77
|
+
// No-op for mock
|
|
78
|
+
}
|
|
79
|
+
async readdir(path) {
|
|
80
|
+
const resolvedPath = this.resolvePath(path) ?? path;
|
|
81
|
+
const prefix = resolvedPath.endsWith("/") ? resolvedPath : `${resolvedPath}/`;
|
|
82
|
+
return [...this.data.keys()]
|
|
83
|
+
.filter((k) => k.startsWith(prefix))
|
|
84
|
+
.map((k) => k.slice(prefix.length))
|
|
85
|
+
.filter((k) => !k.includes("/"));
|
|
86
|
+
}
|
|
87
|
+
setFile(path, content) {
|
|
88
|
+
// Store with multiple path variants for flexibility
|
|
89
|
+
this.data.set(path, content);
|
|
90
|
+
if (!path.startsWith("/")) {
|
|
91
|
+
this.data.set(`/${path}`, content);
|
|
92
|
+
}
|
|
93
|
+
// Also store with cwd prefix (common during tests)
|
|
94
|
+
this.data.set(`/${path}`, content);
|
|
95
|
+
}
|
|
96
|
+
getFile(path) {
|
|
97
|
+
const resolvedPath = this.resolvePath(path) ?? path;
|
|
98
|
+
// Check multiple path variants
|
|
99
|
+
if (this.data.has(resolvedPath)) {
|
|
100
|
+
return this.data.get(resolvedPath);
|
|
101
|
+
}
|
|
102
|
+
// Also try with the full resolved path if provided
|
|
103
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
104
|
+
if (this.data.has(normalizedPath)) {
|
|
105
|
+
return this.data.get(normalizedPath);
|
|
106
|
+
}
|
|
107
|
+
// Try stripping leading slash if it exists
|
|
108
|
+
const strippedPath = path.startsWith("/") ? path.slice(1) : path;
|
|
109
|
+
if (this.data.has(strippedPath)) {
|
|
110
|
+
return this.data.get(strippedPath);
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
clear() {
|
|
115
|
+
this.data.clear();
|
|
116
|
+
this.throwsOnRead.clear();
|
|
117
|
+
this.throwsOnWrite.clear();
|
|
118
|
+
}
|
|
119
|
+
setThrowsOnRead(path) {
|
|
120
|
+
this.throwsOnRead.add(path);
|
|
121
|
+
if (!path.startsWith("/")) {
|
|
122
|
+
this.throwsOnRead.add(`/${path}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
setThrowsOnWrite(path) {
|
|
126
|
+
this.throwsOnWrite.add(path);
|
|
127
|
+
if (!path.startsWith("/")) {
|
|
128
|
+
this.throwsOnWrite.add(`/${path}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
describe("FileSystem", () => {
|
|
133
|
+
describe("NodeFileSystem", () => {
|
|
134
|
+
it("should have all required methods", () => {
|
|
135
|
+
const fs = new NodeFileSystem();
|
|
136
|
+
assert.ok(fs.readFile instanceof Function);
|
|
137
|
+
assert.ok(fs.writeFile instanceof Function);
|
|
138
|
+
assert.ok(fs.mkdir instanceof Function);
|
|
139
|
+
assert.ok(fs.unlink instanceof Function);
|
|
140
|
+
assert.ok(fs.access instanceof Function);
|
|
141
|
+
assert.ok(fs.readdir instanceof Function);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe("MockFileSystem", () => {
|
|
145
|
+
let mockFs;
|
|
146
|
+
beforeEach(() => {
|
|
147
|
+
mockFs = new MockFileSystem();
|
|
148
|
+
});
|
|
149
|
+
it("should store and retrieve files", async () => {
|
|
150
|
+
mockFs.setFile("test.json", '{"test": true}');
|
|
151
|
+
const content = await mockFs.readFile("test.json");
|
|
152
|
+
assert.strictEqual(content, '{"test": true}');
|
|
153
|
+
});
|
|
154
|
+
it("should throw on missing file", async () => {
|
|
155
|
+
await assert.rejects(() => mockFs.readFile("missing.json"));
|
|
156
|
+
});
|
|
157
|
+
it("should write files", async () => {
|
|
158
|
+
await mockFs.writeFile("new.json", '{"new": true}');
|
|
159
|
+
assert.strictEqual(mockFs.getFile("new.json"), '{"new": true}');
|
|
160
|
+
});
|
|
161
|
+
it("should delete files", async () => {
|
|
162
|
+
mockFs.setFile("delete.json", '{"delete": true}');
|
|
163
|
+
await mockFs.unlink("delete.json");
|
|
164
|
+
assert.strictEqual(mockFs.getFile("delete.json"), undefined);
|
|
165
|
+
});
|
|
166
|
+
it("should list directory contents", async () => {
|
|
167
|
+
mockFs.setFile("dir/file1.json", "{}");
|
|
168
|
+
mockFs.setFile("dir/file2.json", "{}");
|
|
169
|
+
mockFs.setFile("other/file.txt", "{}");
|
|
170
|
+
const entries = await mockFs.readdir("dir");
|
|
171
|
+
assert.ok(entries.includes("file1.json"));
|
|
172
|
+
assert.ok(entries.includes("file2.json"));
|
|
173
|
+
assert.ok(!entries.includes("file.txt"));
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("Lock", () => {
|
|
178
|
+
describe("InMemoryLock", () => {
|
|
179
|
+
it("should allow concurrent acquisitions with queueing", async () => {
|
|
180
|
+
const lock = new InMemoryLock();
|
|
181
|
+
const results = [];
|
|
182
|
+
// First acquisition
|
|
183
|
+
void lock.acquire().then(async () => {
|
|
184
|
+
results.push(1);
|
|
185
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
186
|
+
lock.release();
|
|
187
|
+
});
|
|
188
|
+
// Second acquisition (should wait)
|
|
189
|
+
void lock.acquire().then(() => {
|
|
190
|
+
results.push(2);
|
|
191
|
+
lock.release();
|
|
192
|
+
});
|
|
193
|
+
// Third acquisition (should wait)
|
|
194
|
+
void lock.acquire().then(() => {
|
|
195
|
+
results.push(3);
|
|
196
|
+
lock.release();
|
|
197
|
+
});
|
|
198
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
199
|
+
assert.deepStrictEqual(results, [1, 2, 3]);
|
|
200
|
+
});
|
|
201
|
+
it("should release immediately when no waiters", () => {
|
|
202
|
+
const lock = new InMemoryLock();
|
|
203
|
+
lock.release(); // Should not throw
|
|
204
|
+
lock.release(); // Should not throw
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
describe("FileStorage", () => {
|
|
209
|
+
let mockFs;
|
|
210
|
+
let storage;
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
mockFs = new MockFileSystem();
|
|
213
|
+
storage = new FileStorage("test/checkpoint.json", mockFs);
|
|
214
|
+
});
|
|
215
|
+
/**
|
|
216
|
+
* Helper to set file content using the storage's resolved path.
|
|
217
|
+
* @param content
|
|
218
|
+
*/
|
|
219
|
+
const setStorageFile = (content) => {
|
|
220
|
+
const resolvedPath = storage.getPath();
|
|
221
|
+
mockFs.data.set(resolvedPath, content);
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Helper to get file content using the storage's resolved path.
|
|
225
|
+
*/
|
|
226
|
+
const getStorageFile = () => {
|
|
227
|
+
const resolvedPath = storage.getPath();
|
|
228
|
+
return mockFs.data.get(resolvedPath);
|
|
229
|
+
};
|
|
230
|
+
describe("load", () => {
|
|
231
|
+
it("should parse valid JSON checkpoint", async () => {
|
|
232
|
+
const checkpoint = {
|
|
233
|
+
configHash: "abc123",
|
|
234
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
235
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
236
|
+
completedRunIds: ["run1", "run2"],
|
|
237
|
+
results: {},
|
|
238
|
+
totalPlanned: 10,
|
|
239
|
+
};
|
|
240
|
+
setStorageFile(JSON.stringify(checkpoint));
|
|
241
|
+
const loaded = await storage.load();
|
|
242
|
+
assert.deepStrictEqual(loaded, checkpoint);
|
|
243
|
+
});
|
|
244
|
+
it("should return null for missing file", async () => {
|
|
245
|
+
const loaded = await storage.load();
|
|
246
|
+
assert.strictEqual(loaded, null);
|
|
247
|
+
});
|
|
248
|
+
it("should return null for invalid JSON", async () => {
|
|
249
|
+
setStorageFile("not json");
|
|
250
|
+
const loaded = await storage.load();
|
|
251
|
+
assert.strictEqual(loaded, null);
|
|
252
|
+
});
|
|
253
|
+
it("should return null on read error", async () => {
|
|
254
|
+
const resolvedPath = storage.getPath();
|
|
255
|
+
mockFs.setThrowsOnRead(resolvedPath);
|
|
256
|
+
const loaded = await storage.load();
|
|
257
|
+
assert.strictEqual(loaded, null);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
describe("save", () => {
|
|
261
|
+
it("should write checkpoint with updated timestamp", async () => {
|
|
262
|
+
const checkpoint = {
|
|
263
|
+
configHash: "abc123",
|
|
264
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
265
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
266
|
+
completedRunIds: ["run1"],
|
|
267
|
+
results: {},
|
|
268
|
+
totalPlanned: 10,
|
|
269
|
+
};
|
|
270
|
+
await storage.save(checkpoint);
|
|
271
|
+
const saved = getStorageFile();
|
|
272
|
+
assert.ok(saved);
|
|
273
|
+
const parsed = JSON.parse(saved);
|
|
274
|
+
assert.strictEqual(parsed.configHash, "abc123");
|
|
275
|
+
assert.notStrictEqual(parsed.updatedAt, "2024-01-01T00:00:00.000Z"); // Should be updated
|
|
276
|
+
});
|
|
277
|
+
it("should create parent directory via mkdir", async () => {
|
|
278
|
+
const checkpoint = {
|
|
279
|
+
configHash: "abc123",
|
|
280
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
281
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
282
|
+
completedRunIds: [],
|
|
283
|
+
results: {},
|
|
284
|
+
totalPlanned: 0,
|
|
285
|
+
};
|
|
286
|
+
await storage.save(checkpoint);
|
|
287
|
+
assert.ok(getStorageFile());
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
describe("exists", () => {
|
|
291
|
+
it("should return true for valid checkpoint file", async () => {
|
|
292
|
+
const checkpoint = {
|
|
293
|
+
configHash: "abc123",
|
|
294
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
295
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
296
|
+
completedRunIds: [],
|
|
297
|
+
results: {},
|
|
298
|
+
totalPlanned: 0,
|
|
299
|
+
};
|
|
300
|
+
setStorageFile(JSON.stringify(checkpoint));
|
|
301
|
+
const exists = await storage.exists();
|
|
302
|
+
assert.strictEqual(exists, true);
|
|
303
|
+
});
|
|
304
|
+
it("should return false for missing file", async () => {
|
|
305
|
+
const exists = await storage.exists();
|
|
306
|
+
assert.strictEqual(exists, false);
|
|
307
|
+
});
|
|
308
|
+
it("should return false for invalid JSON", async () => {
|
|
309
|
+
setStorageFile("not json");
|
|
310
|
+
const exists = await storage.exists();
|
|
311
|
+
assert.strictEqual(exists, false);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
describe("delete", () => {
|
|
315
|
+
it("should remove checkpoint file", async () => {
|
|
316
|
+
setStorageFile('{"test": true}');
|
|
317
|
+
await storage.delete();
|
|
318
|
+
assert.strictEqual(getStorageFile(), undefined);
|
|
319
|
+
});
|
|
320
|
+
it("should not throw when deleting missing file", async () => {
|
|
321
|
+
await assert.doesNotReject(() => storage.delete());
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
describe("getPath", () => {
|
|
325
|
+
it("should return absolute path", () => {
|
|
326
|
+
const path = storage.getPath();
|
|
327
|
+
assert.ok(path.includes("checkpoint.json"));
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
describe("findShards", () => {
|
|
331
|
+
it("should find all worker checkpoint files", async () => {
|
|
332
|
+
mockFs.setFile("results/execute/checkpoint-worker-00.json", "{}");
|
|
333
|
+
mockFs.setFile("results/execute/checkpoint-worker-01.json", "{}");
|
|
334
|
+
mockFs.setFile("results/execute/checkpoint-worker-02.json", "{}");
|
|
335
|
+
mockFs.setFile("results/execute/other.json", "{}");
|
|
336
|
+
const shards = await FileStorage.findShards("results/execute", mockFs);
|
|
337
|
+
assert.strictEqual(shards.length, 3);
|
|
338
|
+
assert.ok(shards[0].includes("checkpoint-worker-00.json"));
|
|
339
|
+
assert.ok(shards[1].includes("checkpoint-worker-01.json"));
|
|
340
|
+
assert.ok(shards[2].includes("checkpoint-worker-02.json"));
|
|
341
|
+
});
|
|
342
|
+
it("should sort shards by worker index", async () => {
|
|
343
|
+
mockFs.setFile("results/execute/checkpoint-worker-02.json", "{}");
|
|
344
|
+
mockFs.setFile("results/execute/checkpoint-worker-00.json", "{}");
|
|
345
|
+
mockFs.setFile("results/execute/checkpoint-worker-01.json", "{}");
|
|
346
|
+
const shards = await FileStorage.findShards("results/execute", mockFs);
|
|
347
|
+
assert.ok(shards[0].includes("checkpoint-worker-00.json"));
|
|
348
|
+
assert.ok(shards[1].includes("checkpoint-worker-01.json"));
|
|
349
|
+
assert.ok(shards[2].includes("checkpoint-worker-02.json"));
|
|
350
|
+
});
|
|
351
|
+
it("should return empty array when directory has no shards", async () => {
|
|
352
|
+
const shards = await FileStorage.findShards("results/execute", mockFs);
|
|
353
|
+
assert.deepStrictEqual(shards, []);
|
|
354
|
+
});
|
|
355
|
+
it("should return empty array when directory does not exist", async () => {
|
|
356
|
+
const shards = await FileStorage.findShards("nonexistent", mockFs);
|
|
357
|
+
assert.deepStrictEqual(shards, []);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
describe("shardPath", () => {
|
|
361
|
+
it("should generate zero-padded shard paths", () => {
|
|
362
|
+
const path0 = FileStorage.shardPath("results/execute", 0);
|
|
363
|
+
const path1 = FileStorage.shardPath("results/execute", 1);
|
|
364
|
+
const path10 = FileStorage.shardPath("results/execute", 10);
|
|
365
|
+
assert.ok(path0.includes("checkpoint-worker-00.json"));
|
|
366
|
+
assert.ok(path1.includes("checkpoint-worker-01.json"));
|
|
367
|
+
assert.ok(path10.includes("checkpoint-worker-10.json"));
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
describe("GitStorage", () => {
|
|
372
|
+
describe("load", () => {
|
|
373
|
+
it("should return null when git is not available", async () => {
|
|
374
|
+
const storage = new GitStorage("test-namespace", "/nonexistent");
|
|
375
|
+
const loaded = storage.load();
|
|
376
|
+
assert.strictEqual(loaded, null);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
describe("save", () => {
|
|
380
|
+
it("should not throw when git is not available", async () => {
|
|
381
|
+
const storage = new GitStorage("test-namespace", "/nonexistent");
|
|
382
|
+
const checkpoint = {
|
|
383
|
+
configHash: "abc123",
|
|
384
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
385
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
386
|
+
completedRunIds: [],
|
|
387
|
+
results: {},
|
|
388
|
+
totalPlanned: 0,
|
|
389
|
+
};
|
|
390
|
+
// Should not throw when git is not available
|
|
391
|
+
await assert.doesNotReject(() => storage.save(checkpoint));
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
describe("delete", () => {
|
|
395
|
+
it("should not throw when git is not available", () => {
|
|
396
|
+
const storage = new GitStorage("test-namespace", "/nonexistent");
|
|
397
|
+
// Should not throw when git is not available
|
|
398
|
+
assert.doesNotThrow(() => {
|
|
399
|
+
storage.delete();
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
describe("exists", () => {
|
|
404
|
+
it("should return false when git is not available", () => {
|
|
405
|
+
const storage = new GitStorage("test-namespace", "/nonexistent");
|
|
406
|
+
assert.strictEqual(storage.exists(), false);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
describe("listHistory", () => {
|
|
410
|
+
it("should return empty array when git is not available", () => {
|
|
411
|
+
const storage = new GitStorage("test-namespace", "/nonexistent");
|
|
412
|
+
const history = storage.listHistory();
|
|
413
|
+
assert.deepStrictEqual(history, []);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
describe("restoreFromCommit", () => {
|
|
417
|
+
it("should return null when git is not available", () => {
|
|
418
|
+
const storage = new GitStorage("test-namespace", "/nonexistent");
|
|
419
|
+
const restored = storage.restoreFromCommit("abc123");
|
|
420
|
+
assert.strictEqual(restored, null);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
describe("type", () => {
|
|
424
|
+
it("should have type 'git'", () => {
|
|
425
|
+
const storage = new GitStorage("test-namespace");
|
|
426
|
+
assert.strictEqual(storage.type, "git");
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
describe("createCheckpointStorage", () => {
|
|
431
|
+
describe("mode selection", () => {
|
|
432
|
+
it("should create FileStorage for 'file' mode", () => {
|
|
433
|
+
const storage = createCheckpointStorage("file", "results/checkpoint.json");
|
|
434
|
+
assert.ok(storage instanceof FileStorage);
|
|
435
|
+
assert.strictEqual(storage.type, "file");
|
|
436
|
+
});
|
|
437
|
+
it("should create GitStorage for 'git' mode", () => {
|
|
438
|
+
const storage = createCheckpointStorage("git", "results-execute");
|
|
439
|
+
assert.ok(storage instanceof GitStorage);
|
|
440
|
+
assert.strictEqual(storage.type, "git");
|
|
441
|
+
});
|
|
442
|
+
it("should create GitStorage for 'auto' mode in git directory", () => {
|
|
443
|
+
// In a git directory with commits (like this repo), 'auto' should create GitStorage
|
|
444
|
+
const storage = createCheckpointStorage("auto", "results-execute");
|
|
445
|
+
assert.ok(storage instanceof GitStorage);
|
|
446
|
+
assert.strictEqual(storage.type, "git");
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
describe("with repoRoot", () => {
|
|
450
|
+
it("should pass repoRoot to GitStorage", () => {
|
|
451
|
+
const storage = createCheckpointStorage("git", "results-execute", "/custom/root");
|
|
452
|
+
assert.ok(storage instanceof GitStorage);
|
|
453
|
+
assert.strictEqual(storage.type, "git");
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
describe("getGitNamespace", () => {
|
|
458
|
+
it("should convert forward slashes to hyphens", () => {
|
|
459
|
+
assert.strictEqual(getGitNamespace("results/execute"), "results-execute");
|
|
460
|
+
assert.strictEqual(getGitNamespace("a/b/c/d"), "a-b-c-d");
|
|
461
|
+
});
|
|
462
|
+
it("should convert backslashes to hyphens", () => {
|
|
463
|
+
assert.strictEqual(getGitNamespace("results\\execute"), "results-execute");
|
|
464
|
+
assert.strictEqual(getGitNamespace("a\\b\\c"), "a-b-c");
|
|
465
|
+
});
|
|
466
|
+
it("should handle leading dot (slash after dot becomes hyphen, then dot is removed)", () => {
|
|
467
|
+
// "./results" -> ".-results" (slash becomes hyphen) -> "-results" (dot removed)
|
|
468
|
+
assert.strictEqual(getGitNamespace("./results"), "-results");
|
|
469
|
+
// ".results" -> ".results" (no slashes) -> "results" (dot removed)
|
|
470
|
+
assert.strictEqual(getGitNamespace(".results"), "results");
|
|
471
|
+
});
|
|
472
|
+
it("should handle leading slashes (replaced with hyphens, kept because last step only removes slashes)", () => {
|
|
473
|
+
// "/results/execute" -> "-results-execute" (slashes become hyphens)
|
|
474
|
+
assert.strictEqual(getGitNamespace("/results/execute"), "-results-execute");
|
|
475
|
+
// "//results" -> "--results" (slashes become hyphens)
|
|
476
|
+
assert.strictEqual(getGitNamespace("//results"), "--results");
|
|
477
|
+
});
|
|
478
|
+
it("should handle mixed separators", () => {
|
|
479
|
+
assert.strictEqual(getGitNamespace("results\\sub/dir"), "results-sub-dir");
|
|
480
|
+
});
|
|
481
|
+
it("should handle simple paths", () => {
|
|
482
|
+
assert.strictEqual(getGitNamespace("results"), "results");
|
|
483
|
+
assert.strictEqual(getGitNamespace("execute"), "execute");
|
|
484
|
+
});
|
|
485
|
+
it("should handle paths with only slashes", () => {
|
|
486
|
+
// "///a//b///" -> "---a--b---" (slashes become hyphens)
|
|
487
|
+
assert.strictEqual(getGitNamespace("///a//b///"), "---a--b---");
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
//# sourceMappingURL=checkpoint-storage.unit.test.js.map
|