package main import ( "path/filepath" "testing" ) // todoRoot is a convenience helper for these tests. func todoRoot(root string) []todoScanRoot { return []todoScanRoot{{root: root}} } func TestCheckTODOs_GoFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "service.go", `package service // TODO: implement retry logic func Retry() {} `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .go file") } if vs[0].file != "service.go" { t.Fatalf("expected file 'service.go', got %q", vs[0].file) } if vs[0].line != 3 { t.Fatalf("expected line 3, got %d", vs[0].line) } } func TestCheckTODOs_TSFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "utils.ts", `export function foo() { // TODO: add error handling return null } `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .ts file") } } func TestCheckTODOs_TSXFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "component.tsx", `export function Comp() { // TODO(alex): fix this return
} `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .tsx file") } } func TestCheckTODOs_HTMLFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "page.html", `

Hello

`) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .html file") } } func TestCheckTODOs_TemplFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "block.templ", `package block templ Hero() { // TODO: make dynamic

Static

} `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .templ file") } } func TestCheckTODOs_SQLFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "queries.sql", `-- TODO: add index SELECT * FROM users; `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .sql file") } } func TestCheckTODOs_ScssFileFlagsTODO(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "styles.scss", `// TODO: update colours .btn { color: red; } `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation in .scss file") } } func TestCheckTODOs_CaseInsensitive(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "service.go", `package service // todo: lowercase todo should also match func f() {} `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation for lowercase 'todo'") } } func TestCheckTODOs_WithParens(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "service.go", `package service // TODO(alex): with author attribution func f() {} `) vs := checkTODOs(todoRoot(dir)) if len(vs) == 0 { t.Fatal("expected TODO violation for 'TODO(alex)' form") } } func TestCheckTODOs_CleanFile_NoViolation(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "clean.go", `package service // This is a regular comment about the implementation. func DoWork() error { return nil } `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected no violations for clean file, got %d", len(vs)) } } func TestCheckTODOs_TestGoFile_Skipped(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "service_test.go", `package service // TODO: add more test cases func TestFoo(t *testing.T) {} `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected _test.go files to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_TestTSFile_Skipped(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "utils.test.ts", `// TODO: more tests describe("foo", () => {}) `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected .test.ts files to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_TestTSXFile_Skipped(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "comp.test.tsx", `// TODO: add render tests `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected .test.tsx files to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_SpecTSFile_Skipped(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "utils.spec.ts", `// TODO: cover edge cases `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected .spec.ts files to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_NonCodeFile_Skipped(t *testing.T) { // .md files are not in todoFileExts — should be skipped dir := t.TempDir() writeFile(t, dir, "README.md", `# Project TODO: finish the docs `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected .md files to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_NodeModulesSkipped(t *testing.T) { dir := t.TempDir() nmDir := filepath.Join(dir, "node_modules", "some-lib") writeTestFile(t, filepath.Join(nmDir, "index.ts"), `// TODO: vendor file — should be skipped `, 0644) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected node_modules to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_VendorDirSkipped(t *testing.T) { dir := t.TempDir() vendorDir := filepath.Join(dir, "vendor", "somelib") writeTestFile(t, filepath.Join(vendorDir, "lib.go"), `package lib // TODO: vendor should be skipped `, 0644) vs := checkTODOs(todoRoot(dir)) if len(vs) != 0 { t.Fatalf("expected vendor/ to be skipped, got %d violations", len(vs)) } } func TestCheckTODOs_EmptyRootSkipped(t *testing.T) { // An empty root string should be skipped without panicking vs := checkTODOs([]todoScanRoot{{root: ""}}) if len(vs) != 0 { t.Fatalf("expected no violations for empty root, got %d", len(vs)) } } func TestCheckTODOs_DisplayPrefix(t *testing.T) { dir := t.TempDir() writeFile(t, dir, "service.go", `package service // TODO: remove this func f() {} `) vs := checkTODOs([]todoScanRoot{{root: dir, displayPrefix: "backend"}}) if len(vs) == 0 { t.Fatal("expected TODO violation") } if vs[0].file != "backend/service.go" { t.Fatalf("expected prefixed path 'backend/service.go', got %q", vs[0].file) } } func TestCheckTODOs_MultipleFiles_SortedByFileAndLine(t *testing.T) { dir := t.TempDir() // b.go has one TODO on line 2 writeFile(t, dir, "b.go", `package p // TODO: b func b() {} `) // a.go has two TODOs — line 3 and line 5 writeFile(t, dir, "a.go", `package p func a() { // TODO: first x := 1 // TODO: second _ = x } `) vs := checkTODOs(todoRoot(dir)) if len(vs) != 3 { t.Fatalf("expected 3 violations, got %d: %#v", len(vs), vs) } // Sorted: a.go first (alphabetically before b.go) if vs[0].file != "a.go" { t.Fatalf("first violation should be in a.go, got %q", vs[0].file) } if vs[0].line > vs[1].line { t.Fatalf("violations in same file should be sorted by line") } if vs[2].file != "b.go" { t.Fatalf("last violation should be in b.go, got %q", vs[2].file) } }