chore: sync from fn-registry agent
This commit is contained in:
+259
@@ -0,0 +1,259 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// testdataDir returns the path to the testdata directory.
|
||||
func testdataDir() string {
|
||||
return "testdata"
|
||||
}
|
||||
|
||||
// TestParseIssue_BasicFields verifies that ParseIssue correctly reads frontmatter.
|
||||
func TestParseIssue_BasicFields(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "issues", "0099-sample.md")
|
||||
iss, err := ParseIssue(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseIssue: %v", err)
|
||||
}
|
||||
|
||||
if iss.ID != "0099" {
|
||||
t.Errorf("ID: got %q, want %q", iss.ID, "0099")
|
||||
}
|
||||
if iss.Title != "datahub app (launcher central para todas las apps)" {
|
||||
t.Errorf("Title: got %q", iss.Title)
|
||||
}
|
||||
if iss.Status != "pendiente" {
|
||||
t.Errorf("Status: got %q, want %q", iss.Status, "pendiente")
|
||||
}
|
||||
if iss.Priority != "alta" {
|
||||
t.Errorf("Priority: got %q, want %q", iss.Priority, "alta")
|
||||
}
|
||||
if iss.Type != "feature" {
|
||||
t.Errorf("Type: got %q, want %q", iss.Type, "feature")
|
||||
}
|
||||
if len(iss.Domain) != 1 || iss.Domain[0] != "apps-infra" {
|
||||
t.Errorf("Domain: got %v, want [apps-infra]", iss.Domain)
|
||||
}
|
||||
if iss.Path != path {
|
||||
t.Errorf("Path: got %q, want %q", iss.Path, path)
|
||||
}
|
||||
if iss.Body == "" {
|
||||
t.Error("Body should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseIssue_AcceptancePct verifies checkbox counting in ## Acceptance.
|
||||
func TestParseIssue_AcceptancePct(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "issues", "0099-sample.md")
|
||||
iss, err := ParseIssue(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseIssue: %v", err)
|
||||
}
|
||||
// In 0099-sample.md: 2 checked, 2 unchecked = 50%
|
||||
if iss.AcceptancePct != 50 {
|
||||
t.Errorf("AcceptancePct: got %d, want 50", iss.AcceptancePct)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseIssue_DoDPct verifies checkbox counting in ## Definition of Done.
|
||||
func TestParseIssue_DoDPct(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "issues", "0099-sample.md")
|
||||
iss, err := ParseIssue(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseIssue: %v", err)
|
||||
}
|
||||
// In 0099-sample.md DoD: 1 checked, 3 unchecked = 25%
|
||||
if iss.DoDPct != 25 {
|
||||
t.Errorf("DoDPct: got %d, want 25", iss.DoDPct)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseIssue_EmptyBody handles a file with no body.
|
||||
func TestParseIssue_EmptyBody(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "issues", "0051-missing-dep.md")
|
||||
iss, err := ParseIssue(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseIssue: %v", err)
|
||||
}
|
||||
if iss.ID != "0051" {
|
||||
t.Errorf("ID: got %q, want %q", iss.ID, "0051")
|
||||
}
|
||||
// No checkboxes — both pcts should be 0
|
||||
if iss.AcceptancePct != 0 {
|
||||
t.Errorf("AcceptancePct: got %d, want 0", iss.AcceptancePct)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDepsResolved_AllCompletado verifies DepsResolved=true when all deps are completado.
|
||||
func TestDepsResolved_AllCompletado(t *testing.T) {
|
||||
issues := []Issue{
|
||||
{ID: "0001", Status: "completado"},
|
||||
{ID: "0050", Status: "pendiente", Depends: []string{"0001"}},
|
||||
}
|
||||
ComputeDepsResolved(issues)
|
||||
|
||||
if !issues[1].DepsResolved {
|
||||
t.Error("DepsResolved should be true when all deps are completado")
|
||||
}
|
||||
}
|
||||
|
||||
// TestDepsResolved_DepNotCompletado verifies DepsResolved=false when dep not completado.
|
||||
func TestDepsResolved_DepNotCompletado(t *testing.T) {
|
||||
issues := []Issue{
|
||||
{ID: "0001", Status: "pendiente"}, // not completado
|
||||
{ID: "0050", Status: "pendiente", Depends: []string{"0001"}},
|
||||
}
|
||||
ComputeDepsResolved(issues)
|
||||
|
||||
if issues[1].DepsResolved {
|
||||
t.Error("DepsResolved should be false when dep is not completado")
|
||||
}
|
||||
}
|
||||
|
||||
// TestDepsResolved_DepMissing verifies DepsResolved=false when dep is not in list.
|
||||
func TestDepsResolved_DepMissing(t *testing.T) {
|
||||
issues := []Issue{
|
||||
{ID: "0051", Status: "pendiente", Depends: []string{"9999"}},
|
||||
}
|
||||
ComputeDepsResolved(issues)
|
||||
|
||||
if issues[0].DepsResolved {
|
||||
t.Error("DepsResolved should be false when dep is missing from issue list")
|
||||
}
|
||||
}
|
||||
|
||||
// TestDepsResolved_NoDeps verifies DepsResolved=true when there are no deps.
|
||||
func TestDepsResolved_NoDeps(t *testing.T) {
|
||||
issues := []Issue{
|
||||
{ID: "0099", Status: "pendiente", Depends: []string{}},
|
||||
}
|
||||
ComputeDepsResolved(issues)
|
||||
|
||||
if !issues[0].DepsResolved {
|
||||
t.Error("DepsResolved should be true when there are no deps")
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadAllIssues_SkipsSkippable verifies that README.md and template.md are skipped.
|
||||
func TestLoadAllIssues_SkipsSkippable(t *testing.T) {
|
||||
issues, err := loadIssuesFromTestdata()
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
for _, iss := range issues {
|
||||
base := filepath.Base(iss.Path)
|
||||
lower := strings.ToLower(base)
|
||||
if strings.Contains(lower, "readme") || strings.Contains(lower, "template") {
|
||||
t.Errorf("should have skipped %s", base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadAllIssues_CountsCorrect verifies we load the expected number of fixtures.
|
||||
func TestLoadAllIssues_CountsCorrect(t *testing.T) {
|
||||
issues, err := loadIssuesFromTestdata()
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
// We have 4 fixture issues: 0099, 0001, 0050, 0051
|
||||
if len(issues) != 4 {
|
||||
t.Errorf("expected 4 issues, got %d", len(issues))
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseFlow_BasicFields verifies that ParseFlow reads frontmatter correctly.
|
||||
func TestParseFlow_BasicFields(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "flows", "0001-sample.md")
|
||||
fl, err := ParseFlow(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseFlow: %v", err)
|
||||
}
|
||||
|
||||
if fl.ID != "0001" {
|
||||
t.Errorf("ID: got %q, want %q", fl.ID, "0001")
|
||||
}
|
||||
if fl.Name != "hn-top-stories" {
|
||||
t.Errorf("Name: got %q, want %q", fl.Name, "hn-top-stories")
|
||||
}
|
||||
if fl.Status != "pending" {
|
||||
t.Errorf("Status: got %q, want %q", fl.Status, "pending")
|
||||
}
|
||||
if fl.Risk != "low" {
|
||||
t.Errorf("Risk: got %q, want %q", fl.Risk, "low")
|
||||
}
|
||||
if len(fl.Apps) != 2 {
|
||||
t.Errorf("Apps: got %d apps, want 2", len(fl.Apps))
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseFlow_AcceptancePct verifies checkbox counting in flow Acceptance.
|
||||
func TestParseFlow_AcceptancePct(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "flows", "0001-sample.md")
|
||||
fl, err := ParseFlow(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseFlow: %v", err)
|
||||
}
|
||||
// 1 checked, 2 unchecked = 33%
|
||||
if fl.AcceptancePct != 33 {
|
||||
t.Errorf("AcceptancePct: got %d, want 33", fl.AcceptancePct)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseFlow_UserFacingPct verifies user-facing checkbox counting.
|
||||
func TestParseFlow_UserFacingPct(t *testing.T) {
|
||||
path := filepath.Join(testdataDir(), "flows", "0001-sample.md")
|
||||
fl, err := ParseFlow(path)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseFlow: %v", err)
|
||||
}
|
||||
// 0 checked, 2 unchecked = 0%
|
||||
if fl.UserFacingPct != 0 {
|
||||
t.Errorf("UserFacingPct: got %d, want 0", fl.UserFacingPct)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNormalizeID pads short numeric IDs.
|
||||
func TestNormalizeID(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"99", "0099"},
|
||||
{"0099", "0099"},
|
||||
{"1", "0001"},
|
||||
{"0001", "0001"},
|
||||
{"0088a", "0088a"}, // non-numeric, no pad
|
||||
{"101", "0101"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := normalizeID(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("normalizeID(%q) = %q, want %q", tt.input, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loadIssuesFromTestdata is a helper that loads from testdata/ instead of dev/issues/.
|
||||
func loadIssuesFromTestdata() ([]Issue, error) {
|
||||
dir := filepath.Join(testdataDir(), "issues")
|
||||
entries, err := filepath.Glob(filepath.Join(dir, "*.md"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var issues []Issue
|
||||
for _, path := range entries {
|
||||
name := filepath.Base(path)
|
||||
if isSkippable(name) {
|
||||
continue
|
||||
}
|
||||
iss, err := ParseIssue(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
issues = append(issues, iss)
|
||||
}
|
||||
return issues, nil
|
||||
}
|
||||
Reference in New Issue
Block a user