205 lines
5.7 KiB
TypeScript
205 lines
5.7 KiB
TypeScript
import * as fs from "fs"
|
|
import { describe, it, expect } from "vitest"
|
|
import { parseModules, parseBugzillaTracker } from "../../src/tracker.js"
|
|
import { parseTodoFile } from "../../src/file.js"
|
|
import { serializeTodoFile, serializeModules, serializeBugzillaTracker } from "../../src/serializer.js"
|
|
|
|
describe("parseModules", () => {
|
|
it("parses modules list", () => {
|
|
const text = `Modules:
|
|
- Name: General
|
|
Path: .
|
|
- Name: Frontend
|
|
Path: src/frontend`
|
|
|
|
const modules = parseModules(text)
|
|
expect(modules).toHaveLength(2)
|
|
expect(modules[0]).toEqual({ name: "General", path: "." })
|
|
expect(modules[1]).toEqual({ name: "Frontend", path: "src/frontend" })
|
|
})
|
|
})
|
|
|
|
describe("parseBugzillaTracker", () => {
|
|
it("parses bugzilla tracker with mappings", () => {
|
|
const text = `URL: https://bugzilla.example.com
|
|
Mappings:
|
|
- Module: General
|
|
Product: MyProject
|
|
Component: General
|
|
- Module: Frontend
|
|
Product: MyProject
|
|
Component: FrontendUI`
|
|
|
|
const tracker = parseBugzillaTracker(text)
|
|
expect(tracker.url).toBe("https://bugzilla.example.com")
|
|
expect(tracker.mappings).toHaveLength(2)
|
|
expect(tracker.mappings[0]).toEqual({
|
|
module: "General",
|
|
product: "MyProject",
|
|
component: "General",
|
|
})
|
|
expect(tracker.mappings[1]).toEqual({
|
|
module: "Frontend",
|
|
product: "MyProject",
|
|
component: "FrontendUI",
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("parseTodoFile with modules and bugzilla", () => {
|
|
it("parses TODO with modules and bugzilla parts", async () => {
|
|
const todo = await parseTodoFile("tests/_mocks/todo-with-tracker.txt")
|
|
|
|
expect(todo.modules).toBeDefined()
|
|
expect(todo.modules).toHaveLength(3)
|
|
expect(todo.modules![0].name).toBe("General")
|
|
|
|
expect(todo.bugzilla).toBeDefined()
|
|
expect(todo.bugzilla!.url).toBe("https://bugzilla.example.com")
|
|
expect(todo.bugzilla!.mappings).toHaveLength(3)
|
|
|
|
expect(todo.issues).toHaveLength(2)
|
|
expect(todo.issues[0].module).toBe("Frontend")
|
|
expect(todo.issues[1].module).toBe("Backend")
|
|
})
|
|
|
|
it("rejects issue with invalid module", async () => {
|
|
const raw = `--ISSUE
|
|
Content-Type: application/modules
|
|
Modules:
|
|
- Name: General
|
|
Path: .
|
|
|
|
--ISSUE
|
|
Content-Type: application/issue
|
|
ID: 1
|
|
Type: feature
|
|
Title: Bad module
|
|
Status: open
|
|
Priority: low
|
|
Created: 2026-01-01
|
|
Module: NonExistent
|
|
Relationships:
|
|
Description: Test`
|
|
|
|
const tmpPath = "tests/_mocks/_bad-module.txt"
|
|
fs.writeFileSync(tmpPath, raw)
|
|
try {
|
|
await expect(parseTodoFile(tmpPath)).rejects.toThrow("NonExistent")
|
|
} finally {
|
|
fs.unlinkSync(tmpPath)
|
|
}
|
|
})
|
|
|
|
it("rejects bugzilla mapping referencing undefined module", async () => {
|
|
const raw = `--ISSUE
|
|
Content-Type: application/modules
|
|
Modules:
|
|
- Name: General
|
|
Path: .
|
|
|
|
--ISSUE
|
|
Content-Type: application/bugzilla
|
|
URL: https://bz.example.com
|
|
Mappings:
|
|
- Module: DoesNotExist
|
|
Product: P
|
|
Component: C
|
|
|
|
--ISSUE
|
|
Content-Type: application/issue
|
|
ID: 1
|
|
Type: feature
|
|
Title: Test
|
|
Status: open
|
|
Priority: low
|
|
Created: 2026-01-01
|
|
Relationships:
|
|
Description: Test`
|
|
|
|
const tmpPath = "tests/_mocks/_bad-bz-mapping.txt"
|
|
fs.writeFileSync(tmpPath, raw)
|
|
try {
|
|
await expect(parseTodoFile(tmpPath)).rejects.toThrow("DoesNotExist")
|
|
} finally {
|
|
fs.unlinkSync(tmpPath)
|
|
}
|
|
})
|
|
|
|
it("rejects dangling relationship targets", async () => {
|
|
const raw = `--ISSUE
|
|
Content-Type: application/issue
|
|
ID: 1
|
|
Type: feature
|
|
Title: Test
|
|
Status: open
|
|
Priority: low
|
|
Created: 2026-01-01
|
|
Relationships: dependsOn:99
|
|
Description: Test`
|
|
|
|
const tmpPath = "tests/_mocks/_bad-rel.txt"
|
|
fs.writeFileSync(tmpPath, raw)
|
|
try {
|
|
await expect(parseTodoFile(tmpPath)).rejects.toThrow("#99")
|
|
} finally {
|
|
fs.unlinkSync(tmpPath)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("serialize modules and bugzilla", () => {
|
|
it("serializes modules part", () => {
|
|
const result = serializeModules([
|
|
{ name: "General", path: "." },
|
|
{ name: "Frontend", path: "src/frontend" },
|
|
])
|
|
expect(result).toContain("Modules:")
|
|
expect(result).toContain("- Name: General")
|
|
expect(result).toContain("Path: .")
|
|
expect(result).toContain("- Name: Frontend")
|
|
})
|
|
|
|
it("serializes bugzilla tracker part", () => {
|
|
const result = serializeBugzillaTracker({
|
|
url: "https://bugzilla.example.com",
|
|
mappings: [
|
|
{ module: "General", product: "MyProject", component: "General" },
|
|
],
|
|
})
|
|
expect(result).toContain("URL: https://bugzilla.example.com")
|
|
expect(result).toContain("- Module: General")
|
|
expect(result).toContain("Product: MyProject")
|
|
expect(result).toContain("Component: General")
|
|
})
|
|
})
|
|
|
|
describe("round-trip with modules and bugzilla", () => {
|
|
it("parse → serialize → parse preserves modules, bugzilla, and issue modules", async () => {
|
|
const todo1 = await parseTodoFile("tests/_mocks/todo-with-tracker.txt")
|
|
const serialized = serializeTodoFile(todo1)
|
|
const tmpPath = "tests/_mocks/_roundtrip-tracker.txt"
|
|
fs.writeFileSync(tmpPath, serialized)
|
|
|
|
try {
|
|
const todo2 = await parseTodoFile(tmpPath)
|
|
|
|
expect(todo2.modules).toHaveLength(todo1.modules!.length)
|
|
for (let i = 0; i < todo1.modules!.length; i++) {
|
|
expect(todo2.modules![i]).toEqual(todo1.modules![i])
|
|
}
|
|
|
|
expect(todo2.bugzilla!.url).toBe(todo1.bugzilla!.url)
|
|
expect(todo2.bugzilla!.mappings).toHaveLength(todo1.bugzilla!.mappings.length)
|
|
for (let i = 0; i < todo1.bugzilla!.mappings.length; i++) {
|
|
expect(todo2.bugzilla!.mappings[i]).toEqual(todo1.bugzilla!.mappings[i])
|
|
}
|
|
|
|
for (let i = 0; i < todo1.issues.length; i++) {
|
|
expect(todo2.issues[i].module).toBe(todo1.issues[i].module)
|
|
}
|
|
} finally {
|
|
fs.unlinkSync(tmpPath)
|
|
}
|
|
})
|
|
})
|