c468b24d2b
Registry (issue 0130a):
- 5 fns infra: parse_issue_md, write_issue_md, scan_issues_dir,
scan_flows_dir, watch_dir_fsnotify
- 3 tipos: Issue, Flow, FsEvent
- Tests round-trip + scan reales + watcher fsnotify (all PASS)
- Capability group 'kanban' nuevo (docs/capabilities/kanban.md)
Apps:
- apps/kanban_cpp/ (sub-repo) — frontend ImGui: board drag-drop,
flows, filters, detail con CSV editors
- apps/kanban_cpp/backend/ — Go service port 8487: REST + SSE +
fsnotify watcher, parser bidireccional MD<->SQLite cache
Issues:
- dev/issues/0130-kanban-cpp-v2.md (epic)
- 0130a parser, 0130b backend, 0130c frontend
CMakeLists.txt: add_subdirectory apps/kanban_cpp (registrado por
init_cpp_app scaffolder).
End-to-end verde: backend devuelve 189 issues + 9 flows; PATCH a
/api/issues/{id} reescribe .md (solo frontmatter, body intacto);
frontend --self-test exit 0; tests Go infra 5/5 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
102 lines
2.9 KiB
Go
102 lines
2.9 KiB
Go
package infra
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func registryRoot() string {
|
|
_, thisFile, _, _ := runtime.Caller(0)
|
|
return filepath.Join(filepath.Dir(thisFile), "..", "..")
|
|
}
|
|
|
|
func TestParseIssueMd(t *testing.T) {
|
|
root := registryRoot()
|
|
|
|
t.Run("parsea 0130-kanban-cpp-v2 correctamente", func(t *testing.T) {
|
|
path := filepath.Join(root, "dev", "issues", "0130-kanban-cpp-v2.md")
|
|
iss, body, err := ParseIssueMd(path)
|
|
if err != nil {
|
|
t.Fatalf("ParseIssueMd error: %v", err)
|
|
}
|
|
if iss.ID != "0130" {
|
|
t.Errorf("ID: got %q, want %q", iss.ID, "0130")
|
|
}
|
|
if !strings.Contains(iss.Title, "Kanban C++ v2") {
|
|
t.Errorf("Title %q does not contain 'Kanban C++ v2'", iss.Title)
|
|
}
|
|
if iss.Status != "pendiente" {
|
|
t.Errorf("Status: got %q, want %q", iss.Status, "pendiente")
|
|
}
|
|
if len(iss.Domain) < 3 {
|
|
t.Errorf("Domain: got %d items, want >=3: %v", len(iss.Domain), iss.Domain)
|
|
}
|
|
if iss.FilePath != path {
|
|
t.Errorf("FilePath: got %q, want %q", iss.FilePath, path)
|
|
}
|
|
if iss.MtimeNs == 0 {
|
|
t.Error("MtimeNs should be non-zero")
|
|
}
|
|
if iss.Completed {
|
|
t.Error("Completed should be false for non-completed issue")
|
|
}
|
|
if len(body) == 0 {
|
|
t.Error("body should not be empty")
|
|
}
|
|
})
|
|
|
|
t.Run("completed flag se deduce del path", func(t *testing.T) {
|
|
fixturePath := filepath.Join(root, "functions", "infra", "testdata", "issue_fixture.fixture")
|
|
data, err := os.ReadFile(fixturePath)
|
|
if err != nil {
|
|
t.Fatalf("read fixture: %v", err)
|
|
}
|
|
completedDir := filepath.Join(t.TempDir(), "completed")
|
|
if err := os.MkdirAll(completedDir, 0755); err != nil {
|
|
t.Fatalf("mkdir: %v", err)
|
|
}
|
|
completedPath := filepath.Join(completedDir, "9999-fixture.md")
|
|
if err := os.WriteFile(completedPath, data, 0644); err != nil {
|
|
t.Fatalf("write: %v", err)
|
|
}
|
|
|
|
iss, _, err := ParseIssueMd(completedPath)
|
|
if err != nil {
|
|
t.Fatalf("ParseIssueMd error: %v", err)
|
|
}
|
|
if !iss.Completed {
|
|
t.Error("Completed should be true for path with /completed/")
|
|
}
|
|
})
|
|
|
|
t.Run("error en archivo inexistente", func(t *testing.T) {
|
|
_, _, err := ParseIssueMd("/nonexistent/path/issue.md")
|
|
if err == nil {
|
|
t.Error("expected error for nonexistent file")
|
|
}
|
|
})
|
|
|
|
t.Run("fixture preserva campos", func(t *testing.T) {
|
|
fixturePath := filepath.Join(root, "functions", "infra", "testdata", "issue_fixture.fixture")
|
|
iss, body, err := ParseIssueMd(fixturePath)
|
|
if err != nil {
|
|
t.Fatalf("ParseIssueMd error: %v", err)
|
|
}
|
|
if iss.ID != "9999" {
|
|
t.Errorf("ID: got %q, want %q", iss.ID, "9999")
|
|
}
|
|
if iss.Flow != "0001" {
|
|
t.Errorf("Flow: got %q, want %q", iss.Flow, "0001")
|
|
}
|
|
if len(iss.Depends) != 1 || iss.Depends[0] != "0001" {
|
|
t.Errorf("Depends: got %v, want [0001]", iss.Depends)
|
|
}
|
|
if !strings.Contains(string(body), "Este es el body") {
|
|
t.Errorf("body should contain fixture text, got: %s", string(body))
|
|
}
|
|
})
|
|
}
|