all-hands-cli 0.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/.allhands/README.md +75 -0
- package/.allhands/agents/compounder.yaml +15 -0
- package/.allhands/agents/coordinator.yaml +17 -0
- package/.allhands/agents/documentor.yaml +15 -0
- package/.allhands/agents/e2e-test-planner.yaml +17 -0
- package/.allhands/agents/emergent.yaml +22 -0
- package/.allhands/agents/executor.yaml +14 -0
- package/.allhands/agents/ideation.yaml +11 -0
- package/.allhands/agents/initiative-steering.yaml +19 -0
- package/.allhands/agents/judge.yaml +13 -0
- package/.allhands/agents/planner.yaml +19 -0
- package/.allhands/agents/pr-reviewer.yaml +15 -0
- package/.allhands/docs.json +5 -0
- package/.allhands/docs.local.json +26 -0
- package/.allhands/flows/COMPOUNDING.md +203 -0
- package/.allhands/flows/COORDINATION.md +89 -0
- package/.allhands/flows/CORE.md +87 -0
- package/.allhands/flows/DOCUMENTATION.md +218 -0
- package/.allhands/flows/E2E_TEST_PLAN_BUILDING.md +140 -0
- package/.allhands/flows/EMERGENT_PLANNING.md +57 -0
- package/.allhands/flows/IDEATION_SCOPING.md +154 -0
- package/.allhands/flows/INITIATIVE_STEERING.md +110 -0
- package/.allhands/flows/JUDGE_REVIEWING.md +79 -0
- package/.allhands/flows/PROMPT_TASK_EXECUTION.md +68 -0
- package/.allhands/flows/PR_REVIEWING.md +43 -0
- package/.allhands/flows/SPEC_PLANNING.md +216 -0
- package/.allhands/flows/harness/WRITING_HARNESS_FLOWS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_KNOWLEDGE.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_ORCHESTRATION.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_SKILLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_TOOLS.md +27 -0
- package/.allhands/flows/harness/WRITING_HARNESS_VALIDATION_TOOLING.md +27 -0
- package/.allhands/flows/shared/CODEBASE_UNDERSTANDING.md +72 -0
- package/.allhands/flows/shared/CREATE_HARNESS_SPEC.md +48 -0
- package/.allhands/flows/shared/CREATE_SPEC.md +41 -0
- package/.allhands/flows/shared/CREATE_VALIDATION_TOOLING_SPEC.md +70 -0
- package/.allhands/flows/shared/DOCUMENTATION_DISCOVERY.md +123 -0
- package/.allhands/flows/shared/DOCUMENTATION_WRITER.md +101 -0
- package/.allhands/flows/shared/EMERGENT_REFINEMENT_ANALYSIS.md +76 -0
- package/.allhands/flows/shared/EXTERNAL_TECH_GUIDANCE.md +97 -0
- package/.allhands/flows/shared/IDEATION_CODEBASE_GROUNDING.md +49 -0
- package/.allhands/flows/shared/PLAN_DEEPENING.md +152 -0
- package/.allhands/flows/shared/PROMPT_TASKS_CURATION.md +113 -0
- package/.allhands/flows/shared/PROMPT_VALIDATION_REVIEW.MD +99 -0
- package/.allhands/flows/shared/QUICK_PREMORTEM.md +70 -0
- package/.allhands/flows/shared/RESEARCH_GUIDANCE.md +38 -0
- package/.allhands/flows/shared/REVIEW_OPTIONS_BREAKDOWN.md +68 -0
- package/.allhands/flows/shared/SKILL_EXTRACTION.md +84 -0
- package/.allhands/flows/shared/SPEC_FLOW_ANALYSIS.md +119 -0
- package/.allhands/flows/shared/TDD_WORKFLOW.md +109 -0
- package/.allhands/flows/shared/UTILIZE_VALIDATION_TOOLING.md +84 -0
- package/.allhands/flows/shared/WRITING_HARNESS_FLOWS.md +11 -0
- package/.allhands/flows/shared/WRITING_HARNESS_MCP_TOOLS.md +84 -0
- package/.allhands/flows/shared/jury/ARCHITECTURE_REVIEW.md +91 -0
- package/.allhands/flows/shared/jury/BEST_PRACTICES_REVIEW.md +80 -0
- package/.allhands/flows/shared/jury/CLAIM_VERIFICATION_REVIEW.md +101 -0
- package/.allhands/flows/shared/jury/EXPECTATIONS_FIT_REVIEW.md +78 -0
- package/.allhands/flows/shared/jury/MAINTAINABILITY_REVIEW.md +110 -0
- package/.allhands/flows/shared/jury/PROMPTS_EXPECTATIONS_FIT.md +74 -0
- package/.allhands/flows/shared/jury/PROMPTS_FLOW_ANALYSIS.md +92 -0
- package/.allhands/flows/shared/jury/PROMPTS_YAGNI.md +78 -0
- package/.allhands/flows/shared/jury/PROMPT_PREMORTEM.md +125 -0
- package/.allhands/flows/shared/jury/SECURITY_REVIEW.md +86 -0
- package/.allhands/flows/shared/jury/YAGNI_REVIEW.md +82 -0
- package/.allhands/flows/wip/DEBUG_INVESTIGATION.md +162 -0
- package/.allhands/flows/wip/MEMORY_RECALL.md +62 -0
- package/.allhands/harness/ah +131 -0
- package/.allhands/harness/package-lock.json +5292 -0
- package/.allhands/harness/package.json +52 -0
- package/.allhands/harness/src/__tests__/e2e/commands.test.ts +307 -0
- package/.allhands/harness/src/__tests__/e2e/event-loop.test.ts +539 -0
- package/.allhands/harness/src/__tests__/e2e/hooks.test.ts +427 -0
- package/.allhands/harness/src/__tests__/e2e/new-initiative-routing.test.ts +137 -0
- package/.allhands/harness/src/__tests__/e2e/run-e2e.ts +109 -0
- package/.allhands/harness/src/__tests__/e2e/specs-type.test.ts +210 -0
- package/.allhands/harness/src/__tests__/e2e/validation-hooks.test.ts +669 -0
- package/.allhands/harness/src/__tests__/e2e/validation-path-consistency.test.ts +354 -0
- package/.allhands/harness/src/__tests__/e2e/validation.test.ts +528 -0
- package/.allhands/harness/src/__tests__/harness/assertions.ts +318 -0
- package/.allhands/harness/src/__tests__/harness/cli-runner.ts +359 -0
- package/.allhands/harness/src/__tests__/harness/fixture.ts +384 -0
- package/.allhands/harness/src/__tests__/harness/hook-runner.ts +411 -0
- package/.allhands/harness/src/__tests__/harness/index.ts +122 -0
- package/.allhands/harness/src/cli.ts +36 -0
- package/.allhands/harness/src/commands/complexity.ts +177 -0
- package/.allhands/harness/src/commands/context7.ts +202 -0
- package/.allhands/harness/src/commands/docs.ts +557 -0
- package/.allhands/harness/src/commands/hooks.ts +24 -0
- package/.allhands/harness/src/commands/index.ts +51 -0
- package/.allhands/harness/src/commands/knowledge.ts +382 -0
- package/.allhands/harness/src/commands/memories.ts +302 -0
- package/.allhands/harness/src/commands/notify.ts +61 -0
- package/.allhands/harness/src/commands/oracle.ts +158 -0
- package/.allhands/harness/src/commands/perplexity.ts +220 -0
- package/.allhands/harness/src/commands/planning.ts +245 -0
- package/.allhands/harness/src/commands/schema.ts +73 -0
- package/.allhands/harness/src/commands/skills.ts +128 -0
- package/.allhands/harness/src/commands/solutions.ts +353 -0
- package/.allhands/harness/src/commands/spawn.ts +158 -0
- package/.allhands/harness/src/commands/specs.ts +532 -0
- package/.allhands/harness/src/commands/tavily.ts +226 -0
- package/.allhands/harness/src/commands/tools.ts +579 -0
- package/.allhands/harness/src/commands/trace.ts +327 -0
- package/.allhands/harness/src/commands/tui.ts +960 -0
- package/.allhands/harness/src/commands/validate.ts +143 -0
- package/.allhands/harness/src/commands/validation-tools.ts +108 -0
- package/.allhands/harness/src/hooks/context.ts +1442 -0
- package/.allhands/harness/src/hooks/enforcement.ts +170 -0
- package/.allhands/harness/src/hooks/index.ts +54 -0
- package/.allhands/harness/src/hooks/lifecycle.ts +229 -0
- package/.allhands/harness/src/hooks/notification.ts +104 -0
- package/.allhands/harness/src/hooks/observability.ts +551 -0
- package/.allhands/harness/src/hooks/session.ts +88 -0
- package/.allhands/harness/src/hooks/shared.ts +815 -0
- package/.allhands/harness/src/hooks/transcript-parser.ts +208 -0
- package/.allhands/harness/src/hooks/validation.ts +617 -0
- package/.allhands/harness/src/lib/__tests__/ctags.test.ts +244 -0
- package/.allhands/harness/src/lib/__tests__/docs-validation.test.ts +344 -0
- package/.allhands/harness/src/lib/__tests__/mcp-runtime.test.ts +190 -0
- package/.allhands/harness/src/lib/__tests__/schema.test.ts +861 -0
- package/.allhands/harness/src/lib/base-command.ts +198 -0
- package/.allhands/harness/src/lib/cli-daemon.ts +343 -0
- package/.allhands/harness/src/lib/compaction.ts +313 -0
- package/.allhands/harness/src/lib/ctags.ts +497 -0
- package/.allhands/harness/src/lib/docs-validation.ts +907 -0
- package/.allhands/harness/src/lib/event-loop.ts +662 -0
- package/.allhands/harness/src/lib/flows.ts +155 -0
- package/.allhands/harness/src/lib/git.ts +276 -0
- package/.allhands/harness/src/lib/knowledge-worker.ts +72 -0
- package/.allhands/harness/src/lib/knowledge.ts +810 -0
- package/.allhands/harness/src/lib/llm.ts +255 -0
- package/.allhands/harness/src/lib/mcp-client.ts +432 -0
- package/.allhands/harness/src/lib/mcp-daemon.ts +486 -0
- package/.allhands/harness/src/lib/mcp-runtime.ts +418 -0
- package/.allhands/harness/src/lib/notification.ts +115 -0
- package/.allhands/harness/src/lib/opencode/index.ts +70 -0
- package/.allhands/harness/src/lib/opencode/profiles.ts +300 -0
- package/.allhands/harness/src/lib/opencode/prompts/codesearch.md +98 -0
- package/.allhands/harness/src/lib/opencode/prompts/knowledge-aggregator.md +67 -0
- package/.allhands/harness/src/lib/opencode/runner.ts +281 -0
- package/.allhands/harness/src/lib/oracle.ts +926 -0
- package/.allhands/harness/src/lib/planning-utils.ts +150 -0
- package/.allhands/harness/src/lib/planning.ts +605 -0
- package/.allhands/harness/src/lib/pr-review.ts +225 -0
- package/.allhands/harness/src/lib/prompts.ts +522 -0
- package/.allhands/harness/src/lib/schema.ts +418 -0
- package/.allhands/harness/src/lib/schemas/agent-profile.ts +141 -0
- package/.allhands/harness/src/lib/schemas/template-vars.ts +138 -0
- package/.allhands/harness/src/lib/session.ts +164 -0
- package/.allhands/harness/src/lib/specs.ts +348 -0
- package/.allhands/harness/src/lib/tldr.ts +829 -0
- package/.allhands/harness/src/lib/tmux.ts +1051 -0
- package/.allhands/harness/src/lib/trace-store.ts +714 -0
- package/.allhands/harness/src/mcp/__tests__/index.test.ts +46 -0
- package/.allhands/harness/src/mcp/_template.ts +47 -0
- package/.allhands/harness/src/mcp/filesystem.ts +33 -0
- package/.allhands/harness/src/mcp/index.ts +69 -0
- package/.allhands/harness/src/mcp/playwright.ts +34 -0
- package/.allhands/harness/src/mcp/xcodebuild.ts +29 -0
- package/.allhands/harness/src/schemas/docs.schema.json +44 -0
- package/.allhands/harness/src/schemas/settings.schema.json +214 -0
- package/.allhands/harness/src/tui/actions.ts +227 -0
- package/.allhands/harness/src/tui/file-viewer-modal.ts +270 -0
- package/.allhands/harness/src/tui/index.ts +1574 -0
- package/.allhands/harness/src/tui/modal.ts +232 -0
- package/.allhands/harness/src/tui/prompts-pane.ts +186 -0
- package/.allhands/harness/src/tui/status-pane.ts +434 -0
- package/.allhands/harness/tsconfig.json +22 -0
- package/.allhands/harness/vitest.config.ts +13 -0
- package/.allhands/pillars.md +33 -0
- package/.allhands/principles.md +88 -0
- package/.allhands/schemas/alignment.yaml +51 -0
- package/.allhands/schemas/documentation.yaml +10 -0
- package/.allhands/schemas/prompt.yaml +92 -0
- package/.allhands/schemas/skill.yaml +34 -0
- package/.allhands/schemas/solution.yaml +131 -0
- package/.allhands/schemas/spec.yaml +67 -0
- package/.allhands/schemas/validation-suite.yaml +49 -0
- package/.allhands/schemas/workflow.yaml +51 -0
- package/.allhands/settings.json +57 -0
- package/.allhands/skills/claude-code-patterns/SKILL.md +60 -0
- package/.allhands/skills/claude-code-patterns/docs/context-hygiene.md +19 -0
- package/.allhands/skills/harness-maintenance/SKILL.md +449 -0
- package/.allhands/skills/harness-maintenance/references/core-architecture.md +187 -0
- package/.allhands/skills/harness-maintenance/references/harness-skills.md +87 -0
- package/.allhands/skills/harness-maintenance/references/knowledge-compounding.md +78 -0
- package/.allhands/skills/harness-maintenance/references/tools-commands-mcp-hooks.md +115 -0
- package/.allhands/skills/harness-maintenance/references/validation-tooling.md +77 -0
- package/.allhands/skills/harness-maintenance/references/writing-flows.md +84 -0
- package/.allhands/validation/browser-automation.md +109 -0
- package/.allhands/validation/xcode-automation.md +195 -0
- package/.allhands/workflows/documentation.md +86 -0
- package/.allhands/workflows/investigation.md +81 -0
- package/.allhands/workflows/milestone.md +91 -0
- package/.allhands/workflows/optimization.md +85 -0
- package/.allhands/workflows/refactor.md +99 -0
- package/.allhands/workflows/triage.md +81 -0
- package/.claude/README.md +1 -0
- package/.claude/agents/explorer.md +10 -0
- package/.claude/agents/researcher.md +11 -0
- package/.claude/agents/task-runner.md +8 -0
- package/.claude/settings.json +231 -0
- package/.env.ai.example +7 -0
- package/.github/workflows/npm-publish.yml +69 -0
- package/.internal.json +45 -0
- package/.tldr/config.json +11 -0
- package/.tldrignore +90 -0
- package/CLAUDE.md +6 -0
- package/README.md +98 -0
- package/bin/sync-cli.js +7552 -0
- package/concerns.md +7 -0
- package/docs/README.md +41 -0
- package/docs/agents/README.md +24 -0
- package/docs/agents/agent-configuration-system.md +86 -0
- package/docs/agents/execution-agents.md +50 -0
- package/docs/agents/knowledge-agents.md +61 -0
- package/docs/agents/orchestration-agent.md +57 -0
- package/docs/agents/planning-agents.md +84 -0
- package/docs/agents/quality-review-agents.md +67 -0
- package/docs/agents/workflow-agent-orchestration.md +69 -0
- package/docs/flows/README.md +44 -0
- package/docs/flows/compounding.md +126 -0
- package/docs/flows/coordination.md +72 -0
- package/docs/flows/core-harness-integration.md +63 -0
- package/docs/flows/documentation-orchestration.md +98 -0
- package/docs/flows/e2e-test-plan-building.md +83 -0
- package/docs/flows/emergent-refinement.md +104 -0
- package/docs/flows/flow-authoring-and-mcp-tools.md +89 -0
- package/docs/flows/judge-reviewing.md +112 -0
- package/docs/flows/plan-deepening-and-research.md +107 -0
- package/docs/flows/plan-review-jury.md +114 -0
- package/docs/flows/pr-reviewing.md +54 -0
- package/docs/flows/prompt-task-execution.md +119 -0
- package/docs/flows/spec-planning.md +162 -0
- package/docs/flows/type-specific-scoping-flows.md +49 -0
- package/docs/flows/validation-and-skills-integration.md +145 -0
- package/docs/flows/wip/wip-flows.md +102 -0
- package/docs/harness/README.md +23 -0
- package/docs/harness/agent-profiles.md +84 -0
- package/docs/harness/cli/README.md +24 -0
- package/docs/harness/cli/cli-entry-and-command-discovery.md +91 -0
- package/docs/harness/cli/docs-command.md +87 -0
- package/docs/harness/cli/knowledge-command.md +91 -0
- package/docs/harness/cli/minor-cli-commands.md +65 -0
- package/docs/harness/cli/oracle-command.md +113 -0
- package/docs/harness/cli/planning-command.md +95 -0
- package/docs/harness/cli/schema-and-validation-commands.md +154 -0
- package/docs/harness/cli/search-commands.md +97 -0
- package/docs/harness/cli/spawn-command.md +136 -0
- package/docs/harness/cli/specs-command.md +102 -0
- package/docs/harness/cli/tools-command.md +122 -0
- package/docs/harness/cli/trace-command.md +122 -0
- package/docs/harness/cli-daemon.md +92 -0
- package/docs/harness/event-loop.md +184 -0
- package/docs/harness/hooks/README.md +15 -0
- package/docs/harness/hooks/context-hooks.md +96 -0
- package/docs/harness/hooks/lifecycle-and-observability-hooks.md +135 -0
- package/docs/harness/hooks/validation-hooks.md +97 -0
- package/docs/harness/test-harness.md +149 -0
- package/docs/harness/tui.md +176 -0
- package/docs/memories.md +20 -0
- package/docs/solutions/agentic-issues/premature-agent-deletion-tui-action-dependency-20260130.md +49 -0
- package/docs/solutions/agentic-issues/ref-anchor-scope-mismatch-skill-references-20260131.md +55 -0
- package/docs/solutions/agentic-issues/tautological-tests-routing-20260131.md +52 -0
- package/docs/solutions/integration_issue/blocktool-output-format-mismatch-hook-runner-20260130.md +52 -0
- package/docs/solutions/integration_issue/dual-validation-path-divergence-schema-20260130.md +66 -0
- package/docs/solutions/security-issues/unsanitized-domain-path-join-20260131.md +52 -0
- package/docs/solutions/test-failures/event-loop-mock-ordering-checkAgentWindows-20260130.md +63 -0
- package/docs/sync-cli/README.md +19 -0
- package/docs/sync-cli/cli-entrypoint-and-commands.md +39 -0
- package/docs/sync-cli/commands/README.md +11 -0
- package/docs/sync-cli/commands/pull-manifest-command.md +36 -0
- package/docs/sync-cli/commands/push-command.md +84 -0
- package/docs/sync-cli/commands/sync-command.md +71 -0
- package/docs/sync-cli/systems/README.md +14 -0
- package/docs/sync-cli/systems/git-and-github-integration.md +49 -0
- package/docs/sync-cli/systems/interactive-ui.md +43 -0
- package/docs/sync-cli/systems/manifest-and-distribution.md +51 -0
- package/docs/sync-cli/systems/path-resolution.md +42 -0
- package/package.json +46 -0
- package/scripts/install-shim.sh +40 -0
- package/scripts/pre-pack.sh +25 -0
- package/specs/harness-maintenance-skill.spec.md +138 -0
- package/specs/roadmap/git-spec-lifecycle-management.spec.md +113 -0
- package/specs/sync-init-flag.spec.md +117 -0
- package/specs/unified-workflow-orchestration.spec.md +250 -0
- package/specs/validation-tooling-practice.spec.md +98 -0
- package/specs/workflow-domain-configuration.spec.md +265 -0
- package/src/commands/pull-manifest.ts +31 -0
- package/src/commands/push.ts +344 -0
- package/src/commands/sync.ts +289 -0
- package/src/lib/constants.ts +10 -0
- package/src/lib/dotfiles.ts +36 -0
- package/src/lib/fs-utils.ts +18 -0
- package/src/lib/gh.ts +40 -0
- package/src/lib/git.ts +63 -0
- package/src/lib/gitignore.ts +167 -0
- package/src/lib/manifest.ts +121 -0
- package/src/lib/marker-sync.ts +39 -0
- package/src/lib/paths.ts +38 -0
- package/src/lib/target-lines.ts +66 -0
- package/src/lib/ui.ts +78 -0
- package/src/sync-cli.ts +120 -0
- package/target-lines.json +23 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for ctags utilities.
|
|
3
|
+
*
|
|
4
|
+
* Note: These tests require universal-ctags to be installed.
|
|
5
|
+
* Tests are skipped if ctags is not available.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, it, beforeAll } from "vitest";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { writeFileSync, mkdirSync, rmSync, existsSync } from "fs";
|
|
11
|
+
import {
|
|
12
|
+
checkCtagsAvailable,
|
|
13
|
+
generateCtagsIndex,
|
|
14
|
+
lookupSymbol,
|
|
15
|
+
searchSymbol,
|
|
16
|
+
getFileSymbols,
|
|
17
|
+
generateFileCtags,
|
|
18
|
+
findSymbolInFile,
|
|
19
|
+
CtagsIndex,
|
|
20
|
+
} from "../ctags.js";
|
|
21
|
+
|
|
22
|
+
// Check if ctags is available for tests
|
|
23
|
+
const ctagsCheck = checkCtagsAvailable();
|
|
24
|
+
const hasCtagsInstalled = ctagsCheck.available;
|
|
25
|
+
|
|
26
|
+
describe("checkCtagsAvailable", () => {
|
|
27
|
+
it("returns availability status", () => {
|
|
28
|
+
const result = checkCtagsAvailable();
|
|
29
|
+
expect(result).toHaveProperty("available");
|
|
30
|
+
|
|
31
|
+
if (result.available) {
|
|
32
|
+
expect(result.version).toBeDefined();
|
|
33
|
+
} else {
|
|
34
|
+
expect(result.error).toBeDefined();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Skip remaining tests if ctags not installed
|
|
40
|
+
describe.skipIf(!hasCtagsInstalled)("ctags with installed ctags", () => {
|
|
41
|
+
const testDir = join(process.cwd(), ".test-ctags-temp");
|
|
42
|
+
const testFile = join(testDir, "test-sample.ts");
|
|
43
|
+
|
|
44
|
+
const sampleCode = `
|
|
45
|
+
// Test TypeScript file for ctags tests
|
|
46
|
+
|
|
47
|
+
export class MyClass {
|
|
48
|
+
private value: number;
|
|
49
|
+
|
|
50
|
+
constructor(value: number) {
|
|
51
|
+
this.value = value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getValue(): number {
|
|
55
|
+
return this.value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setValue(newValue: number): void {
|
|
59
|
+
this.value = newValue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface MyInterface {
|
|
64
|
+
name: string;
|
|
65
|
+
age: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function myFunction(arg: string): string {
|
|
69
|
+
return arg.toUpperCase();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const MY_CONSTANT = 42;
|
|
73
|
+
|
|
74
|
+
type MyType = string | number;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
beforeAll(() => {
|
|
78
|
+
// Create test directory and file
|
|
79
|
+
if (existsSync(testDir)) {
|
|
80
|
+
rmSync(testDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
mkdirSync(testDir, { recursive: true });
|
|
83
|
+
writeFileSync(testFile, sampleCode, "utf-8");
|
|
84
|
+
|
|
85
|
+
return () => {
|
|
86
|
+
// Cleanup
|
|
87
|
+
if (existsSync(testDir)) {
|
|
88
|
+
rmSync(testDir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("generateFileCtags", () => {
|
|
94
|
+
it("generates ctags for a single file", () => {
|
|
95
|
+
const result = generateFileCtags(testFile, testDir);
|
|
96
|
+
|
|
97
|
+
expect(result.success).toBe(true);
|
|
98
|
+
expect(result.entries.length).toBeGreaterThan(0);
|
|
99
|
+
|
|
100
|
+
// Should find known symbols
|
|
101
|
+
const symbolNames = result.entries.map((e) => e.name);
|
|
102
|
+
expect(symbolNames).toContain("MyClass");
|
|
103
|
+
expect(symbolNames).toContain("myFunction");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("returns error for non-existent file", () => {
|
|
107
|
+
const result = generateFileCtags(join(testDir, "nonexistent.ts"), testDir);
|
|
108
|
+
|
|
109
|
+
expect(result.success).toBe(false);
|
|
110
|
+
expect(result.error).toBeDefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("includes symbol metadata", () => {
|
|
114
|
+
const result = generateFileCtags(testFile, testDir);
|
|
115
|
+
expect(result.success).toBe(true);
|
|
116
|
+
|
|
117
|
+
const classEntry = result.entries.find((e) => e.name === "MyClass");
|
|
118
|
+
expect(classEntry).toBeDefined();
|
|
119
|
+
expect(classEntry!.kind).toBe("class");
|
|
120
|
+
expect(classEntry!.line).toBeGreaterThan(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("generateCtagsIndex", () => {
|
|
125
|
+
it("generates index for directory", () => {
|
|
126
|
+
const result = generateCtagsIndex(testDir);
|
|
127
|
+
|
|
128
|
+
expect(result.success).toBe(true);
|
|
129
|
+
expect(result.entryCount).toBeGreaterThan(0);
|
|
130
|
+
expect(result.index.size).toBeGreaterThan(0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("indexes symbols by file and name", () => {
|
|
134
|
+
const { index, success } = generateCtagsIndex(testDir);
|
|
135
|
+
expect(success).toBe(true);
|
|
136
|
+
|
|
137
|
+
// The file path should be relative to cwd
|
|
138
|
+
const relPath = "test-sample.ts";
|
|
139
|
+
const fileMap = index.get(relPath);
|
|
140
|
+
expect(fileMap).toBeDefined();
|
|
141
|
+
|
|
142
|
+
// Should have MyClass
|
|
143
|
+
const myClass = fileMap!.get("MyClass");
|
|
144
|
+
expect(myClass).toBeDefined();
|
|
145
|
+
expect(myClass!.length).toBeGreaterThan(0);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("lookupSymbol", () => {
|
|
150
|
+
let index: CtagsIndex;
|
|
151
|
+
|
|
152
|
+
beforeAll(() => {
|
|
153
|
+
const result = generateCtagsIndex(testDir);
|
|
154
|
+
index = result.index;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("finds symbol in specific file", () => {
|
|
158
|
+
const entries = lookupSymbol(index, "test-sample.ts", "MyClass");
|
|
159
|
+
|
|
160
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
161
|
+
expect(entries[0].name).toBe("MyClass");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("returns empty array for non-existent symbol", () => {
|
|
165
|
+
const entries = lookupSymbol(index, "test-sample.ts", "NonExistent");
|
|
166
|
+
expect(entries).toHaveLength(0);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("returns empty array for non-existent file", () => {
|
|
170
|
+
const entries = lookupSymbol(index, "nonexistent.ts", "MyClass");
|
|
171
|
+
expect(entries).toHaveLength(0);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("searchSymbol", () => {
|
|
176
|
+
let index: CtagsIndex;
|
|
177
|
+
|
|
178
|
+
beforeAll(() => {
|
|
179
|
+
const result = generateCtagsIndex(testDir);
|
|
180
|
+
index = result.index;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("finds symbol across all files", () => {
|
|
184
|
+
const results = searchSymbol(index, "MyClass");
|
|
185
|
+
|
|
186
|
+
expect(results.length).toBeGreaterThan(0);
|
|
187
|
+
expect(results[0].name).toBe("MyClass");
|
|
188
|
+
expect(results[0].file).toBeDefined();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("returns empty for non-existent symbol", () => {
|
|
192
|
+
const results = searchSymbol(index, "NonExistent");
|
|
193
|
+
expect(results).toHaveLength(0);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("getFileSymbols", () => {
|
|
198
|
+
let index: CtagsIndex;
|
|
199
|
+
|
|
200
|
+
beforeAll(() => {
|
|
201
|
+
const result = generateCtagsIndex(testDir);
|
|
202
|
+
index = result.index;
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("returns all symbols in a file", () => {
|
|
206
|
+
const symbols = getFileSymbols(index, "test-sample.ts");
|
|
207
|
+
|
|
208
|
+
expect(symbols.length).toBeGreaterThan(0);
|
|
209
|
+
|
|
210
|
+
// Should include class, function, interface
|
|
211
|
+
const names = symbols.map((s) => s.name);
|
|
212
|
+
expect(names).toContain("MyClass");
|
|
213
|
+
expect(names).toContain("myFunction");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("returns symbols sorted by line number", () => {
|
|
217
|
+
const symbols = getFileSymbols(index, "test-sample.ts");
|
|
218
|
+
|
|
219
|
+
for (let i = 1; i < symbols.length; i++) {
|
|
220
|
+
expect(symbols[i].line).toBeGreaterThanOrEqual(symbols[i - 1].line);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("returns empty for non-existent file", () => {
|
|
225
|
+
const symbols = getFileSymbols(index, "nonexistent.ts");
|
|
226
|
+
expect(symbols).toHaveLength(0);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe("findSymbolInFile", () => {
|
|
231
|
+
it("finds symbol in file", () => {
|
|
232
|
+
const entry = findSymbolInFile(testFile, "MyClass", testDir);
|
|
233
|
+
|
|
234
|
+
expect(entry).not.toBeNull();
|
|
235
|
+
expect(entry!.name).toBe("MyClass");
|
|
236
|
+
expect(entry!.kind).toBe("class");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("returns null for non-existent symbol", () => {
|
|
240
|
+
const entry = findSymbolInFile(testFile, "NonExistent", testDir);
|
|
241
|
+
expect(entry).toBeNull();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for docs validation utilities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
6
|
+
import {
|
|
7
|
+
REF_PATTERN,
|
|
8
|
+
PLACEHOLDER_PATTERN,
|
|
9
|
+
extractRefs,
|
|
10
|
+
validateFrontMatter,
|
|
11
|
+
detectPlaceholders,
|
|
12
|
+
countCodeBlocks,
|
|
13
|
+
hasCapabilityList,
|
|
14
|
+
getBlobHashForFile,
|
|
15
|
+
batchGetBlobHashes,
|
|
16
|
+
} from "../docs-validation.js";
|
|
17
|
+
|
|
18
|
+
describe("REF_PATTERN", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
REF_PATTERN.lastIndex = 0;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("matches symbol reference", () => {
|
|
24
|
+
const match = REF_PATTERN.exec("[ref:src/lib/foo.ts:MyClass:abc1234]");
|
|
25
|
+
expect(match).not.toBeNull();
|
|
26
|
+
expect(match![1]).toBe("src/lib/foo.ts");
|
|
27
|
+
expect(match![2]).toBe("MyClass");
|
|
28
|
+
expect(match![3]).toBe("abc1234");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("matches file-only reference", () => {
|
|
32
|
+
const match = REF_PATTERN.exec("[ref:src/lib/foo.ts::def5678]");
|
|
33
|
+
expect(match).not.toBeNull();
|
|
34
|
+
expect(match![1]).toBe("src/lib/foo.ts");
|
|
35
|
+
expect(match![2]).toBe("");
|
|
36
|
+
expect(match![3]).toBe("def5678");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("matches multiple refs", () => {
|
|
40
|
+
const content = `
|
|
41
|
+
See [ref:src/a.ts:foo:abc1234] for implementation.
|
|
42
|
+
Also [ref:src/b.ts::def5678] is relevant.
|
|
43
|
+
`;
|
|
44
|
+
const matches: string[] = [];
|
|
45
|
+
let match;
|
|
46
|
+
while ((match = REF_PATTERN.exec(content)) !== null) {
|
|
47
|
+
matches.push(match[0]);
|
|
48
|
+
}
|
|
49
|
+
expect(matches).toHaveLength(2);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("does not match invalid hash (too short)", () => {
|
|
53
|
+
const match = REF_PATTERN.exec("[ref:src/foo.ts:bar:abc]");
|
|
54
|
+
expect(match).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("extractRefs", () => {
|
|
59
|
+
it("extracts symbol refs", () => {
|
|
60
|
+
const content = "See [ref:src/foo.ts:MyClass:abc1234] for details.";
|
|
61
|
+
const refs = extractRefs(content, "docs/test.md");
|
|
62
|
+
|
|
63
|
+
expect(refs).toHaveLength(1);
|
|
64
|
+
expect(refs[0]).toEqual({
|
|
65
|
+
reference: "[ref:src/foo.ts:MyClass:abc1234]",
|
|
66
|
+
file: "src/foo.ts",
|
|
67
|
+
symbol: "MyClass",
|
|
68
|
+
hash: "abc1234",
|
|
69
|
+
isFileOnly: false,
|
|
70
|
+
docFile: "docs/test.md",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("extracts file-only refs", () => {
|
|
75
|
+
const content = "See [ref:src/foo.ts::abc1234] for details.";
|
|
76
|
+
const refs = extractRefs(content, "docs/test.md");
|
|
77
|
+
|
|
78
|
+
expect(refs).toHaveLength(1);
|
|
79
|
+
expect(refs[0].isFileOnly).toBe(true);
|
|
80
|
+
expect(refs[0].symbol).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("extracts multiple refs", () => {
|
|
84
|
+
const content = `
|
|
85
|
+
[ref:a.ts:foo:1111111]
|
|
86
|
+
[ref:b.ts::2222222]
|
|
87
|
+
[ref:c.ts:bar:3333333]
|
|
88
|
+
`;
|
|
89
|
+
const refs = extractRefs(content, "test.md");
|
|
90
|
+
expect(refs).toHaveLength(3);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("returns empty array for no refs", () => {
|
|
94
|
+
const content = "No references here.";
|
|
95
|
+
const refs = extractRefs(content, "test.md");
|
|
96
|
+
expect(refs).toHaveLength(0);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("validateFrontMatter", () => {
|
|
101
|
+
it("validates correct front matter", () => {
|
|
102
|
+
const content = `---
|
|
103
|
+
description: This is a test document
|
|
104
|
+
relevant_files:
|
|
105
|
+
- src/foo.ts
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
Content here.`;
|
|
109
|
+
|
|
110
|
+
const result = validateFrontMatter(content);
|
|
111
|
+
expect(result.valid).toBe(true);
|
|
112
|
+
expect(result.error).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("fails if missing front matter", () => {
|
|
116
|
+
const content = "No front matter here.";
|
|
117
|
+
const result = validateFrontMatter(content);
|
|
118
|
+
expect(result.valid).toBe(false);
|
|
119
|
+
expect(result.error).toContain("Missing front matter");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("fails if missing description", () => {
|
|
123
|
+
const content = `---
|
|
124
|
+
title: Test
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
Content.`;
|
|
128
|
+
|
|
129
|
+
const result = validateFrontMatter(content);
|
|
130
|
+
expect(result.valid).toBe(false);
|
|
131
|
+
expect(result.error).toContain("description");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("fails if description is empty", () => {
|
|
135
|
+
const content = `---
|
|
136
|
+
description: ""
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
Content.`;
|
|
140
|
+
|
|
141
|
+
const result = validateFrontMatter(content);
|
|
142
|
+
expect(result.valid).toBe(false);
|
|
143
|
+
expect(result.error).toContain("Empty");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("fails if relevant_files is not array", () => {
|
|
147
|
+
const content = `---
|
|
148
|
+
description: Test
|
|
149
|
+
relevant_files: "not-an-array"
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
Content.`;
|
|
153
|
+
|
|
154
|
+
const result = validateFrontMatter(content);
|
|
155
|
+
expect(result.valid).toBe(false);
|
|
156
|
+
expect(result.error).toContain("array");
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("detectPlaceholders", () => {
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
PLACEHOLDER_PATTERN.lastIndex = 0;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("detects abc123 placeholder", () => {
|
|
166
|
+
const content = "[ref:foo.ts:bar:abc123]";
|
|
167
|
+
const placeholders = detectPlaceholders(content);
|
|
168
|
+
expect(placeholders).toHaveLength(1);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("detects 000000 placeholder", () => {
|
|
172
|
+
const content = "[ref:foo.ts:bar:000000]";
|
|
173
|
+
const placeholders = detectPlaceholders(content);
|
|
174
|
+
expect(placeholders).toHaveLength(1);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("detects hash prefix placeholder", () => {
|
|
178
|
+
const content = "[ref:foo.ts:bar:hash12]";
|
|
179
|
+
const placeholders = detectPlaceholders(content);
|
|
180
|
+
expect(placeholders).toHaveLength(1);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("detects test prefix placeholder", () => {
|
|
184
|
+
const content = "[ref:foo.ts:bar:test99]";
|
|
185
|
+
const placeholders = detectPlaceholders(content);
|
|
186
|
+
expect(placeholders).toHaveLength(1);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("does not flag valid hashes", () => {
|
|
190
|
+
const content = "[ref:foo.ts:bar:a1b2c3d]";
|
|
191
|
+
const placeholders = detectPlaceholders(content);
|
|
192
|
+
expect(placeholders).toHaveLength(0);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("detects multiple placeholders", () => {
|
|
196
|
+
const content = `
|
|
197
|
+
[ref:a.ts:x:abc123]
|
|
198
|
+
[ref:b.ts:y:test00]
|
|
199
|
+
`;
|
|
200
|
+
const placeholders = detectPlaceholders(content);
|
|
201
|
+
expect(placeholders).toHaveLength(2);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("countCodeBlocks", () => {
|
|
206
|
+
it("counts single code block", () => {
|
|
207
|
+
const content = "```typescript\nconst x = 1;\n```";
|
|
208
|
+
expect(countCodeBlocks(content)).toBe(1);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("counts multiple code blocks", () => {
|
|
212
|
+
const content = `
|
|
213
|
+
\`\`\`typescript
|
|
214
|
+
const x = 1;
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
Some text.
|
|
218
|
+
|
|
219
|
+
\`\`\`bash
|
|
220
|
+
echo "hello"
|
|
221
|
+
\`\`\`
|
|
222
|
+
`;
|
|
223
|
+
expect(countCodeBlocks(content)).toBe(2);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("counts plain code blocks", () => {
|
|
227
|
+
const content = "```\nplain code\n```";
|
|
228
|
+
expect(countCodeBlocks(content)).toBe(1);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("returns 0 for no code blocks", () => {
|
|
232
|
+
const content = "Just some text.";
|
|
233
|
+
expect(countCodeBlocks(content)).toBe(0);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe("hasCapabilityList", () => {
|
|
238
|
+
it("detects Command/Purpose table", () => {
|
|
239
|
+
const content = `
|
|
240
|
+
| Command | Purpose |
|
|
241
|
+
|---------|---------|
|
|
242
|
+
| foo | Does X |
|
|
243
|
+
`;
|
|
244
|
+
expect(hasCapabilityList(content)).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("detects Option/Description table", () => {
|
|
248
|
+
const content = `
|
|
249
|
+
| Option | Description |
|
|
250
|
+
|--------|-------------|
|
|
251
|
+
| --foo | Enables X |
|
|
252
|
+
`;
|
|
253
|
+
expect(hasCapabilityList(content)).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("detects Flag/Purpose table", () => {
|
|
257
|
+
const content = `
|
|
258
|
+
| Flag | Purpose |
|
|
259
|
+
|------|---------|
|
|
260
|
+
| -v | Verbose |
|
|
261
|
+
`;
|
|
262
|
+
expect(hasCapabilityList(content)).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("does not flag regular tables", () => {
|
|
266
|
+
const content = `
|
|
267
|
+
| Name | Age |
|
|
268
|
+
|------|-----|
|
|
269
|
+
| John | 30 |
|
|
270
|
+
`;
|
|
271
|
+
expect(hasCapabilityList(content)).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("does not flag plain text", () => {
|
|
275
|
+
const content = "Just some text about Command and Purpose.";
|
|
276
|
+
expect(hasCapabilityList(content)).toBe(false);
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe("getBlobHashForFile", () => {
|
|
281
|
+
// These tests require running inside a git repo with committed files.
|
|
282
|
+
// They use the project's own files as fixtures.
|
|
283
|
+
const projectRoot = process.cwd();
|
|
284
|
+
|
|
285
|
+
it("returns a 7-char hex hash for a committed file", () => {
|
|
286
|
+
const result = getBlobHashForFile("package.json", projectRoot);
|
|
287
|
+
expect(result.success).toBe(true);
|
|
288
|
+
expect(result.hash).toMatch(/^[a-f0-9]{7}$/);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("handles absolute paths by normalizing to relative", () => {
|
|
292
|
+
const absPath = `${projectRoot}/package.json`;
|
|
293
|
+
const result = getBlobHashForFile(absPath, projectRoot);
|
|
294
|
+
expect(result.success).toBe(true);
|
|
295
|
+
expect(result.hash).toMatch(/^[a-f0-9]{7}$/);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("returns failure for non-existent file", () => {
|
|
299
|
+
const result = getBlobHashForFile("this-file-does-not-exist.xyz", projectRoot);
|
|
300
|
+
expect(result.success).toBe(false);
|
|
301
|
+
expect(result.hash).toBe("0000000");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("returns consistent hash for the same file content", () => {
|
|
305
|
+
const result1 = getBlobHashForFile("package.json", projectRoot);
|
|
306
|
+
const result2 = getBlobHashForFile("package.json", projectRoot);
|
|
307
|
+
expect(result1.hash).toBe(result2.hash);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe("batchGetBlobHashes", () => {
|
|
312
|
+
const projectRoot = process.cwd();
|
|
313
|
+
|
|
314
|
+
it("returns hashes for multiple files", () => {
|
|
315
|
+
const files = ["package.json", "tsconfig.json"];
|
|
316
|
+
const results = batchGetBlobHashes(files, projectRoot);
|
|
317
|
+
expect(results.size).toBe(2);
|
|
318
|
+
for (const file of files) {
|
|
319
|
+
const result = results.get(file);
|
|
320
|
+
expect(result).toBeDefined();
|
|
321
|
+
expect(result!.success).toBe(true);
|
|
322
|
+
expect(result!.hash).toMatch(/^[a-f0-9]{7}$/);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("returns same hash as individual getBlobHashForFile", () => {
|
|
327
|
+
const file = "package.json";
|
|
328
|
+
const individual = getBlobHashForFile(file, projectRoot);
|
|
329
|
+
const batch = batchGetBlobHashes([file], projectRoot);
|
|
330
|
+
expect(batch.get(file)!.hash).toBe(individual.hash);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("handles empty file list", () => {
|
|
334
|
+
const results = batchGetBlobHashes([], projectRoot);
|
|
335
|
+
expect(results.size).toBe(0);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("handles mix of existing and non-existing files", () => {
|
|
339
|
+
const files = ["package.json", "nonexistent-file.xyz"];
|
|
340
|
+
const results = batchGetBlobHashes(files, projectRoot);
|
|
341
|
+
expect(results.get("package.json")!.success).toBe(true);
|
|
342
|
+
expect(results.get("nonexistent-file.xyz")!.success).toBe(false);
|
|
343
|
+
});
|
|
344
|
+
});
|