package core import ( "errors" "testing" ) func TestParseProposalState(t *testing.T) { tests := []struct { in string ok bool }{ {"open", true}, {"merged", true}, {"rejected", true}, // The states that were deliberately collapsed away. Accepting either // would resurrect a review conversation that has no second party. {"approved", false}, {"changes-requested", false}, {"closed", false}, {"draft", false}, {"", false}, {"Open", false}, {"OPEN", false}, {"open ", false}, {" open", false}, {"открыт", false}, } for _, tc := range tests { st, err := ParseProposalState(tc.in) if (err == nil) != tc.ok { t.Errorf("ParseProposalState(%q) = %q, %v, want ok=%v", tc.in, st, err, tc.ok) } if err != nil && !errors.Is(err, ErrInvalidState) { t.Errorf("ParseProposalState(%q) error %v is not ErrInvalidState", tc.in, err) } } } func TestProposalStatesIsExactlyThree(t *testing.T) { got := ProposalStates() if len(got) != 3 { t.Fatalf("ProposalStates() = %v, want exactly open/merged/rejected", got) } want := []ProposalState{StateOpen, StateMerged, StateRejected} for i := range want { if got[i] != want[i] { t.Fatalf("ProposalStates() = %v, want %v", got, want) } } } func TestProposalTransitions(t *testing.T) { all := []ProposalState{StateOpen, StateMerged, StateRejected} // The whole table, explicitly: only the two edges out of `open` exist. want := map[ProposalState]map[ProposalState]bool{ StateOpen: {StateOpen: false, StateMerged: true, StateRejected: true}, StateMerged: {StateOpen: false, StateMerged: false, StateRejected: false}, StateRejected: {StateOpen: false, StateMerged: false, StateRejected: false}, } for _, from := range all { for _, to := range all { t.Run(string(from)+"->"+string(to), func(t *testing.T) { expect := want[from][to] if got := ValidTransition(from, to); got != expect { t.Fatalf("ValidTransition(%s, %s) = %v, want %v", from, to, got, expect) } err := from.CanTransitionTo(to) if (err == nil) != expect { t.Fatalf("%s.CanTransitionTo(%s) = %v, want ok=%v", from, to, err, expect) } if err != nil && !errors.Is(err, ErrInvalidTransition) { t.Fatalf("error %v is not ErrInvalidTransition", err) } }) } } } func TestCanTransitionToRejectsUnknownStates(t *testing.T) { // An unparseable state is a different failure class from an illegal edge: // it means something wrote garbage into the proposal row. if err := ProposalState("approved").CanTransitionTo(StateMerged); !errors.Is(err, ErrInvalidState) { t.Fatalf("from=approved: %v, want ErrInvalidState", err) } if err := StateOpen.CanTransitionTo("approved"); !errors.Is(err, ErrInvalidState) { t.Fatalf("to=approved: %v, want ErrInvalidState", err) } if err := ProposalState("").CanTransitionTo(StateMerged); !errors.Is(err, ErrInvalidState) { t.Fatalf("from=empty: %v, want ErrInvalidState", err) } } func TestProposalStateTerminal(t *testing.T) { tests := []struct { in ProposalState want bool }{ {StateOpen, false}, {StateMerged, true}, {StateRejected, true}, {"", false}, {"approved", false}, } for _, tc := range tests { if got := tc.in.Terminal(); got != tc.want { t.Errorf("%q.Terminal() = %v, want %v", tc.in, got, tc.want) } } } func TestParseApproval(t *testing.T) { tests := []struct { in string ok bool }{ {"human", true}, {"policy", true}, {"", false}, {"auto", false}, {"Human", false}, {"human ", false}, {"approved", false}, } for _, tc := range tests { a, err := ParseApproval(tc.in) if (err == nil) != tc.ok { t.Errorf("ParseApproval(%q) = %q, %v, want ok=%v", tc.in, a, err, tc.ok) } if err != nil && !errors.Is(err, ErrInvalidApproval) { t.Errorf("ParseApproval(%q) error %v is not ErrInvalidApproval", tc.in, err) } } } // A policy-merged document must be distinguishable from a human-approved one: // collapsing the two would quietly launder unreviewed agent output as blessed. func TestApprovalKindsAreDistinct(t *testing.T) { if ApprovalHuman == ApprovalPolicy { t.Fatal("human and policy approval must not be the same value") } }