AkurAI Build
Menu

popagent

public

Latest change ac1b32888da9db0a129d0e91ff5cb4dac22c12f0 - Add workspace documentation RAG by AkurAI Build

import { afterEach, describe, expect, test } from "bun:test";
import { mkdir, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { DocumentationFiles, DocumentationConflictError } from "./documentation-files";

const roots: string[] = [];

async function fixture() {
  const root = join(import.meta.dir, `.tmp-docs-${crypto.randomUUID()}`);
  roots.push(root);
  await mkdir(join(root, "repo", "docs", "guide"), { recursive: true });
  await writeFile(join(root, "repo", "README.md"), "# Project\n\nHome");
  await writeFile(join(root, "repo", "docs", "guide", "start.md"), "# Start here\n\nHello");
  await writeFile(join(root, "repo", "docs", "ignored.txt"), "ignore");
  const workspaces = {
    resolveRepository: async () => ({
      workspace: { id: "workspace-1", name: "Project", repositoryPath: "repo", createdAt: "", updatedAt: "" },
      path: join(root, "repo"),
    }),
  };
  return { docs: new DocumentationFiles(workspaces), root };
}

afterEach(async () => {
  await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});

describe("DocumentationFiles", () => {
  test("lists only README.md and contained docs Markdown with heading titles", async () => {
    const { docs } = await fixture();
    expect(await docs.list("workspace-1")).toEqual([
      expect.objectContaining({ path: "README.md", title: "Project" }),
      expect.objectContaining({ path: "docs/guide/start.md", title: "Start here" }),
    ]);
  });

  test("creates, updates, and deletes Markdown using content revisions", async () => {
    const { docs } = await fixture();
    const created = await docs.save("workspace-1", { path: "docs/architecture.md", content: "# Architecture\n" });
    expect(created.title).toBe("Architecture");
    await expect(docs.save("workspace-1", { path: created.path, content: "stale", revision: "bad" }))
      .rejects.toBeInstanceOf(DocumentationConflictError);
    const updated = await docs.save("workspace-1", { path: created.path, content: "# System\n", revision: created.revision });
    expect(updated.revision).not.toBe(created.revision);
    const renamed = await docs.move("workspace-1", updated.path, "docs/system/design.md", updated.revision);
    expect(renamed.path).toBe("docs/system/design.md");
    await expect(docs.remove("workspace-1", renamed.path, created.revision))
      .rejects.toBeInstanceOf(DocumentationConflictError);
    await docs.remove("workspace-1", renamed.path, renamed.revision);
    await expect(docs.read("workspace-1", renamed.path)).rejects.toThrow("Documentation page not found");
  });

  test("rejects paths outside the managed Markdown roots", async () => {
    const { docs } = await fixture();
    await expect(docs.save("workspace-1", { path: "../secret.md", content: "no" })).rejects.toThrow("Invalid documentation path");
    await expect(docs.save("workspace-1", { path: "src/private.md", content: "no" })).rejects.toThrow("Invalid documentation path");
    await expect(docs.save("workspace-1", { path: "docs/.private/page.md", content: "no" })).rejects.toThrow("Invalid documentation path");
    await expect(docs.save("workspace-1", { path: "docs/not-markdown.txt", content: "no" })).rejects.toThrow("Invalid documentation path");
  });
});