package main import ( "os" "path/filepath" "testing" ) func writeFixture(t *testing.T, dir, name, content string) { t.Helper() if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { t.Fatalf("write %s: %v", name, err) } } func TestLoadIssueCards_MapsStatusesAndSkipsNonIssues(t *testing.T) { dir := t.TempDir() writeFixture(t, dir, "README.md", "skip me") writeFixture(t, dir, "AGENT_GUIDE.md", "skip me too") writeFixture(t, dir, "0001-foo.md", "---\n"+ "id: \"0001\"\n"+ "title: \"Foo\"\n"+ "status: pendiente\n"+ "priority: alta\n"+ "type: feature\n"+ "tags: [x, y]\n"+ "flow: \"0008\"\n"+ "created: 2026-05-18\n"+ "updated: 2026-05-18\n"+ "---\n# Foo\n\nDescription body line 1.\nLine 2.\n") writeFixture(t, dir, "0002-bar.md", "---\n"+ "id: \"0002\"\n"+ "title: \"Bar\"\n"+ "status: en-curso\n"+ "---\nBody\n") writeFixture(t, dir, "0003-baz.md", "---\n"+ "id: \"0003\"\n"+ "title: \"Baz\"\n"+ "status: done\n"+ "---\nBody\n") cards, err := loadIssueCards(dir) if err != nil { t.Fatalf("load: %v", err) } if len(cards) != 3 { t.Fatalf("expected 3 cards, got %d: %#v", len(cards), cards) } byID := map[string]IssueCard{} for _, c := range cards { byID[c.ID] = c } if c := byID["0001"]; c.ColumnID != "Backlog" || c.Priority != "alta" || c.FlowID != "0008" || c.Type != "feature" { t.Fatalf("0001 mismapped: %#v", c) } if c := byID["0002"]; c.ColumnID != "Doing" { t.Fatalf("0002 expected Doing, got %s", c.ColumnID) } if c := byID["0003"]; c.ColumnID != "Done" { t.Fatalf("0003 expected Done, got %s", c.ColumnID) } // Description must contain body content but NOT the title heading. if c := byID["0001"]; c.Description == "" { t.Fatalf("0001 missing description") } } func TestLoadIssueCards_MalformedYAMLDoesNotCrash(t *testing.T) { dir := t.TempDir() writeFixture(t, dir, "0010-bad.md", "---\nid: \"0010\"\ntitle: \"Bad\"\nstatus: pendiente\n : malformed\n---\nbody\n") cards, err := loadIssueCards(dir) if err != nil { t.Fatalf("expected no top-level error, got: %v", err) } if len(cards) != 1 { t.Fatalf("expected 1 card (with parse error), got %d", len(cards)) } if cards[0].ParseError == "" { t.Fatalf("expected ParseError to be set on malformed card") } if cards[0].ID != "0010" { t.Fatalf("expected id 0010 derived from filename, got %q", cards[0].ID) } } func TestLoadIssueCards_MissingStatusDefaultsPendiente(t *testing.T) { dir := t.TempDir() writeFixture(t, dir, "0011-nostatus.md", "---\nid: \"0011\"\ntitle: \"NoStatus\"\n---\nbody\n") cards, err := loadIssueCards(dir) if err != nil { t.Fatalf("load: %v", err) } if len(cards) != 1 { t.Fatalf("expected 1 card, got %d", len(cards)) } if cards[0].Status != "pendiente" { t.Fatalf("expected default status pendiente, got %q", cards[0].Status) } if cards[0].ColumnID != "Backlog" { t.Fatalf("expected column Backlog, got %q", cards[0].ColumnID) } } func TestIssuesCacheTTL(t *testing.T) { // Sanity: a fresh cache misses. cache := &cardsCache{ttl: 30 * 1_000_000_000} // 30s in ns if _, ok := cache.get(); ok { t.Fatalf("expected miss on empty cache") } cache.set([]IssueCard{{ID: "0001"}}) if c, ok := cache.get(); !ok || len(c) != 1 { t.Fatalf("expected cache hit") } cache.invalidate() if _, ok := cache.get(); ok { t.Fatalf("expected miss after invalidate") } }