@sanity/ailf 0.4.1 → 1.0.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/config/features.ts +23 -0
- package/config/models.ts +83 -0
- package/config/prompts.ts +16 -0
- package/config/rubrics.ts +225 -0
- package/config/schedules.ts +47 -0
- package/config/sinks.ts +37 -0
- package/config/sources.ts +21 -0
- package/config/thresholds.ts +61 -0
- package/dist/_vendor/ailf-core/config-helpers.d.ts +174 -0
- package/dist/_vendor/ailf-core/config-helpers.js +150 -0
- package/dist/_vendor/ailf-core/env-helper.d.ts +35 -0
- package/dist/_vendor/ailf-core/env-helper.js +45 -0
- package/dist/_vendor/ailf-core/examples/index.d.ts +10 -10
- package/dist/_vendor/ailf-core/examples/index.js +10 -10
- package/dist/_vendor/ailf-core/index.d.ts +3 -0
- package/dist/_vendor/ailf-core/index.js +5 -0
- package/dist/_vendor/ailf-core/ports/context.d.ts +15 -2
- package/dist/_vendor/ailf-core/ports/doc-fetcher.d.ts +2 -2
- package/dist/_vendor/ailf-core/ports/index.d.ts +2 -1
- package/dist/_vendor/ailf-core/ports/mode-handler.d.ts +129 -0
- package/dist/_vendor/ailf-core/ports/mode-handler.js +19 -0
- package/dist/_vendor/ailf-core/ports/task-source.d.ts +16 -122
- package/dist/_vendor/ailf-core/ports/task-source.js +7 -7
- package/dist/_vendor/ailf-core/schemas/eval-config.d.ts +7 -2
- package/dist/_vendor/ailf-core/schemas/eval-config.js +7 -2
- package/dist/_vendor/ailf-core/schemas/pipeline-request.d.ts +8 -3
- package/dist/_vendor/ailf-core/schemas/pipeline-request.js +6 -1
- package/dist/_vendor/ailf-core/schemas/pipeline.d.ts +32 -31
- package/dist/_vendor/ailf-core/schemas/pipeline.js +52 -12
- package/dist/_vendor/ailf-core/schemas/schedules.d.ts +14 -4
- package/dist/_vendor/ailf-core/schemas/schedules.js +6 -2
- package/dist/_vendor/ailf-core/schemas/sinks.d.ts +1 -1
- package/dist/_vendor/ailf-core/services/comparison-formatters.js +57 -19
- package/dist/_vendor/ailf-core/services/index.d.ts +2 -1
- package/dist/_vendor/ailf-core/services/index.js +2 -1
- package/dist/_vendor/ailf-core/services/scoring-engine.d.ts +153 -0
- package/dist/_vendor/ailf-core/services/scoring-engine.js +237 -0
- package/dist/_vendor/ailf-core/services/scoring.d.ts +15 -2
- package/dist/_vendor/ailf-core/services/scoring.js +25 -15
- package/dist/_vendor/ailf-core/types/branded-ids.d.ts +137 -0
- package/dist/_vendor/ailf-core/types/branded-ids.js +136 -0
- package/dist/_vendor/ailf-core/types/eval-mode-config.d.ts +150 -0
- package/dist/_vendor/ailf-core/types/eval-mode-config.js +24 -0
- package/dist/_vendor/ailf-core/types/generalized-task.d.ts +319 -0
- package/dist/_vendor/ailf-core/types/generalized-task.js +13 -0
- package/dist/_vendor/ailf-core/types/index.d.ts +45 -81
- package/dist/_vendor/ailf-core/types/index.js +8 -1
- package/dist/_vendor/ailf-core/types/plugin-registry.d.ts +202 -0
- package/dist/_vendor/ailf-core/types/plugin-registry.js +132 -0
- package/dist/_vendor/ailf-core/types/storage-schema.d.ts +199 -0
- package/dist/_vendor/ailf-core/types/storage-schema.js +39 -0
- package/dist/_vendor/ailf-core/types/task-graph.d.ts +86 -0
- package/dist/_vendor/ailf-core/types/task-graph.js +20 -0
- package/dist/_vendor/ailf-core/types/trace.d.ts +118 -0
- package/dist/_vendor/ailf-core/types/trace.js +18 -0
- package/dist/_vendor/ailf-core/types/variable-envelope.d.ts +80 -0
- package/dist/_vendor/ailf-core/types/variable-envelope.js +16 -0
- package/dist/_vendor/ailf-shared/dimension-names.d.ts +5 -18
- package/dist/_vendor/ailf-shared/dimension-names.js +6 -24
- package/dist/_vendor/ailf-shared/eval-modes.d.ts +38 -6
- package/dist/_vendor/ailf-shared/eval-modes.js +26 -2
- package/dist/_vendor/ailf-shared/index.d.ts +0 -1
- package/dist/_vendor/ailf-shared/index.js +0 -1
- package/dist/adapters/api-client/build-request.js +14 -13
- package/dist/adapters/config-sources/file-config-adapter.d.ts +20 -11
- package/dist/adapters/config-sources/file-config-adapter.js +38 -12
- package/dist/adapters/config-sources/index.d.ts +2 -0
- package/dist/adapters/config-sources/index.js +1 -0
- package/dist/adapters/config-sources/ts-config-loader.d.ts +59 -0
- package/dist/adapters/config-sources/ts-config-loader.js +133 -0
- package/dist/adapters/doc-fetchers/sanity-doc-fetcher.d.ts +3 -2
- package/dist/adapters/doc-fetchers/sanity-doc-fetcher.js +7 -2
- package/dist/adapters/task-sources/composite-task-source.d.ts +3 -3
- package/dist/adapters/task-sources/composite-task-source.js +1 -1
- package/dist/adapters/task-sources/content-lake-task-source.d.ts +7 -6
- package/dist/adapters/task-sources/content-lake-task-source.js +22 -23
- package/dist/adapters/task-sources/index.d.ts +1 -0
- package/dist/adapters/task-sources/index.js +1 -0
- package/dist/adapters/task-sources/repo-task-source.d.ts +4 -4
- package/dist/adapters/task-sources/repo-task-source.js +69 -16
- package/dist/adapters/task-sources/task-file-loader.d.ts +64 -0
- package/dist/adapters/task-sources/task-file-loader.js +83 -0
- package/dist/adapters/task-sources/yaml-task-source.d.ts +6 -6
- package/dist/adapters/task-sources/yaml-task-source.js +19 -16
- package/dist/cli.js +0 -2
- package/dist/commands/baseline.js +4 -1
- package/dist/commands/calculate-scores.js +1 -1
- package/dist/commands/coverage-audit.js +7 -1
- package/dist/commands/explain-handler.js +25 -23
- package/dist/commands/fetch-docs.js +3 -2
- package/dist/commands/generate-configs.js +1 -1
- package/dist/commands/interactive.js +11 -7
- package/dist/commands/pipeline-action.d.ts +2 -0
- package/dist/commands/pipeline-action.js +16 -6
- package/dist/commands/pipeline.d.ts +1 -0
- package/dist/commands/pipeline.js +4 -2
- package/dist/commands/pr-comment.js +1 -1
- package/dist/commands/publish.js +2 -2
- package/dist/commands/readiness-report.js +13 -6
- package/dist/composition-root.d.ts +1 -1
- package/dist/composition-root.js +67 -4
- package/dist/orchestration/build-app-context.js +1 -0
- package/dist/orchestration/build-step-sequence.js +24 -6
- package/dist/orchestration/steps/calculate-scores-step.js +24 -11
- package/dist/orchestration/steps/fetch-docs-step.js +6 -4
- package/dist/orchestration/steps/gap-analysis-step.js +8 -7
- package/dist/orchestration/steps/generate-configs-step.d.ts +16 -3
- package/dist/orchestration/steps/generate-configs-step.js +245 -51
- package/dist/orchestration/steps/grader-consistency-step.js +7 -4
- package/dist/orchestration/steps/mirror-repo-tasks-step.js +1 -1
- package/dist/orchestration/steps/readiness-step.js +5 -6
- package/dist/orchestration/steps/run-eval-step.d.ts +1 -2
- package/dist/orchestration/steps/run-eval-step.js +8 -7
- package/dist/pipeline/cache.d.ts +1 -1
- package/dist/pipeline/cache.js +36 -8
- package/dist/pipeline/calculate-scores.d.ts +5 -7
- package/dist/pipeline/calculate-scores.js +74 -153
- package/dist/pipeline/checks.js +2 -2
- package/dist/pipeline/compare.js +8 -8
- package/dist/pipeline/compiler/__tests__/agent-harness-handler.test.d.ts +10 -0
- package/dist/pipeline/compiler/__tests__/agent-harness-handler.test.js +288 -0
- package/dist/pipeline/compiler/__tests__/assertion-mapper.test.d.ts +9 -0
- package/dist/pipeline/compiler/__tests__/assertion-mapper.test.js +145 -0
- package/dist/pipeline/compiler/__tests__/knowledge-probe-handler.test.d.ts +10 -0
- package/dist/pipeline/compiler/__tests__/knowledge-probe-handler.test.js +314 -0
- package/dist/pipeline/compiler/__tests__/literacy-handler.test.d.ts +10 -0
- package/dist/pipeline/compiler/__tests__/literacy-handler.test.js +486 -0
- package/dist/pipeline/compiler/__tests__/mcp-server-handler.test.d.ts +10 -0
- package/dist/pipeline/compiler/__tests__/mcp-server-handler.test.js +355 -0
- package/dist/pipeline/compiler/__tests__/promptfoo-compiler.test.d.ts +9 -0
- package/dist/pipeline/compiler/__tests__/promptfoo-compiler.test.js +333 -0
- package/dist/pipeline/compiler/__tests__/sandbox-and-fixtures.test.d.ts +12 -0
- package/dist/pipeline/compiler/__tests__/sandbox-and-fixtures.test.js +210 -0
- package/dist/pipeline/compiler/__tests__/scoring-and-presets.test.d.ts +7 -0
- package/dist/pipeline/compiler/__tests__/scoring-and-presets.test.js +471 -0
- package/dist/pipeline/compiler/__tests__/scoring-bridge.test.d.ts +10 -0
- package/dist/pipeline/compiler/__tests__/scoring-bridge.test.js +184 -0
- package/dist/pipeline/compiler/__tests__/task-graph-builder.test.d.ts +8 -0
- package/dist/pipeline/compiler/__tests__/task-graph-builder.test.js +301 -0
- package/dist/pipeline/compiler/__tests__/telemetry.test.d.ts +9 -0
- package/dist/pipeline/compiler/__tests__/telemetry.test.js +503 -0
- package/dist/pipeline/compiler/assertion-mapper.d.ts +58 -0
- package/dist/pipeline/compiler/assertion-mapper.js +175 -0
- package/dist/pipeline/compiler/compiler-to-yaml.d.ts +51 -0
- package/dist/pipeline/compiler/compiler-to-yaml.js +222 -0
- package/dist/pipeline/compiler/config-loader.d.ts +56 -0
- package/dist/pipeline/compiler/config-loader.js +111 -0
- package/dist/pipeline/compiler/fixture-resolver.d.ts +41 -0
- package/dist/pipeline/compiler/fixture-resolver.js +113 -0
- package/dist/pipeline/compiler/hash.d.ts +11 -0
- package/dist/pipeline/compiler/hash.js +18 -0
- package/dist/pipeline/compiler/ignore-fields.d.ts +53 -0
- package/dist/pipeline/compiler/ignore-fields.js +113 -0
- package/dist/pipeline/compiler/index.d.ts +29 -0
- package/dist/pipeline/compiler/index.js +45 -0
- package/dist/pipeline/compiler/literacy-bridge.d.ts +102 -0
- package/dist/pipeline/compiler/literacy-bridge.js +172 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/agent-harness-example-tasks.d.ts +14 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/agent-harness-example-tasks.js +152 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/knowledge-probe-example-tasks.d.ts +32 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/knowledge-probe-example-tasks.js +176 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/mcp-example-tasks.d.ts +49 -0
- package/dist/pipeline/compiler/mode-handlers/__fixtures__/mcp-example-tasks.js +259 -0
- package/dist/pipeline/compiler/mode-handlers/agent-harness-handler.d.ts +70 -0
- package/dist/pipeline/compiler/mode-handlers/agent-harness-handler.js +485 -0
- package/dist/pipeline/compiler/mode-handlers/index.d.ts +16 -0
- package/dist/pipeline/compiler/mode-handlers/index.js +21 -0
- package/dist/pipeline/compiler/mode-handlers/knowledge-probe-handler.d.ts +76 -0
- package/dist/pipeline/compiler/mode-handlers/knowledge-probe-handler.js +245 -0
- package/dist/pipeline/compiler/mode-handlers/literacy-handler.d.ts +89 -0
- package/dist/pipeline/compiler/mode-handlers/literacy-handler.js +379 -0
- package/dist/pipeline/compiler/mode-handlers/mcp-assertions.d.ts +50 -0
- package/dist/pipeline/compiler/mode-handlers/mcp-assertions.js +277 -0
- package/dist/pipeline/compiler/mode-handlers/mcp-server-handler.d.ts +67 -0
- package/dist/pipeline/compiler/mode-handlers/mcp-server-handler.js +309 -0
- package/dist/pipeline/compiler/presets/index.d.ts +9 -0
- package/dist/pipeline/compiler/presets/index.js +8 -0
- package/dist/pipeline/compiler/presets/sanity-literacy.d.ts +45 -0
- package/dist/pipeline/compiler/presets/sanity-literacy.js +354 -0
- package/dist/pipeline/compiler/promptfoo-compiler.d.ts +96 -0
- package/dist/pipeline/compiler/promptfoo-compiler.js +230 -0
- package/dist/pipeline/compiler/provider-assembler.d.ts +39 -0
- package/dist/pipeline/compiler/provider-assembler.js +137 -0
- package/dist/pipeline/compiler/sandbox/docker-sandbox.d.ts +21 -0
- package/dist/pipeline/compiler/sandbox/docker-sandbox.js +136 -0
- package/dist/pipeline/compiler/sandbox/fixture-provisioner.d.ts +69 -0
- package/dist/pipeline/compiler/sandbox/fixture-provisioner.js +189 -0
- package/dist/pipeline/compiler/sandbox/git-worktree-sandbox.d.ts +20 -0
- package/dist/pipeline/compiler/sandbox/git-worktree-sandbox.js +114 -0
- package/dist/pipeline/compiler/sandbox/index.d.ts +10 -0
- package/dist/pipeline/compiler/sandbox/index.js +11 -0
- package/dist/pipeline/compiler/sandbox/sandbox-selector.d.ts +35 -0
- package/dist/pipeline/compiler/sandbox/sandbox-selector.js +86 -0
- package/dist/pipeline/compiler/sandbox/sandbox-strategy.d.ts +81 -0
- package/dist/pipeline/compiler/sandbox/sandbox-strategy.js +15 -0
- package/dist/pipeline/compiler/sandbox/tempdir-sandbox.d.ts +20 -0
- package/dist/pipeline/compiler/sandbox/tempdir-sandbox.js +74 -0
- package/dist/pipeline/compiler/scoring-bridge.d.ts +49 -0
- package/dist/pipeline/compiler/scoring-bridge.js +114 -0
- package/dist/pipeline/compiler/task-graph-builder.d.ts +54 -0
- package/dist/pipeline/compiler/task-graph-builder.js +291 -0
- package/dist/pipeline/compiler/telemetry/cost-tracker.d.ts +90 -0
- package/dist/pipeline/compiler/telemetry/cost-tracker.js +146 -0
- package/dist/pipeline/compiler/telemetry/index.d.ts +14 -0
- package/dist/pipeline/compiler/telemetry/index.js +19 -0
- package/dist/pipeline/compiler/telemetry/redactor.d.ts +58 -0
- package/dist/pipeline/compiler/telemetry/redactor.js +222 -0
- package/dist/pipeline/compiler/telemetry/tool-classifier.d.ts +32 -0
- package/dist/pipeline/compiler/telemetry/tool-classifier.js +120 -0
- package/dist/pipeline/compiler/telemetry/trace-collector.d.ts +75 -0
- package/dist/pipeline/compiler/telemetry/trace-collector.js +297 -0
- package/dist/pipeline/compiler/telemetry/trace-store.d.ts +78 -0
- package/dist/pipeline/compiler/telemetry/trace-store.js +85 -0
- package/dist/pipeline/compiler/variable-resolver.d.ts +46 -0
- package/dist/pipeline/compiler/variable-resolver.js +115 -0
- package/dist/pipeline/coverage-audit.d.ts +15 -5
- package/dist/pipeline/coverage-audit.js +41 -22
- package/dist/pipeline/eval-constants.d.ts +16 -6
- package/dist/pipeline/eval-constants.js +25 -4
- package/dist/pipeline/eval-fingerprint.d.ts +2 -2
- package/dist/pipeline/eval-fingerprint.js +8 -9
- package/dist/pipeline/expand-tasks.d.ts +23 -14
- package/dist/pipeline/expand-tasks.js +37 -31
- package/dist/pipeline/gap-analysis.d.ts +1 -1
- package/dist/pipeline/gap-analysis.js +2 -2
- package/dist/pipeline/generate-configs.d.ts +22 -4
- package/dist/pipeline/generate-configs.js +53 -24
- package/dist/pipeline/grader-api.d.ts +3 -3
- package/dist/pipeline/grader-api.js +5 -12
- package/dist/pipeline/grader-compare-runner.js +20 -27
- package/dist/pipeline/grader-comparison.d.ts +4 -8
- package/dist/pipeline/grader-comparison.js +11 -17
- package/dist/pipeline/grader-consistency-runner.d.ts +2 -3
- package/dist/pipeline/grader-consistency-runner.js +18 -21
- package/dist/pipeline/grader-consistency.d.ts +6 -10
- package/dist/pipeline/grader-consistency.js +13 -32
- package/dist/pipeline/grader-sensitivity-runner.js +7 -5
- package/dist/pipeline/grader-sensitivity.d.ts +2 -6
- package/dist/pipeline/grader-sensitivity.js +10 -10
- package/dist/pipeline/grader-validate-runner.js +7 -5
- package/dist/pipeline/grader-validation.d.ts +2 -6
- package/dist/pipeline/grader-validation.js +14 -22
- package/dist/pipeline/map-request-to-config.js +6 -1
- package/dist/pipeline/mirror-repo-tasks.d.ts +6 -6
- package/dist/pipeline/mirror-repo-tasks.js +16 -15
- package/dist/pipeline/normalize-mode.d.ts +49 -0
- package/dist/pipeline/normalize-mode.js +64 -0
- package/dist/pipeline/plan.d.ts +5 -2
- package/dist/pipeline/plan.js +134 -78
- package/dist/pipeline/pr-comment.js +2 -0
- package/dist/pipeline/profile-resolution.d.ts +47 -0
- package/dist/pipeline/profile-resolution.js +91 -0
- package/dist/pipeline/provenance.d.ts +2 -2
- package/dist/pipeline/provenance.js +12 -17
- package/dist/pipeline/release-report.js +4 -4
- package/dist/pipeline/repo-threshold-evaluator.d.ts +1 -1
- package/dist/pipeline/repo-threshold-evaluator.js +1 -1
- package/dist/pipeline/rubric-loader.d.ts +20 -0
- package/dist/pipeline/rubric-loader.js +37 -0
- package/dist/pipeline/validate.d.ts +4 -4
- package/dist/pipeline/validate.js +64 -53
- package/dist/schedules/loader.js +18 -8
- package/dist/scripts/migrate-task-mode.d.ts +24 -0
- package/dist/scripts/migrate-task-mode.js +85 -0
- package/dist/scripts/migrate-tasks-to-content-lake.js +11 -10
- package/dist/scripts/validate-task-sources.d.ts +1 -1
- package/dist/scripts/validate-task-sources.js +15 -15
- package/dist/sinks/loader.js +5 -7
- package/dist/sources.d.ts +7 -7
- package/dist/sources.js +22 -24
- package/dist/webhook/dispatch.js +2 -1
- package/package.json +6 -3
- package/tasks/knowledge-probe/define-type-api.task.ts +55 -0
- package/tasks/knowledge-probe/groq-projections.task.ts +59 -0
- package/tasks/literacy/frameworks.task.ts +128 -0
- package/tasks/literacy/functions.task.ts +69 -0
- package/tasks/literacy/groq.task.ts +258 -0
- package/tasks/literacy/nextjs-live.task.ts +75 -0
- package/tasks/literacy/studio-setup.task.ts +131 -0
- package/tasks/literacy/visual-editing.task.ts +146 -0
- package/config/features.yaml +0 -116
- package/config/models.yaml +0 -116
- package/config/prompts.yaml +0 -75
- package/config/rubrics.yaml +0 -62
- package/config/schedules.yaml +0 -43
- package/config/sinks.yaml +0 -54
- package/config/sources.yaml +0 -51
- package/config/thresholds.yaml +0 -49
- package/dist/agent-observer/test-imports.d.ts +0 -7
- package/dist/agent-observer/test-imports.js +0 -185
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* promptfoo-compiler.test.ts — Unit tests for the PromptfooCompiler.
|
|
3
|
+
*
|
|
4
|
+
* Tests compilation of a TaskGraph into Promptfoo configuration,
|
|
5
|
+
* including provider assembly, prompt generation, and test case creation.
|
|
6
|
+
*
|
|
7
|
+
* Run: npx tsx --test src/pipeline/compiler/__tests__/promptfoo-compiler.test.ts
|
|
8
|
+
*/
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { describe, it } from "node:test";
|
|
11
|
+
import { tmpdir } from "os";
|
|
12
|
+
import { LiteracyVariant } from "../../normalize-mode.js";
|
|
13
|
+
import { compileToPromptfoo } from "../promptfoo-compiler.js";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
function makeModels(models = []) {
|
|
18
|
+
return {
|
|
19
|
+
defaults: { temperature: 0.2 },
|
|
20
|
+
grader: { id: "openai:chat:gpt-5", label: "GPT-5" },
|
|
21
|
+
models: models.map((m) => ({
|
|
22
|
+
config: {},
|
|
23
|
+
id: m.id,
|
|
24
|
+
label: m.label,
|
|
25
|
+
modes: m.modes,
|
|
26
|
+
})),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function makeNode(id, prompt) {
|
|
30
|
+
return {
|
|
31
|
+
dependsOn: [],
|
|
32
|
+
priority: 0,
|
|
33
|
+
resolvedPrompt: prompt ?? `Implement ${id}`,
|
|
34
|
+
resolvedVariables: {
|
|
35
|
+
declarations: [],
|
|
36
|
+
provenance: {},
|
|
37
|
+
values: {
|
|
38
|
+
task: prompt ?? `Implement ${id}`,
|
|
39
|
+
docs: `Documentation for ${id}`,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
taskId: id,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function makeGraph(nodes, edges = []) {
|
|
46
|
+
const nodeMap = new Map();
|
|
47
|
+
for (const n of nodes)
|
|
48
|
+
nodeMap.set(n.taskId, n);
|
|
49
|
+
return {
|
|
50
|
+
compilationTarget: "promptfoo",
|
|
51
|
+
edges,
|
|
52
|
+
fixtures: new Map(),
|
|
53
|
+
nodes: nodeMap,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// compileToPromptfoo — basic compilation
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
describe("compileToPromptfoo", () => {
|
|
60
|
+
it("compiles a single-task graph into a Promptfoo config", () => {
|
|
61
|
+
const graph = makeGraph([makeNode("groq-basics")]);
|
|
62
|
+
const result = compileToPromptfoo(graph, {
|
|
63
|
+
mode: "literacy",
|
|
64
|
+
models: makeModels([{ id: "openai:chat:gpt-4o", label: "GPT-4o" }]),
|
|
65
|
+
rootDir: tmpdir(),
|
|
66
|
+
});
|
|
67
|
+
assert.ok(result.config);
|
|
68
|
+
assert.equal(result.taskCount, 1);
|
|
69
|
+
// Literacy mode produces gold + baseline test cases
|
|
70
|
+
assert.equal(result.testCaseCount, 2);
|
|
71
|
+
assert.equal(result.config.tests.length, 2);
|
|
72
|
+
});
|
|
73
|
+
it("generates correct test case descriptions", () => {
|
|
74
|
+
const graph = makeGraph([makeNode("groq-basics")]);
|
|
75
|
+
const result = compileToPromptfoo(graph, {
|
|
76
|
+
mode: "literacy",
|
|
77
|
+
models: makeModels(),
|
|
78
|
+
rootDir: tmpdir(),
|
|
79
|
+
});
|
|
80
|
+
assert.equal(result.config.tests[0].description, "groq-basics");
|
|
81
|
+
assert.equal(result.config.tests[1].description, "groq-basics [baseline]");
|
|
82
|
+
});
|
|
83
|
+
it("includes task variables in test case vars", () => {
|
|
84
|
+
const graph = makeGraph([makeNode("task-1", "Do the thing")]);
|
|
85
|
+
const result = compileToPromptfoo(graph, {
|
|
86
|
+
mode: "literacy",
|
|
87
|
+
models: makeModels(),
|
|
88
|
+
rootDir: tmpdir(),
|
|
89
|
+
});
|
|
90
|
+
const vars = result.config.tests[0].vars;
|
|
91
|
+
assert.equal(vars.task, "Do the thing");
|
|
92
|
+
assert.equal(vars.docs, "Documentation for task-1");
|
|
93
|
+
});
|
|
94
|
+
it("baseline test case has empty docs", () => {
|
|
95
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
96
|
+
const result = compileToPromptfoo(graph, {
|
|
97
|
+
mode: "literacy",
|
|
98
|
+
models: makeModels(),
|
|
99
|
+
rootDir: tmpdir(),
|
|
100
|
+
});
|
|
101
|
+
// Second test case is the baseline
|
|
102
|
+
assert.equal(result.config.tests[1].vars.docs, "");
|
|
103
|
+
});
|
|
104
|
+
it("builds providers from model registry", () => {
|
|
105
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
106
|
+
const result = compileToPromptfoo(graph, {
|
|
107
|
+
mode: "literacy",
|
|
108
|
+
models: makeModels([
|
|
109
|
+
{ id: "openai:chat:gpt-4o", label: "GPT-4o" },
|
|
110
|
+
{
|
|
111
|
+
id: "anthropic:messages:claude-sonnet-4-6",
|
|
112
|
+
label: "Claude Sonnet",
|
|
113
|
+
},
|
|
114
|
+
]),
|
|
115
|
+
rootDir: tmpdir(),
|
|
116
|
+
});
|
|
117
|
+
assert.equal(result.config.providers.length, 2);
|
|
118
|
+
assert.equal(result.config.providers[0].id, "openai:chat:gpt-4o");
|
|
119
|
+
assert.equal(result.config.providers[1].id, "anthropic:messages:claude-sonnet-4-6");
|
|
120
|
+
});
|
|
121
|
+
it("filters models by mode", () => {
|
|
122
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
123
|
+
const result = compileToPromptfoo(graph, {
|
|
124
|
+
mode: "literacy",
|
|
125
|
+
models: makeModels([
|
|
126
|
+
{ id: "model-a", label: "A", modes: [LiteracyVariant.STANDARD] },
|
|
127
|
+
{ id: "model-b", label: "B", modes: ["agentic-naive"] },
|
|
128
|
+
]),
|
|
129
|
+
rootDir: tmpdir(),
|
|
130
|
+
});
|
|
131
|
+
assert.equal(result.config.providers.length, 1);
|
|
132
|
+
assert.equal(result.config.providers[0].id, "model-a");
|
|
133
|
+
});
|
|
134
|
+
it("generates default prompts for literacy mode", () => {
|
|
135
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
136
|
+
const result = compileToPromptfoo(graph, {
|
|
137
|
+
mode: "literacy",
|
|
138
|
+
models: makeModels(),
|
|
139
|
+
rootDir: tmpdir(),
|
|
140
|
+
});
|
|
141
|
+
assert.equal(result.config.prompts.length, 2);
|
|
142
|
+
assert.equal(result.config.prompts[0].id, "with-docs");
|
|
143
|
+
assert.equal(result.config.prompts[1].id, "without-docs");
|
|
144
|
+
});
|
|
145
|
+
it("generates default prompts for non-literacy modes", () => {
|
|
146
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
147
|
+
const result = compileToPromptfoo(graph, {
|
|
148
|
+
mode: "mcp-server",
|
|
149
|
+
models: makeModels(),
|
|
150
|
+
rootDir: tmpdir(),
|
|
151
|
+
});
|
|
152
|
+
assert.equal(result.config.prompts.length, 1);
|
|
153
|
+
assert.equal(result.config.prompts[0].id, "default");
|
|
154
|
+
// Non-literacy modes don't create baseline variants
|
|
155
|
+
assert.equal(result.config.tests.length, 1);
|
|
156
|
+
});
|
|
157
|
+
it("sets grader provider in defaultTest options", () => {
|
|
158
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
159
|
+
const result = compileToPromptfoo(graph, {
|
|
160
|
+
mode: "literacy",
|
|
161
|
+
models: makeModels(),
|
|
162
|
+
rootDir: tmpdir(),
|
|
163
|
+
graderProvider: "openai:chat:gpt-5",
|
|
164
|
+
});
|
|
165
|
+
assert.ok(result.config.defaultTest);
|
|
166
|
+
assert.equal(result.config.defaultTest.options?.provider, "openai:chat:gpt-5");
|
|
167
|
+
});
|
|
168
|
+
it("compiles multiple tasks in priority order", () => {
|
|
169
|
+
const nodeA = makeNode("task-a");
|
|
170
|
+
nodeA.priority = 0;
|
|
171
|
+
const nodeB = makeNode("task-b");
|
|
172
|
+
nodeB.priority = 1;
|
|
173
|
+
const graph = makeGraph([nodeB, nodeA]); // Intentionally reversed
|
|
174
|
+
const result = compileToPromptfoo(graph, {
|
|
175
|
+
mode: "mcp-server",
|
|
176
|
+
models: makeModels(),
|
|
177
|
+
rootDir: tmpdir(),
|
|
178
|
+
});
|
|
179
|
+
// Should be sorted by priority: task-a first, then task-b
|
|
180
|
+
assert.equal(result.config.tests[0].description, "task-a");
|
|
181
|
+
assert.equal(result.config.tests[1].description, "task-b");
|
|
182
|
+
});
|
|
183
|
+
it("sets output path when provided", () => {
|
|
184
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
185
|
+
const result = compileToPromptfoo(graph, {
|
|
186
|
+
mode: "literacy",
|
|
187
|
+
models: makeModels(),
|
|
188
|
+
rootDir: tmpdir(),
|
|
189
|
+
outputPath: "results/latest/eval-results.json",
|
|
190
|
+
});
|
|
191
|
+
assert.equal(result.config.outputPath, "results/latest/eval-results.json");
|
|
192
|
+
});
|
|
193
|
+
it("merges model defaults with per-model config", () => {
|
|
194
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
195
|
+
const result = compileToPromptfoo(graph, {
|
|
196
|
+
mode: "literacy",
|
|
197
|
+
models: {
|
|
198
|
+
defaults: { temperature: 0.2, max_tokens: 4096 },
|
|
199
|
+
grader: { id: "gpt-5" },
|
|
200
|
+
models: [
|
|
201
|
+
{
|
|
202
|
+
id: "model-a",
|
|
203
|
+
label: "A",
|
|
204
|
+
config: { temperature: 0.5 },
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
rootDir: tmpdir(),
|
|
209
|
+
});
|
|
210
|
+
assert.equal(result.config.providers.length, 1);
|
|
211
|
+
const config = result.config.providers[0].config;
|
|
212
|
+
// Per-model config overrides defaults
|
|
213
|
+
assert.equal(config.temperature, 0.5);
|
|
214
|
+
assert.equal(config.max_tokens, 4096);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Handler-owned prompt resolution
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
describe("compileToPromptfoo — handler prompt resolution", () => {
|
|
221
|
+
/** Build a minimal handler that returns prompts from getPrompts() */
|
|
222
|
+
function makeHandlerWithPrompts(prompts) {
|
|
223
|
+
return {
|
|
224
|
+
compileTask() {
|
|
225
|
+
return { providers: [], tests: [], prompts: [], warnings: [] };
|
|
226
|
+
},
|
|
227
|
+
getPrompts() {
|
|
228
|
+
return prompts;
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** Build a handler that does NOT implement getPrompts */
|
|
233
|
+
function makeHandlerWithoutPrompts() {
|
|
234
|
+
return {
|
|
235
|
+
compileTask() {
|
|
236
|
+
return { providers: [], tests: [], prompts: [], warnings: [] };
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
it("uses handler prompts when handler.getPrompts() returns templates", () => {
|
|
241
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
242
|
+
const handler = makeHandlerWithPrompts({
|
|
243
|
+
"custom-prompt": {
|
|
244
|
+
id: "custom-prompt",
|
|
245
|
+
label: "Custom Mode Prompt",
|
|
246
|
+
template: "Do the thing: {{task}}",
|
|
247
|
+
variables: ["task"],
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
const result = compileToPromptfoo(graph, {
|
|
251
|
+
mode: "literacy",
|
|
252
|
+
models: makeModels(),
|
|
253
|
+
rootDir: tmpdir(),
|
|
254
|
+
handler,
|
|
255
|
+
});
|
|
256
|
+
assert.equal(result.config.prompts.length, 1);
|
|
257
|
+
assert.equal(result.config.prompts[0].id, "custom-prompt");
|
|
258
|
+
assert.equal(result.config.prompts[0].label, "Custom Mode Prompt");
|
|
259
|
+
assert.equal(result.config.prompts[0].raw, "Do the thing: {{task}}");
|
|
260
|
+
});
|
|
261
|
+
it("falls back to default prompts when handler has no getPrompts()", () => {
|
|
262
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
263
|
+
const handler = makeHandlerWithoutPrompts();
|
|
264
|
+
const result = compileToPromptfoo(graph, {
|
|
265
|
+
mode: "literacy",
|
|
266
|
+
models: makeModels(),
|
|
267
|
+
rootDir: tmpdir(),
|
|
268
|
+
handler,
|
|
269
|
+
});
|
|
270
|
+
// Should fall back to built-in literacy defaults
|
|
271
|
+
assert.equal(result.config.prompts.length, 2);
|
|
272
|
+
assert.equal(result.config.prompts[0].id, "with-docs");
|
|
273
|
+
assert.equal(result.config.prompts[1].id, "without-docs");
|
|
274
|
+
});
|
|
275
|
+
it("falls back to default prompts when no handler is provided", () => {
|
|
276
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
277
|
+
const result = compileToPromptfoo(graph, {
|
|
278
|
+
mode: "mcp-server",
|
|
279
|
+
models: makeModels(),
|
|
280
|
+
rootDir: tmpdir(),
|
|
281
|
+
// No handler
|
|
282
|
+
});
|
|
283
|
+
// Non-literacy modes get generic default prompt
|
|
284
|
+
assert.equal(result.config.prompts.length, 1);
|
|
285
|
+
assert.equal(result.config.prompts[0].id, "default");
|
|
286
|
+
});
|
|
287
|
+
it("prefers handler prompts over explicit options.prompts", () => {
|
|
288
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
289
|
+
const handler = makeHandlerWithPrompts({
|
|
290
|
+
"handler-owned": {
|
|
291
|
+
id: "handler-owned",
|
|
292
|
+
label: "Handler Prompt",
|
|
293
|
+
template: "From handler: {{task}}",
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
const result = compileToPromptfoo(graph, {
|
|
297
|
+
mode: "literacy",
|
|
298
|
+
models: makeModels(),
|
|
299
|
+
rootDir: tmpdir(),
|
|
300
|
+
handler,
|
|
301
|
+
// Explicit prompts should be ignored when handler provides them
|
|
302
|
+
prompts: [
|
|
303
|
+
{ id: "explicit", label: "Explicit", raw: "explicit {{task}}" },
|
|
304
|
+
],
|
|
305
|
+
});
|
|
306
|
+
assert.equal(result.config.prompts.length, 1);
|
|
307
|
+
assert.equal(result.config.prompts[0].id, "handler-owned");
|
|
308
|
+
});
|
|
309
|
+
it("falls back to explicit prompts when handler.getPrompts() returns undefined", () => {
|
|
310
|
+
const graph = makeGraph([makeNode("task-1")]);
|
|
311
|
+
// Handler with getPrompts defined but returning undefined
|
|
312
|
+
const handler = {
|
|
313
|
+
compileTask() {
|
|
314
|
+
return { providers: [], tests: [], prompts: [], warnings: [] };
|
|
315
|
+
},
|
|
316
|
+
getPrompts() {
|
|
317
|
+
return undefined;
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
const explicitPrompts = [
|
|
321
|
+
{ id: "explicit-p", label: "Explicit Prompt", raw: "explicit: {{task}}" },
|
|
322
|
+
];
|
|
323
|
+
const result = compileToPromptfoo(graph, {
|
|
324
|
+
mode: "literacy",
|
|
325
|
+
models: makeModels(),
|
|
326
|
+
rootDir: tmpdir(),
|
|
327
|
+
handler,
|
|
328
|
+
prompts: explicitPrompts,
|
|
329
|
+
});
|
|
330
|
+
assert.equal(result.config.prompts.length, 1);
|
|
331
|
+
assert.equal(result.config.prompts[0].id, "explicit-p");
|
|
332
|
+
});
|
|
333
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandbox-and-fixtures.test.ts — Tests for sandbox strategy + fixture provisioning.
|
|
3
|
+
*
|
|
4
|
+
* Tests TempDir sandbox (the universal fallback), fixture provisioning
|
|
5
|
+
* pipeline stages, ignoreFields stripping, and sandbox selection logic.
|
|
6
|
+
*
|
|
7
|
+
* Docker and GitWorktree sandboxes are not tested here (they require
|
|
8
|
+
* external dependencies). CI tests for Docker are in a separate workflow.
|
|
9
|
+
*
|
|
10
|
+
* Run: npx tsx --test src/pipeline/compiler/__tests__/sandbox-and-fixtures.test.ts
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sandbox-and-fixtures.test.ts — Tests for sandbox strategy + fixture provisioning.
|
|
3
|
+
*
|
|
4
|
+
* Tests TempDir sandbox (the universal fallback), fixture provisioning
|
|
5
|
+
* pipeline stages, ignoreFields stripping, and sandbox selection logic.
|
|
6
|
+
*
|
|
7
|
+
* Docker and GitWorktree sandboxes are not tested here (they require
|
|
8
|
+
* external dependencies). CI tests for Docker are in a separate workflow.
|
|
9
|
+
*
|
|
10
|
+
* Run: npx tsx --test src/pipeline/compiler/__tests__/sandbox-and-fixtures.test.ts
|
|
11
|
+
*/
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
14
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
15
|
+
import { tmpdir } from "os";
|
|
16
|
+
import { resolve } from "path";
|
|
17
|
+
import { TempDirSandboxStrategy } from "../sandbox/tempdir-sandbox.js";
|
|
18
|
+
import { selectSandboxStrategy } from "../sandbox/sandbox-selector.js";
|
|
19
|
+
import { provisionFixtures } from "../sandbox/fixture-provisioner.js";
|
|
20
|
+
import { stripFields, compareWithIgnoredFields, buildIgnoreFieldsWrapper, } from "../ignore-fields.js";
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// TempDirSandboxStrategy
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
describe("TempDirSandboxStrategy", () => {
|
|
25
|
+
const strategy = new TempDirSandboxStrategy();
|
|
26
|
+
it("is always available", async () => {
|
|
27
|
+
assert.equal(await strategy.isAvailable(), true);
|
|
28
|
+
});
|
|
29
|
+
it("provisions a sandbox with a working directory", async () => {
|
|
30
|
+
const sandbox = await strategy.provision({
|
|
31
|
+
type: "tempdir",
|
|
32
|
+
taskId: "test-task",
|
|
33
|
+
});
|
|
34
|
+
assert.ok(sandbox.id.startsWith("ailf-sandbox-"));
|
|
35
|
+
assert.ok(existsSync(sandbox.workingDir));
|
|
36
|
+
assert.equal(sandbox.strategy, "tempdir");
|
|
37
|
+
// Clean up
|
|
38
|
+
await strategy.teardown(sandbox);
|
|
39
|
+
assert.ok(!existsSync(sandbox.workingDir));
|
|
40
|
+
});
|
|
41
|
+
it("collects artifacts from sandbox", async () => {
|
|
42
|
+
const sandbox = await strategy.provision({
|
|
43
|
+
type: "tempdir",
|
|
44
|
+
taskId: "artifact-test",
|
|
45
|
+
});
|
|
46
|
+
// Create some files
|
|
47
|
+
writeFileSync(resolve(sandbox.workingDir, "file1.ts"), "export const x = 1");
|
|
48
|
+
mkdirSync(resolve(sandbox.workingDir, "subdir"), { recursive: true });
|
|
49
|
+
writeFileSync(resolve(sandbox.workingDir, "subdir", "file2.ts"), "export const y = 2");
|
|
50
|
+
const artifacts = await strategy.collectArtifacts(sandbox);
|
|
51
|
+
assert.ok(artifacts.modifiedFiles.includes("file1.ts"));
|
|
52
|
+
assert.ok(artifacts.modifiedFiles.includes("subdir/file2.ts"));
|
|
53
|
+
assert.ok(artifacts.durationMs >= 0);
|
|
54
|
+
await strategy.teardown(sandbox);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// selectSandboxStrategy
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
describe("selectSandboxStrategy", () => {
|
|
61
|
+
it("returns TempDir as universal fallback", async () => {
|
|
62
|
+
// Force fallback by requesting an unlikely-available strategy
|
|
63
|
+
const result = await selectSandboxStrategy("tempdir");
|
|
64
|
+
assert.equal(result.strategy.type, "tempdir");
|
|
65
|
+
assert.equal(result.isFallback, false);
|
|
66
|
+
});
|
|
67
|
+
it("reports fallback when preferred strategy is unavailable", async () => {
|
|
68
|
+
// git-worktree is available when git is installed, but docker may not be
|
|
69
|
+
// Use tempdir as a reliable test
|
|
70
|
+
const result = await selectSandboxStrategy("tempdir");
|
|
71
|
+
assert.equal(result.isFallback, false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Fixture provisioning
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
const FIXTURE_DIR = resolve(tmpdir(), `ailf-fixture-test-${process.pid}`);
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
mkdirSync(FIXTURE_DIR, { recursive: true });
|
|
80
|
+
});
|
|
81
|
+
afterEach(() => {
|
|
82
|
+
rmSync(FIXTURE_DIR, { recursive: true, force: true });
|
|
83
|
+
});
|
|
84
|
+
describe("provisionFixtures", () => {
|
|
85
|
+
it("resolves file:// fixtures", async () => {
|
|
86
|
+
writeFileSync(resolve(FIXTURE_DIR, "test.md"), "# Hello World");
|
|
87
|
+
const result = await provisionFixtures([{ uri: "file://test.md", inject: "vars", key: "docs" }], { rootDir: FIXTURE_DIR });
|
|
88
|
+
assert.equal(result.fixtures.length, 1);
|
|
89
|
+
assert.equal(result.fixtures[0].content, "# Hello World");
|
|
90
|
+
assert.equal(result.vars.docs, "# Hello World");
|
|
91
|
+
assert.ok(result.manifest["file://test.md"]);
|
|
92
|
+
});
|
|
93
|
+
it("injects fixtures into sandbox working directory", async () => {
|
|
94
|
+
writeFileSync(resolve(FIXTURE_DIR, "schema.ts"), "export default {}");
|
|
95
|
+
const sandbox = await new TempDirSandboxStrategy().provision({
|
|
96
|
+
type: "tempdir",
|
|
97
|
+
taskId: "inject-test",
|
|
98
|
+
});
|
|
99
|
+
const result = await provisionFixtures([
|
|
100
|
+
{
|
|
101
|
+
uri: "file://schema.ts",
|
|
102
|
+
inject: "working_dir",
|
|
103
|
+
key: "src/schema.ts",
|
|
104
|
+
},
|
|
105
|
+
], { rootDir: FIXTURE_DIR, sandbox });
|
|
106
|
+
assert.equal(result.fixtures.length, 1);
|
|
107
|
+
const injectedPath = resolve(sandbox.workingDir, "src", "schema.ts");
|
|
108
|
+
assert.ok(existsSync(injectedPath));
|
|
109
|
+
assert.equal(readFileSync(injectedPath, "utf-8"), "export default {}");
|
|
110
|
+
rmSync(sandbox.workingDir, { recursive: true, force: true });
|
|
111
|
+
});
|
|
112
|
+
it("warns on missing file:// fixture", async () => {
|
|
113
|
+
const result = await provisionFixtures([{ uri: "file://nonexistent.md", inject: "vars", key: "docs" }], { rootDir: FIXTURE_DIR });
|
|
114
|
+
assert.equal(result.fixtures.length, 0);
|
|
115
|
+
assert.ok(result.warnings.some((w) => w.includes("not found")));
|
|
116
|
+
});
|
|
117
|
+
it("applies strip-html transform", async () => {
|
|
118
|
+
writeFileSync(resolve(FIXTURE_DIR, "page.html"), "<h1>Title</h1><p>Body text</p>");
|
|
119
|
+
const result = await provisionFixtures([
|
|
120
|
+
{
|
|
121
|
+
uri: "file://page.html",
|
|
122
|
+
inject: "vars",
|
|
123
|
+
key: "content",
|
|
124
|
+
transform: "strip-html",
|
|
125
|
+
},
|
|
126
|
+
], { rootDir: FIXTURE_DIR });
|
|
127
|
+
assert.equal(result.vars.content, "TitleBody text");
|
|
128
|
+
});
|
|
129
|
+
it("applies truncate transform", async () => {
|
|
130
|
+
const longContent = "x".repeat(20_000);
|
|
131
|
+
writeFileSync(resolve(FIXTURE_DIR, "long.txt"), longContent);
|
|
132
|
+
const result = await provisionFixtures([
|
|
133
|
+
{
|
|
134
|
+
uri: "file://long.txt",
|
|
135
|
+
inject: "vars",
|
|
136
|
+
key: "docs",
|
|
137
|
+
transform: "truncate",
|
|
138
|
+
},
|
|
139
|
+
], { rootDir: FIXTURE_DIR });
|
|
140
|
+
const content = result.vars.docs;
|
|
141
|
+
assert.ok(content.length < 20_000);
|
|
142
|
+
assert.ok(content.includes("truncated"));
|
|
143
|
+
});
|
|
144
|
+
it("produces content hash manifest", async () => {
|
|
145
|
+
writeFileSync(resolve(FIXTURE_DIR, "a.md"), "alpha");
|
|
146
|
+
writeFileSync(resolve(FIXTURE_DIR, "b.md"), "beta");
|
|
147
|
+
const result = await provisionFixtures([
|
|
148
|
+
{ uri: "file://a.md", inject: "vars", key: "a" },
|
|
149
|
+
{ uri: "file://b.md", inject: "vars", key: "b" },
|
|
150
|
+
], { rootDir: FIXTURE_DIR });
|
|
151
|
+
assert.equal(Object.keys(result.manifest).length, 2);
|
|
152
|
+
assert.ok(result.manifest["file://a.md"]);
|
|
153
|
+
assert.ok(result.manifest["file://b.md"]);
|
|
154
|
+
// Different content → different hashes
|
|
155
|
+
assert.notEqual(result.manifest["file://a.md"], result.manifest["file://b.md"]);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// stripFields / ignoreFields
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
describe("stripFields", () => {
|
|
162
|
+
it("removes a top-level field", () => {
|
|
163
|
+
const result = stripFields({ a: 1, b: 2, c: 3 }, ["b"]);
|
|
164
|
+
assert.deepEqual(result, { a: 1, c: 3 });
|
|
165
|
+
});
|
|
166
|
+
it("removes a nested field", () => {
|
|
167
|
+
const result = stripFields({ meta: { createdAt: "2024-01-01", author: "Alice" }, title: "Hello" }, ["meta.createdAt"]);
|
|
168
|
+
assert.deepEqual(result, { meta: { author: "Alice" }, title: "Hello" });
|
|
169
|
+
});
|
|
170
|
+
it("removes multiple fields", () => {
|
|
171
|
+
const result = stripFields({ a: 1, b: 2, c: { d: 3, e: 4 } }, ["b", "c.d"]);
|
|
172
|
+
assert.deepEqual(result, { a: 1, c: { e: 4 } });
|
|
173
|
+
});
|
|
174
|
+
it("handles non-existent fields gracefully", () => {
|
|
175
|
+
const result = stripFields({ a: 1 }, ["b", "c.d"]);
|
|
176
|
+
assert.deepEqual(result, { a: 1 });
|
|
177
|
+
});
|
|
178
|
+
it("handles null/undefined input", () => {
|
|
179
|
+
assert.equal(stripFields(null, ["a"]), null);
|
|
180
|
+
assert.equal(stripFields(undefined, ["a"]), undefined);
|
|
181
|
+
});
|
|
182
|
+
it("does not mutate the original object", () => {
|
|
183
|
+
const original = { a: 1, b: 2 };
|
|
184
|
+
stripFields(original, ["b"]);
|
|
185
|
+
assert.deepEqual(original, { a: 1, b: 2 });
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe("compareWithIgnoredFields", () => {
|
|
189
|
+
it("returns true when objects match after stripping", () => {
|
|
190
|
+
const result = compareWithIgnoredFields({ title: "Hello", _rev: "abc123", _updatedAt: "2024-01-01" }, { title: "Hello", _rev: "def456", _updatedAt: "2024-06-01" }, ["_rev", "_updatedAt"]);
|
|
191
|
+
assert.equal(result, true);
|
|
192
|
+
});
|
|
193
|
+
it("returns false when objects differ in non-ignored fields", () => {
|
|
194
|
+
const result = compareWithIgnoredFields({ title: "Hello", _rev: "abc123" }, { title: "World", _rev: "def456" }, ["_rev"]);
|
|
195
|
+
assert.equal(result, false);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
describe("buildIgnoreFieldsWrapper", () => {
|
|
199
|
+
it("returns original code when no fields to ignore", () => {
|
|
200
|
+
const code = "return true";
|
|
201
|
+
assert.equal(buildIgnoreFieldsWrapper(code, []), code);
|
|
202
|
+
});
|
|
203
|
+
it("wraps code with stripFields function", () => {
|
|
204
|
+
const wrapped = buildIgnoreFieldsWrapper("// check", ["_rev", "meta.ts"]);
|
|
205
|
+
assert.ok(wrapped.includes("function stripFields"));
|
|
206
|
+
assert.ok(wrapped.includes("__ignoreFields"));
|
|
207
|
+
assert.ok(wrapped.includes('"_rev"'));
|
|
208
|
+
assert.ok(wrapped.includes('"meta.ts"'));
|
|
209
|
+
});
|
|
210
|
+
});
|