Files
commandkit/integrations/ckconfig/comparison_contract_test.go

106 lines
4.6 KiB
Go
Raw Normal View History

package ckconfig
import (
"context"
"errors"
"io"
"strings"
"testing"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
)
func TestComparisonCapabilitiesAreIndependentAndCaptured(t *testing.T) {
type input struct{ Value string }
var inputCalls, stateCalls, acquireCalls, compareCalls int
compare := func(context.Context, ComparisonRequest[input], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
compareCalls++
return commandkit.Acquired[[]configkit.Difference]{}, nil
}
operations := []Operation{Diff}
suite := Suite[input, struct{}]{
NewInput: func() input { inputCalls++; return input{} },
NewState: func() struct{} { stateCalls++; return struct{}{} },
Scopes: []Scope[input, struct{}]{
{Name: "app", Operations: operations, Compare: compare},
{Name: "promptkit", Operations: []Operation{Diff, Show, Validate}, Acquire: func(context.Context, Request, input, struct{}) (commandkit.Acquired[View], error) {
acquireCalls++
return commandkit.Acquired[View]{}, nil
}, Compare: compare},
{Name: "alpha"},
{Name: "plain", Operations: []Operation{Validate}, Acquire: func(context.Context, Request, input, struct{}) (commandkit.Acquired[View], error) {
acquireCalls++
return commandkit.Acquired[View]{}, nil
}},
},
}
captured := captureSuite(suite)
declaration := Command(suite)
// Capability metadata and callback references are copied during capture.
suite.Scopes[0].Operations[0] = Show
suite.Scopes[0].Compare = nil
app := newConfigTestApp(t, declaration)
var output strings.Builder
outcome := app.Execute(context.Background(), []string{"config", "scopes"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("scope discovery outcome = %#v", outcome)
}
if got, want := output.String(), "app: diff\npromptkit: validate, show, diff\nalpha: \nplain: validate\n"; got != want {
t.Fatalf("scope discovery = %q, want %q", got, want)
}
// A family-wide diff command is available, but unsupported scopes fail before
// suite factories or either provider runs.
outcome = app.Execute(context.Background(), []string{"config", "diff", "--scope", "plain"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage {
t.Fatalf("unsupported scope outcome = %#v", outcome)
}
if inputCalls != 0 || stateCalls != 0 || acquireCalls != 0 || compareCalls != 0 {
t.Fatalf("discovery or preflight invoked suite work: input=%d state=%d acquire=%d compare=%d", inputCalls, stateCalls, acquireCalls, compareCalls)
}
if captured.byName["app"].scope.Compare == nil {
t.Fatal("captured comparison callback was cleared by a source edit")
}
if _, err := captured.byName["app"].scope.Compare(context.Background(), ComparisonRequest[input]{}, struct{}{}); err != nil || compareCalls != 1 {
t.Fatalf("captured comparison callback result=%v calls=%d", err, compareCalls)
}
withoutComparison := newConfigTestApp(t, Command(Suite[input, struct{}]{Scopes: []Scope[input, struct{}]{{
Name: "plain", Operations: []Operation{Validate},
Acquire: func(context.Context, Request, input, struct{}) (commandkit.Acquired[View], error) {
return commandkit.Acquired[View]{}, nil
},
}}}))
outcome = withoutComparison.Execute(context.Background(), []string{"config", "diff"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage {
t.Fatalf("family without comparison exposed diff: %#v", outcome)
}
}
func TestComparisonCallbackWiringAndRetainedAdapterIssues(t *testing.T) {
newApp := func(scope Scope[struct{}, struct{}]) error {
_, err := commandkit.New(commandkit.Identity{Name: "example"}, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{scope}}))
return err
}
if err := newApp(Scope[struct{}, struct{}]{Name: "app", Operations: []Operation{Diff}}); err == nil {
t.Fatalf("missing comparison callback error = %v", err)
}
if err := newApp(Scope[struct{}, struct{}]{Name: "app", Operations: []Operation{Validate}}); err == nil {
t.Fatalf("missing ordinary acquisition callback error = %v", err)
}
scope := FromConfigKit[struct{}, struct{}, struct{}]("app", "Application configuration", nil)
scope.Operations = []Operation{Diff}
scope.Compare = func(context.Context, ComparisonRequest[struct{}], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
return commandkit.Acquired[[]configkit.Difference]{}, nil
}
retained := scope.issues[0]
if err := newApp(scope); err == nil || !errors.Is(err, retained) {
t.Fatalf("narrowed adapter lost its retained loader issue: %v", err)
}
}