parent_cycle_test.go

 1package main
 2
 3import (
 4	"strings"
 5	"testing"
 6	"time"
 7)
 8
 9func TestCreatesParentCycle(t *testing.T) {
10	issues := []BugIssue{
11		{FullID: "grandparent"},
12		{FullID: "parent", ParentID: "grandparent"},
13		{FullID: "child", ParentID: "parent"},
14		{FullID: "unrelated"},
15	}
16
17	tests := []struct {
18		name   string
19		bug    string
20		parent string
21		want   bool
22	}{
23		{"direct cycle", "parent", "child", true},
24		{"indirect cycle", "grandparent", "child", true},
25		{"self link", "child", "child", true},
26		{"unrelated link", "unrelated", "child", false},
27		{"sibling link", "child", "unrelated", false},
28	}
29
30	for _, tt := range tests {
31		t.Run(tt.name, func(t *testing.T) {
32			if got := createsParentCycle(issues, tt.bug, tt.parent); got != tt.want {
33				t.Errorf("createsParentCycle(%q, %q) = %v, want %v", tt.bug, tt.parent, got, tt.want)
34			}
35		})
36	}
37}
38
39// TestBuildIssueTree_CycleNodesShownOnce ensures issues in a dependency
40// cycle (possible only via manual metadata tampering, since the CLI
41// refuses to create cycles) are still displayed exactly once instead
42// of silently vanishing from `bug ls`.
43func TestBuildIssueTree_CycleNodesShownOnce(t *testing.T) {
44	now := time.Now()
45	issues := []BugIssue{
46		{FullID: "aaa", ShortID: "aaa", Title: "A", CreatedAt: now.Add(-3 * time.Hour), ParentID: "bbb"},
47		{FullID: "bbb", ShortID: "bbb", Title: "B", CreatedAt: now.Add(-2 * time.Hour), ParentID: "aaa"},
48		{FullID: "root", ShortID: "root", Title: "Root", CreatedAt: now.Add(-1 * time.Hour)},
49	}
50
51	roots := buildIssueTree(issues)
52	out := captureTreeOutput(t, roots)
53
54	for _, id := range []string{"aaa", "bbb", "root"} {
55		if got := strings.Count(out, id); got != 1 {
56			t.Errorf("ID %q appears %d times, want exactly once\noutput:\n%s", id, got, out)
57		}
58	}
59}