spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: tests/repograph/repograph.test.ts4 * Description: RepoGraph tests — TS/Python extraction, symbol queries, refs directions, skeletons (v2 §3).5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";11import { tmpdir } from "node:os";12import { join } from "node:path";13import { describe, expect, it } from "vitest";14import { extractFile, RepoGraphService } from "../../src/repograph/index.js";15import { LocalWorkspace } from "../../src/workspace/index.js";1617const TS_SAMPLE = `/**18 * Handles authentication.19 */20export async function handleAuth(token: string): Promise<boolean> {21 return verifyToken(token);22}2324export class AuthController {25 /** Login entrypoint. */26 async login(user: string): Promise<void> {27 await handleAuth(user);28 }29}3031export interface Session { id: string }32export type AuthResult = boolean;33const secret = "x";34import { verifyToken } from "./token.js";35`;3637describe("extractFile (TS)", () => {38 it("extracts functions, classes, methods, types, and imports", () => {39 const result = extractFile("src/auth.ts", TS_SAMPLE);40 const names = result.symbols.map((symbol) => `${symbol.kind}:${symbol.name}`);41 expect(names).toContain("function:handleAuth");42 expect(names).toContain("class:AuthController");43 expect(names).toContain("method:login");44 expect(names).toContain("type:Session");45 expect(names).toContain("type:AuthResult");46 expect(names).toContain("variable:secret");47 const auth = result.symbols.find((symbol) => symbol.name === "handleAuth");48 expect(auth?.docComment).toContain("Handles authentication");49 expect(auth?.exported).toBe(true);50 expect(result.imports[0]?.spec).toBe("./token.js");51 expect(result.imports[0]?.names).toContain("verifyToken");52 });53});5455describe("extractFile (Python)", () => {56 it("extracts defs, classes, and imports", () => {57 const result = extractFile("app.py", "import os\nfrom flask import Flask\n\nclass App:\n def run(self):\n pass\n\ndef main():\n pass\n");58 const names = result.symbols.map((symbol) => `${symbol.kind}:${symbol.name}`);59 expect(names).toContain("class:App");60 expect(names).toContain("method:run");61 expect(names).toContain("function:main");62 expect(result.imports.map((imp) => imp.spec)).toEqual(["os", "flask"]);63 });64});6566describe("RepoGraphService", () => {67 function makeRepo(): string {68 const dir = mkdtempSync(join(tmpdir(), "khaelor-repograph-"));69 mkdirSync(join(dir, "src"), { recursive: true });70 writeFileSync(join(dir, "src", "auth.ts"), TS_SAMPLE);71 writeFileSync(72 join(dir, "src", "caller.ts"),73 `import { handleAuth } from "./auth.js";\nexport function guard(): void {\n void handleAuth("t");\n}\n`,74 );75 return dir;76 }7778 it("answers symbol queries with wildcards, kind, and scope filters", async () => {79 const service = new RepoGraphService({ workspace: new LocalWorkspace(makeRepo()) });80 const exact = await service.querySymbols("handleAuth");81 expect(exact).toHaveLength(1);82 expect(exact[0]?.file).toBe("src/auth.ts");83 const wildcard = await service.querySymbols("*Controller", "class");84 expect(wildcard.map((hit) => hit.symbol)).toEqual(["AuthController"]);85 const shorthand = await service.querySymbols("class:*Controller");86 expect(shorthand).toHaveLength(1);87 const scoped = await service.querySymbols("handleAuth", undefined, "docs/**");88 expect(scoped).toHaveLength(0);89 });9091 it("finds importers and callers of a symbol", async () => {92 const service = new RepoGraphService({ workspace: new LocalWorkspace(makeRepo()) });93 const importers = await service.queryRefs("handleAuth", "importers");94 expect(importers.some((hit) => hit.file === "src/caller.ts")).toBe(true);95 const callers = await service.queryRefs("handleAuth", "callers");96 expect(callers.some((hit) => hit.file === "src/caller.ts" && hit.context.includes("handleAuth"))).toBe(true);97 });9899 it("produces a token-cheap skeleton with line anchors", async () => {100 const service = new RepoGraphService({ workspace: new LocalWorkspace(makeRepo()) });101 const skeleton = await service.skeleton("src/auth.ts");102 expect(skeleton).not.toBeNull();103 expect(skeleton).toContain("export async function handleAuth");104 expect(skeleton).toContain("re-read the file if you need bodies");105 expect((skeleton as string).length).toBeLessThan(TS_SAMPLE.length * 2);106 });107});108