package validation import ( "testing" "github.com/steveyegge/beads/internal/types" ) func TestExists(t *testing.T) { tests := []struct { name string issue *types.Issue wantErr bool }{ { name: "nil issue returns error", issue: nil, wantErr: true, }, { name: "non-nil issue passes", issue: &types.Issue{ID: "bd-test"}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := Exists()("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("Exists() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestNotTemplate(t *testing.T) { tests := []struct { name string issue *types.Issue wantErr bool }{ { name: "nil issue passes (delegated check)", issue: nil, wantErr: false, }, { name: "non-template passes", issue: &types.Issue{ID: "bd-test", IsTemplate: false}, wantErr: false, }, { name: "template returns error", issue: &types.Issue{ID: "bd-test", IsTemplate: true}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := NotTemplate()("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("NotTemplate() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestNotPinned(t *testing.T) { tests := []struct { name string issue *types.Issue force bool wantErr bool }{ { name: "nil issue passes", issue: nil, force: false, wantErr: false, }, { name: "open issue passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusOpen}, force: false, wantErr: false, }, { name: "pinned issue without force fails", issue: &types.Issue{ID: "bd-test", Status: types.StatusPinned}, force: false, wantErr: true, }, { name: "pinned issue with force passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusPinned}, force: true, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := NotPinned(tt.force)("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("NotPinned() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestNotClosed(t *testing.T) { tests := []struct { name string issue *types.Issue wantErr bool }{ { name: "nil issue passes", issue: nil, wantErr: false, }, { name: "open issue passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusOpen}, wantErr: false, }, { name: "closed issue fails", issue: &types.Issue{ID: "bd-test", Status: types.StatusClosed}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := NotClosed()("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("NotClosed() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestChain(t *testing.T) { tests := []struct { name string issue *types.Issue validators []IssueValidator wantErr bool }{ { name: "empty chain passes", issue: &types.Issue{ID: "bd-test"}, validators: []IssueValidator{}, wantErr: false, }, { name: "all validators pass", issue: &types.Issue{ID: "bd-test", Status: types.StatusOpen}, validators: []IssueValidator{ Exists(), NotTemplate(), NotPinned(false), }, wantErr: false, }, { name: "first validator fails stops chain", issue: nil, validators: []IssueValidator{ Exists(), NotTemplate(), }, wantErr: true, }, { name: "middle validator fails", issue: &types.Issue{ID: "bd-test", IsTemplate: true}, validators: []IssueValidator{ Exists(), NotTemplate(), NotPinned(false), }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { chain := Chain(tt.validators...) err := chain("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("Chain() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestHasStatus(t *testing.T) { tests := []struct { name string issue *types.Issue allowed []types.Status wantErr bool }{ { name: "nil issue passes", issue: nil, allowed: []types.Status{types.StatusOpen}, wantErr: false, }, { name: "matching status passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusOpen}, allowed: []types.Status{types.StatusOpen}, wantErr: false, }, { name: "one of multiple allowed passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusClosed}, allowed: []types.Status{types.StatusOpen, types.StatusClosed}, wantErr: false, }, { name: "non-matching status fails", issue: &types.Issue{ID: "bd-test", Status: types.StatusPinned}, allowed: []types.Status{types.StatusOpen, types.StatusClosed}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := HasStatus(tt.allowed...)("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("HasStatus() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestHasType(t *testing.T) { tests := []struct { name string issue *types.Issue allowed []types.IssueType wantErr bool }{ { name: "nil issue passes", issue: nil, allowed: []types.IssueType{types.TypeTask}, wantErr: false, }, { name: "matching type passes", issue: &types.Issue{ID: "bd-test", IssueType: types.TypeTask}, allowed: []types.IssueType{types.TypeTask}, wantErr: false, }, { name: "one of multiple allowed passes", issue: &types.Issue{ID: "bd-test", IssueType: types.TypeBug}, allowed: []types.IssueType{types.TypeTask, types.TypeBug}, wantErr: false, }, { name: "non-matching type fails", issue: &types.Issue{ID: "bd-test", IssueType: types.TypeEpic}, allowed: []types.IssueType{types.TypeTask, types.TypeBug}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := HasType(tt.allowed...)("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("HasType() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestForUpdate(t *testing.T) { tests := []struct { name string issue *types.Issue wantErr bool }{ { name: "nil issue fails", issue: nil, wantErr: true, }, { name: "template fails", issue: &types.Issue{ID: "bd-test", IsTemplate: true}, wantErr: true, }, { name: "regular issue passes", issue: &types.Issue{ID: "bd-test", IsTemplate: false}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ForUpdate()("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("ForUpdate() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestForClose(t *testing.T) { tests := []struct { name string issue *types.Issue force bool wantErr bool }{ { name: "nil issue fails", issue: nil, force: false, wantErr: true, }, { name: "template fails", issue: &types.Issue{ID: "bd-test", IsTemplate: true}, force: false, wantErr: true, }, { name: "pinned without force fails", issue: &types.Issue{ID: "bd-test", Status: types.StatusPinned}, force: false, wantErr: true, }, { name: "pinned with force passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusPinned}, force: true, wantErr: false, }, { name: "regular open issue passes", issue: &types.Issue{ID: "bd-test", Status: types.StatusOpen}, force: false, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ForClose(tt.force)("bd-test", tt.issue) if (err != nil) != tt.wantErr { t.Errorf("ForClose() error = %v, wantErr %v", err, tt.wantErr) } }) } }