184 lines
7.7 KiB
Go
184 lines
7.7 KiB
Go
package ckconfig
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
|
|
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
|
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
|
)
|
|
|
|
func TestComparisonSelectionInheritsFieldsIndependently(t *testing.T) {
|
|
leftConfig := commandkit.Supplied[string]{Value: "before.yml", Set: true}
|
|
leftOverlay := commandkit.Supplied[string]{Value: "base", Set: true}
|
|
tests := []struct {
|
|
name string
|
|
left Selection
|
|
right Selection
|
|
want Selection
|
|
}{
|
|
{
|
|
name: "omitted fields stay omitted",
|
|
},
|
|
{
|
|
name: "both fields inherit supplied state",
|
|
left: Selection{Config: leftConfig, Overlay: leftOverlay},
|
|
want: Selection{Config: leftConfig, Overlay: leftOverlay},
|
|
},
|
|
{
|
|
name: "explicit empty config overrides while overlay inherits",
|
|
left: Selection{Config: leftConfig, Overlay: leftOverlay},
|
|
right: Selection{Config: commandkit.Supplied[string]{Set: true}},
|
|
want: Selection{Config: commandkit.Supplied[string]{Set: true}, Overlay: leftOverlay},
|
|
},
|
|
{
|
|
name: "explicit overlay overrides while config inherits",
|
|
left: Selection{Config: leftConfig, Overlay: leftOverlay},
|
|
right: Selection{Overlay: commandkit.Supplied[string]{Value: "candidate", Set: true}},
|
|
want: Selection{Config: leftConfig, Overlay: commandkit.Supplied[string]{Value: "candidate", Set: true}},
|
|
},
|
|
{
|
|
name: "explicit empty overlay remains supplied",
|
|
left: Selection{Config: leftConfig, Overlay: leftOverlay},
|
|
right: Selection{Overlay: commandkit.Supplied[string]{Set: true}},
|
|
want: Selection{Config: leftConfig, Overlay: commandkit.Supplied[string]{Set: true}},
|
|
},
|
|
{
|
|
name: "both explicit right fields replace left",
|
|
left: Selection{Config: leftConfig, Overlay: leftOverlay},
|
|
right: Selection{Config: commandkit.Supplied[string]{Value: "after.yml", Set: true}, Overlay: commandkit.Supplied[string]{Value: "new", Set: true}},
|
|
want: Selection{Config: commandkit.Supplied[string]{Value: "after.yml", Set: true}, Overlay: commandkit.Supplied[string]{Value: "new", Set: true}},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := inheritComparisonSelection(test.left, test.right); got != test.want {
|
|
t.Fatalf("inherited selection = %#v, want %#v", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComparisonHelpLimitsFlagsAndDefaultsFormatToYAML(t *testing.T) {
|
|
scope := Scope[struct{}, struct{}]{
|
|
Name: "app", Operations: []Operation{Diff, Show, Explain, Validate, Sources},
|
|
Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
|
return commandkit.Acquired[View]{}, nil
|
|
},
|
|
Compare: func(context.Context, ComparisonRequest[struct{}], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
|
|
return commandkit.Acquired[[]configkit.Difference]{}, nil
|
|
},
|
|
}
|
|
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{scope}}))
|
|
help := func(args ...string) string {
|
|
t.Helper()
|
|
var output strings.Builder
|
|
app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
return output.String()
|
|
}
|
|
|
|
diffHelp := help("config", "diff", "--help")
|
|
if !strings.Contains(diffHelp, "--against-config") || !strings.Contains(diffHelp, "--against-overlay") {
|
|
t.Fatalf("diff help omits right-side selection flags:\n%s", diffHelp)
|
|
}
|
|
if !strings.Contains(diffHelp, "default: yaml") {
|
|
t.Fatalf("diff help does not show the YAML default:\n%s", diffHelp)
|
|
}
|
|
for _, operation := range []string{"validate", "show", "sources", "explain"} {
|
|
operationHelp := help("config", operation, "--help")
|
|
if strings.Contains(operationHelp, "--against-config") || strings.Contains(operationHelp, "--against-overlay") {
|
|
t.Errorf("%s exposes comparison-only flags:\n%s", operation, operationHelp)
|
|
}
|
|
if operation == "show" && !strings.Contains(operationHelp, "default: yaml") {
|
|
t.Errorf("show format default changed:\n%s", operationHelp)
|
|
}
|
|
if operation != "show" && !strings.Contains(operationHelp, "default: text") {
|
|
t.Errorf("%s format default changed:\n%s", operation, operationHelp)
|
|
}
|
|
if operation != "explain" && strings.Contains(operationHelp, "--path") {
|
|
t.Errorf("%s exposes the explain-only path flag:\n%s", operation, operationHelp)
|
|
}
|
|
}
|
|
explainHelp := help("config", "explain", "--help")
|
|
if !strings.Contains(explainHelp, "--path") {
|
|
t.Fatalf("explain flags changed:\n%s", explainHelp)
|
|
}
|
|
}
|
|
|
|
func TestComparisonPreparationAcceptsOnlyYAMLWithoutCreatingInputs(t *testing.T) {
|
|
inputCalls, stateCalls := 0, 0
|
|
prepared := captureSuite(Suite[struct{}, struct{}]{
|
|
NewInput: func() struct{} { inputCalls++; return struct{}{} },
|
|
NewState: func() struct{} { stateCalls++; return struct{}{} },
|
|
Scopes: []Scope[struct{}, struct{}]{{
|
|
Name: "app", Operations: []Operation{Diff},
|
|
Compare: func(context.Context, ComparisonRequest[struct{}], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
|
|
return commandkit.Acquired[[]configkit.Difference]{}, nil
|
|
},
|
|
}},
|
|
})
|
|
|
|
input := &configCommandInput[struct{}, struct{}]{
|
|
selection: Selection{Config: commandkit.Supplied[string]{Value: "before.yml", Set: true}},
|
|
against: Selection{Overlay: commandkit.Supplied[string]{Value: "candidate", Set: true}},
|
|
format: commandkit.Supplied[string]{Value: "yaml", Set: true},
|
|
}
|
|
if err := prepareConfigOperation(prepared, Diff, input); err != nil {
|
|
t.Fatalf("explicit YAML format rejected: %v", err)
|
|
}
|
|
if input.plan == nil || input.plan.format != "yaml" {
|
|
t.Fatalf("comparison plan = %#v", input.plan)
|
|
}
|
|
if input.plan.scopeName != "app" {
|
|
t.Fatalf("comparison default scope = %q, want app", input.plan.scopeName)
|
|
}
|
|
if want := (Selection{
|
|
Config: commandkit.Supplied[string]{Value: "before.yml", Set: true},
|
|
Overlay: commandkit.Supplied[string]{Value: "candidate", Set: true},
|
|
}); input.against != want {
|
|
t.Fatalf("resolved right selection = %#v, want %#v", input.against, want)
|
|
}
|
|
|
|
for _, format := range []string{"text", "json"} {
|
|
input := &configCommandInput[struct{}, struct{}]{format: commandkit.Supplied[string]{Value: format}}
|
|
if err := prepareConfigOperation(prepared, Diff, input); err == nil {
|
|
t.Errorf("comparison format %q was accepted", format)
|
|
}
|
|
}
|
|
if inputCalls != 0 || stateCalls != 0 {
|
|
t.Fatalf("comparison preparation created input/state: %d/%d", inputCalls, stateCalls)
|
|
}
|
|
}
|
|
|
|
func TestComparisonAvailabilityPreflightPrecedesFactories(t *testing.T) {
|
|
inputCalls, stateCalls, acquireCalls, compareCalls := 0, 0, 0, 0
|
|
prepared := captureSuite(Suite[struct{}, struct{}]{
|
|
NewInput: func() struct{} { inputCalls++; return struct{}{} },
|
|
NewState: func() struct{} { stateCalls++; return struct{}{} },
|
|
Scopes: []Scope[struct{}, struct{}]{
|
|
{Name: "app", Operations: []Operation{Diff}, Compare: func(context.Context, ComparisonRequest[struct{}], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
|
|
compareCalls++
|
|
return commandkit.Acquired[[]configkit.Difference]{}, nil
|
|
}},
|
|
{Name: "plain", Operations: []Operation{Validate}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
|
acquireCalls++
|
|
return commandkit.Acquired[View]{}, nil
|
|
}},
|
|
},
|
|
})
|
|
for _, scope := range []string{"missing", "all", "plain"} {
|
|
input := &configCommandInput[struct{}, struct{}]{
|
|
scope: commandkit.Supplied[string]{Value: scope, Set: true},
|
|
format: commandkit.Supplied[string]{Value: "yaml"},
|
|
}
|
|
if err := prepareConfigOperation(prepared, Diff, input); err == nil {
|
|
t.Errorf("comparison scope %q was accepted", scope)
|
|
}
|
|
}
|
|
if inputCalls != 0 || stateCalls != 0 || acquireCalls != 0 || compareCalls != 0 {
|
|
t.Fatalf("failed preflight invoked suite work: input=%d state=%d acquire=%d compare=%d", inputCalls, stateCalls, acquireCalls, compareCalls)
|
|
}
|
|
}
|