Test comparison preparation through provider behavior

This commit is contained in:
2026-10-12 00:16:47 +00:00
parent 251d3adeee
commit c7fb5a27bb
2 changed files with 58 additions and 35 deletions

View File

@@ -93,8 +93,8 @@ func TestComparisonPreparesEqualSelectionsWithSeparateTypedInputs(t *testing.T)
"config", "diff", "--config", "same.yml", "--against-config", "same.yml", "--overlay=",
"--enabled=false", "--limit=0", "--tag", "alpha", "--tag", "beta",
}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind == commandkit.OutcomeUsage {
t.Fatalf("valid comparison inputs rejected: %#v", outcome)
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("valid comparison outcome = %#v", outcome)
}
if inputCalls != 2 || checkCalls != 2 || stateCalls != 1 {
t.Fatalf("preparation calls input=%d check=%d state=%d", inputCalls, checkCalls, stateCalls)
@@ -111,53 +111,65 @@ func TestComparisonPreparesEqualSelectionsWithSeparateTypedInputs(t *testing.T)
}
}
func TestComparisonPlanStoresPairSeparatelyFromOrdinaryInputs(t *testing.T) {
func TestComparisonProviderReceivesCheckedPairAndSharedState(t *testing.T) {
type input struct{ ID int }
inputCalls, checkCalls, stateCalls := 0, 0, 0
prepared := captureSuite(Suite[input, struct{}]{
type state struct{ Label string }
inputCalls, checkCalls, stateCalls, compareCalls, acquireCalls := 0, 0, 0, 0, 0
checked := make(map[Selection]input)
shared := &state{Label: "shared"}
var request ComparisonRequest[input]
app := newConfigTestApp(t, Command(Suite[input, *state]{
NewInput: func() input {
inputCalls++
return input{ID: inputCalls}
},
NewState: func() struct{} { stateCalls++; return struct{}{} },
NewState: func() *state {
stateCalls++
if checkCalls != 2 {
t.Errorf("shared state created after %d input checks", checkCalls)
}
return shared
},
CheckInput: func(scope string, selection Selection, value input) error {
checkCalls++
if scope != "app" || value.ID != checkCalls {
return errors.New("comparison inputs were not prepared in order")
if scope != "app" {
return errors.New("unexpected scope")
}
checked[selection] = value
return nil
},
Scopes: []Scope[input, struct{}]{{
Name: "app", Operations: []Operation{Diff},
Compare: func(context.Context, ComparisonRequest[input], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
Scopes: []Scope[input, *state]{{
Name: "app", Operations: []Operation{Show, Diff},
Acquire: func(context.Context, Request, input, *state) (commandkit.Acquired[View], error) {
acquireCalls++
return commandkit.Acquired[View]{}, errors.New("ordinary acquisition reached during comparison")
},
Compare: func(_ context.Context, pair ComparisonRequest[input], value *state) (commandkit.Acquired[[]configkit.Difference], error) {
compareCalls++
if checkCalls != 2 || stateCalls != 1 || value != shared {
t.Errorf("provider reached with checks=%d states=%d shared=%t", checkCalls, stateCalls, value == shared)
}
request = pair
return commandkit.Acquired[[]configkit.Difference]{}, nil
},
}},
})
}))
outcome := app.Execute(context.Background(), []string{
"config", "diff", "--config", "before.yml", "--against-overlay=",
}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("comparison outcome = %#v", outcome)
}
left := Selection{Config: commandkit.Supplied[string]{Value: "before.yml", Set: true}}
right := Selection{Overlay: commandkit.Supplied[string]{Set: true}}
commandInput := &configCommandInput[input, struct{}]{
selection: left,
against: right,
format: commandkit.Supplied[string]{Value: "yaml"},
right := Selection{Config: left.Config, Overlay: commandkit.Supplied[string]{Set: true}}
if request.Left.Selection != left || request.Right.Selection != right {
t.Fatalf("provider selections = %#v", request)
}
if err := prepareConfigOperation(prepared, Diff, commandInput); err != nil {
t.Fatal(err)
if len(checked) != 2 || request.Left.Input != checked[left] || request.Right.Input != checked[right] {
t.Fatalf("provider inputs = %#v, checked = %#v", request, checked)
}
plan := commandInput.plan
if plan == nil || plan.comparison == nil || len(plan.scopes) != 0 {
t.Fatalf("comparison was not stored separately: %#v", plan)
}
if plan.comparison.scope.scope.Name != "app" {
t.Fatalf("comparison scope = %q", plan.comparison.scope.scope.Name)
}
wantRight := Selection{Config: left.Config, Overlay: right.Overlay}
request := plan.comparison.request
if request.Left.Selection != left || request.Right.Selection != wantRight || request.Left.Input.ID != 1 || request.Right.Input.ID != 2 {
t.Fatalf("prepared request = %#v", request)
}
if inputCalls != 2 || checkCalls != 2 || stateCalls != 0 {
t.Fatalf("preparation calls input=%d check=%d state=%d", inputCalls, checkCalls, stateCalls)
if inputCalls != 2 || checkCalls != 2 || stateCalls != 1 || compareCalls != 1 || acquireCalls != 0 {
t.Fatalf("invocation calls input=%d check=%d state=%d compare=%d acquire=%d", inputCalls, checkCalls, stateCalls, compareCalls, acquireCalls)
}
}