ความคืบหน้างาน ไฟล์ต้นฉบับ

tracker/workflow_test.go

package main

import (
	"fmt"
	"testing"
)

func TestCanonicalWorkflowBranches(t *testing.T) {
	c, err := loadUseCases("..")
	if err != nil {
		t.Fatal(err)
	}
	for id, u := range c.UC {
		t.Run(id, func(t *testing.T) {
			d := draftDiagram(u)
			if err := validateDiagram(d); err != nil {
				t.Fatal(err)
			}
			seen := map[string]bool{"start": true}
			for changed := true; changed; {
				changed = false
				for _, e := range d.Edges {
					if seen[e.Source] && !seen[e.Target] {
						seen[e.Target] = true
						changed = true
					}
				}
			}
			for _, n := range d.Nodes {
				if !seen[n.ID] {
					t.Errorf("unreachable %s", n.ID)
				}
				if n.Type != "end" {
					found := false
					for _, e := range d.Edges {
						found = found || e.Source == n.ID
					}
					if !found {
						t.Errorf("dead end %s", n.ID)
					}
				}
			}
			canFinish := map[string]bool{}
			for _, n := range d.Nodes {
				if n.Type == "end" {
					canFinish[n.ID] = true
				}
			}
			for changed := true; changed; {
				changed = false
				for _, e := range d.Edges {
					if canFinish[e.Target] && !canFinish[e.Source] {
						canFinish[e.Source] = true
						changed = true
					}
				}
			}
			for _, n := range d.Nodes {
				if !canFinish[n.ID] {
					t.Errorf("no terminal outcome from %s", n.ID)
				}
			}
			for i := range u.Flow {
				if !seen[fmt.Sprintf("step-%d", i+1)] {
					t.Fatal("source step lost")
				}
			}
		})
	}
	check := func(id, from, to, label string) {
		t.Helper()
		for _, e := range draftDiagram(c.UC[id]).Edges {
			if e.Source == from && e.Target == to && e.Label == label {
				return
			}
		}
		t.Fatalf("missing %s: %s -> %s (%s)", id, from, to, label)
	}
	check("W-41", "decision-3", "step-4", "ไม่จำเป็น — สมัครด้วยตนเอง")
	check("W-77", "step-3", "step-2", "")
	check("W-14", "decision-1", "step-5", "ฉุกเฉินเข้าเกณฑ์")
	check("W-14", "step-4", "step-6", "")
	for _, e := range draftDiagram(c.UC["W-147"]).Edges {
		if e.Source == "decision-3" && e.Label == "ต่อสัญญา" && e.Target == "step-3" {
			t.Fatal("renewal entered checkout")
		}
	}
	u := c.UC["W-41"]
	old := sourceDigest(u)
	u.Workflow.Decisions[0].Title += " changed"
	if old == sourceDigest(u) {
		t.Fatal("workflow missing from approval fingerprint")
	}
}

func TestWorkflowChangeStalesApprovalWithoutReplacingSavedDiagram(t *testing.T) {
	c, err := loadUseCases("..")
	if err != nil {
		t.Fatal(err)
	}
	u := c.UC["W-41"]
	ds := &diagramStore{dir: t.TempDir()}
	status, err := ds.approve(u, 0, sourceDigest(u), "Test reviewer", "Temporary fixture only", true)
	if err != nil || !status.Approved {
		t.Fatal(err, status)
	}
	before, err := ds.load(u.ID, u)
	if err != nil {
		t.Fatal(err)
	}
	u.Workflow.Decisions[0].Branches[0].Label += " revised"
	if ds.approvalStatus(u).Approved {
		t.Fatal("changed condition kept approval")
	}
	after, err := ds.load(u.ID, u)
	if err != nil || digestJSON(before) != digestJSON(after) {
		t.Fatal("saved user graph overwritten", err)
	}
}