Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/lib/__tests__/wiki-graph.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* wiki-graph.test.ts — regression tests for frontmatter-scoped title/type extraction
*
* extractTitle/extractType previously searched for `title:`/`type:` anywhere in the
* whole file content, not just inside the `---...---` frontmatter block, because the
* lazy `[\s\S]*?` in their regexes was never required to stop at the closing `---`.
* A body line that merely starts with `title:` or `type:` (plain prose, not YAML)
* could be misread as the frontmatter value.
*/
import { describe, it, expect, vi } from "vitest"
import type { FileNode } from "@/types/wiki"

const mockListDirectory = vi.fn()
const mockReadFile = vi.fn()

vi.mock("@/commands/fs", () => ({
listDirectory: (...args: unknown[]) => mockListDirectory(...args),
readFile: (...args: unknown[]) => mockReadFile(...args),
}))

async function loadBuildWikiGraph() {
const mod = await import("../wiki-graph")
return mod.buildWikiGraph
}

function mdFile(name: string): FileNode {
return { name, path: `/project/wiki/${name}`, is_dir: false }
}

describe("buildWikiGraph frontmatter extraction", () => {
it("does not read a title: line from the document body as the frontmatter title", async () => {
const buildWikiGraph = await loadBuildWikiGraph()
mockListDirectory.mockResolvedValue([mdFile("page.md")])
mockReadFile.mockResolvedValue(
"---\ntype: entity\n---\n# Real Heading\n\nSome text.\ntitle: not-frontmatter-at-all\n",
)

const graph = await buildWikiGraph("/project")

expect(graph.nodes).toHaveLength(1)
expect(graph.nodes[0].label).toBe("Real Heading")
})

it("does not read a type: line from the document body as the frontmatter type", async () => {
const buildWikiGraph = await loadBuildWikiGraph()
mockListDirectory.mockResolvedValue([mdFile("page.md")])
mockReadFile.mockResolvedValue(
"---\ntitle: Real Page\n---\n# Real Page\n\nSome text.\ntype: query\nMore text.\n",
)

const graph = await buildWikiGraph("/project")

// A misread type of "query" would match HIDDEN_TYPES and silently drop the page.
expect(graph.nodes).toHaveLength(1)
expect(graph.nodes[0].type).toBe("other")
})
})
11 changes: 9 additions & 2 deletions src/lib/wiki-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,14 @@ function flattenMdFiles(nodes: FileNode[]): FileNode[] {
return files
}

function extractFrontmatterBlock(content: string): string {
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/)
return fmMatch ? fmMatch[1] : ""
}

function extractTitle(content: string, fileName: string): string {
const frontmatterTitleMatch = content.match(/^---\n[\s\S]*?^title:\s*["']?(.+?)["']?\s*$/m)
const frontmatter = extractFrontmatterBlock(content)
const frontmatterTitleMatch = frontmatter.match(/^title:\s*["']?(.+?)["']?\s*$/m)
if (frontmatterTitleMatch) return frontmatterTitleMatch[1].trim()

const headingMatch = content.match(/^#\s+(.+)$/m)
Expand All @@ -137,7 +143,8 @@ function extractTitle(content: string, fileName: string): string {
}

function extractType(content: string): string {
const frontmatterTypeMatch = content.match(/^---\n[\s\S]*?^type:\s*["']?(.+?)["']?\s*$/m)
const frontmatter = extractFrontmatterBlock(content)
const frontmatterTypeMatch = frontmatter.match(/^type:\s*["']?(.+?)["']?\s*$/m)
if (frontmatterTypeMatch) return frontmatterTypeMatch[1].trim().toLowerCase()
return "other"
}
Expand Down