package ckconfig import ( "bytes" "context" "reflect" "strconv" "strings" "testing" ) func TestConfigScopeDiscoveryOrdersAndDeduplicatesCapabilities(t *testing.T) { app, err := New(Identity{Name: "example"}) if err != nil { t.Fatal(err) } newContextCalls := 0 acquireCalls := 0 suite := ConfigSuite[struct{}]{ NewContext: func() struct{} { newContextCalls++; return struct{}{} }, Scopes: []ConfigScope[struct{}]{ {Name: "zeta", Operations: []ConfigOperation{ConfigExplain, ConfigShow, ConfigExplain}, Acquire: func(context.Context, ConfigRequest, struct{}) (Acquired[ConfigView], error) { acquireCalls++ return Acquired[ConfigView]{}, nil }}, {Name: "promptkit", Operations: []ConfigOperation{ConfigSources}, Acquire: noopConfigAcquire[struct{}]}, {Name: "app", Operations: []ConfigOperation{ConfigShow, ConfigValidate, ConfigShow}, Acquire: func(context.Context, ConfigRequest, struct{}) (Acquired[ConfigView], error) { acquireCalls++ return Acquired[ConfigView]{}, nil }}, {Name: "metadata", Summary: "No provider is required."}, }, } normalized, err := normalizeConfigSuite(suite) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(normalized.Scopes[0].Operations, []ConfigOperation{ConfigShow, ConfigExplain}) || !reflect.DeepEqual(normalized.Scopes[2].Operations, []ConfigOperation{ConfigValidate, ConfigShow}) { t.Fatalf("normalized capabilities = %#v", normalized.Scopes) } if err := RegisterConfig(app, suite); err != nil { t.Fatal(err) } var textOutput bytes.Buffer outcome := app.Execute(context.Background(), []string{"config", "scopes"}, Streams{Out: &textOutput}) if outcome.Kind != OutcomeSuccess { t.Fatalf("text inventory outcome = %#v", outcome) } wantText := "app: validate, show\npromptkit: sources\nmetadata:\nzeta: show, explain\n" if textOutput.String() != wantText { t.Fatalf("text inventory = %q, want %q", textOutput.String(), wantText) } var yamlOutput bytes.Buffer outcome = app.Execute(context.Background(), []string{"config", "scopes", "--format", "yaml"}, Streams{Out: &yamlOutput}) if outcome.Kind != OutcomeSuccess { t.Fatalf("YAML inventory outcome = %#v", outcome) } if got := yamlOutput.String(); !strings.Contains(got, "scopes:") || strings.Index(got, "name: app") > strings.Index(got, "name: promptkit") || !strings.Contains(got, "operations: []") { t.Fatalf("YAML inventory = %q", got) } outcome = app.Execute(context.Background(), []string{"config", "scopes", "--help"}, Streams{}) if outcome.Kind != OutcomeSuccess { t.Fatalf("scope help outcome = %#v", outcome) } outcome = app.Execute(context.Background(), []string{"config", "scopes", "--format", "json"}, Streams{}) if outcome.Kind != OutcomeUsage { t.Fatalf("invalid format outcome = %#v", outcome) } if newContextCalls != 0 || acquireCalls != 0 { t.Fatalf("discovery called runtime callbacks: NewContext=%d Acquire=%d", newContextCalls, acquireCalls) } } func TestConfigInputDefaultsToAppAndRetainsAbsentSelections(t *testing.T) { suite := ConfigSuite[struct{}]{Scopes: []ConfigScope[struct{}]{{Name: "app"}}} prepared, err := prepareInput(CommandSpec[configInput[struct{}]]{Flags: configInputFlags(suite, "yaml")}, make([][]string, 4), nil, true) if err != nil { t.Fatal(err) } input := prepared.build() if input.Scope != "app" || input.Format != "yaml" || input.Selection.Config.Set || input.Selection.Overlay.Set { t.Fatalf("configuration defaults = %#v", input) } } func TestRegisterConfigOwnsPreparedScopeAndSelectorMetadata(t *testing.T) { type selectorInput struct{ Region Supplied[string] } var acquired selectorInput suite := ConfigSuite[selectorInput]{ Selectors: []Selector[selectorInput]{{ Binding: ValueFlag[selectorInput](FlagMeta{Name: "region"}, nil, parseString, func(input *selectorInput, value Supplied[string]) { input.Region = value }), Scopes: []string{"app"}, }}, Scopes: []ConfigScope[selectorInput]{{ Name: "app", Operations: []ConfigOperation{ConfigValidate}, Acquire: func(_ context.Context, _ ConfigRequest, input selectorInput) (Acquired[ConfigView], error) { acquired = input return Acquired[ConfigView]{}, nil }, }}, } app, err := New(Identity{Name: "example"}) if err != nil { t.Fatal(err) } if err := RegisterConfig(app, suite); err != nil { t.Fatal(err) } suite.Scopes[0].Name = "renamed" suite.Scopes[0].Operations[0] = ConfigShow suite.Selectors[0].Scopes[0] = "renamed" var inventory bytes.Buffer if result := app.Execute(context.Background(), []string{"config", "scopes"}, Streams{Out: &inventory}); result.Kind != OutcomeSuccess { t.Fatalf("scope inventory outcome = %#v", result) } if got := inventory.String(); got != "app: validate\n" { t.Fatalf("scope inventory after caller mutation = %q", got) } if result := app.Execute(context.Background(), []string{"config", "validate", "--scope=app", "--region=west"}, Streams{}); result.Kind != OutcomeSuccess { t.Fatalf("registered scope execution after caller mutation = %#v", result) } if acquired.Region != (Supplied[string]{Value: "west", Set: true}) { t.Fatalf("acquired selector after caller mutation = %#v", acquired) } } func TestRegisterConfigRejectsDuplicateSelectorNamesAndShorts(t *testing.T) { type selectorInput struct{ First, Second string } tests := []struct { name string firstMeta FlagMeta secondMeta FlagMeta }{ {name: "name", firstMeta: FlagMeta{Name: "region", Short: "r"}, secondMeta: FlagMeta{Name: "region", Short: "g"}}, {name: "short", firstMeta: FlagMeta{Name: "region", Short: "r"}, secondMeta: FlagMeta{Name: "group", Short: "r"}}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { app, err := New(Identity{Name: "example"}) if err != nil { t.Fatal(err) } suite := ConfigSuite[selectorInput]{Selectors: []Selector[selectorInput]{ {Binding: ValueFlag[selectorInput](test.firstMeta, nil, parseString, func(input *selectorInput, value Supplied[string]) { input.First = value.Value })}, {Binding: ValueFlag[selectorInput](test.secondMeta, nil, parseString, func(input *selectorInput, value Supplied[string]) { input.Second = value.Value })}, }} if err := RegisterConfig(app, suite); err == nil || !strings.Contains(err.Error(), "duplicate configuration selector flag") { t.Fatalf("RegisterConfig error = %v", err) } }) } } func TestRegisterConfigRejectsInvalidSuitesAndCollisions(t *testing.T) { tests := []struct { name string scope ConfigScope[struct{}] want string }{ {name: "empty name", scope: ConfigScope[struct{}]{}, want: "name must not be empty"}, {name: "all name", scope: ConfigScope[struct{}]{Name: "all"}, want: "reserved"}, {name: "unknown operation", scope: ConfigScope[struct{}]{Name: "app", Operations: []ConfigOperation{"diff"}}, want: "unknown operation"}, {name: "missing acquisition", scope: ConfigScope[struct{}]{Name: "app", Operations: []ConfigOperation{ConfigValidate}}, want: "without an acquisition"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { app, _ := New(Identity{Name: "example"}) err := RegisterConfig(app, ConfigSuite[struct{}]{Scopes: []ConfigScope[struct{}]{test.scope}}) if err == nil || !strings.Contains(err.Error(), test.want) { t.Fatalf("RegisterConfig error = %v, want %q", err, test.want) } }) } app, _ := New(Identity{Name: "example"}) custom, err := NewCommand(CommandSpec[struct{}]{Name: "config", Run: func(Invocation, struct{}) Outcome { return Success() }}) if err != nil { t.Fatal(err) } if err := app.Register(custom); err != nil { t.Fatal(err) } if err := RegisterConfig(app, ConfigSuite[struct{}]{}); err == nil { t.Fatal("replaced a pre-existing config command") } } func TestConfigSelectorPreflightAndScopedContextProjection(t *testing.T) { type selectorInput struct { Count Supplied[int] Enabled Supplied[bool] Other Supplied[int] Runtime string } baseContextCalls := 0 checkCalls := 0 var checked selectorInput countDefault := 9 suite := ConfigSuite[selectorInput]{ NewContext: func() selectorInput { baseContextCalls++; return selectorInput{Runtime: "invocation cache"} }, Selectors: []Selector[selectorInput]{ {Binding: ValueFlag[selectorInput](FlagMeta{Name: "count"}, &countDefault, strconv.Atoi, func(input *selectorInput, value Supplied[int]) { input.Count = value }), Scopes: []string{"app"}}, {Binding: BoolFlag[selectorInput](FlagMeta{Name: "enabled"}, true, func(input *selectorInput, value Supplied[bool]) { input.Enabled = value }), Scopes: []string{"promptkit"}}, {Binding: ValueFlag[selectorInput](FlagMeta{Name: "other"}, nil, strconv.Atoi, func(input *selectorInput, value Supplied[int]) { input.Other = value })}, }, CheckInput: func(_ Selection, input selectorInput) error { checkCalls++; checked = input; return nil }, Scopes: []ConfigScope[selectorInput]{ {Name: "app", Operations: []ConfigOperation{ConfigValidate}, Acquire: noopConfigAcquire[selectorInput]}, {Name: "promptkit", Operations: []ConfigOperation{ConfigValidate}, Acquire: noopConfigAcquire[selectorInput]}, }, } normalized, err := normalizeConfigSuite(suite) if err != nil { t.Fatal(err) } preparedSuite := prepareConfigSuite(normalized) spec := CommandSpec[configInput[selectorInput]]{Flags: configInputFlags(normalized, "text")} prepared, err := prepareInput(spec, [][]string{{"all"}, {"text"}, {""}, nil, {"0"}, {"false"}, nil}, nil, true) if err != nil { t.Fatal(err) } input := prepared.build() scopeNames, err := preflightConfig(preparedSuite, input, ConfigValidate) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(scopeNames, []string{"app", "promptkit"}) || checkCalls != 1 || checked.Count != (Supplied[int]{Value: 0, Set: true}) || checked.Enabled != (Supplied[bool]{Value: false, Set: true}) { t.Fatalf("preflight result=%v checked=%#v calls=%d", scopeNames, checked, checkCalls) } if !input.Selection.Config.Set || input.Selection.Config.Value != "" || input.Selection.Overlay.Set { t.Fatalf("configuration selection presence = %#v", input.Selection) } if baseContextCalls != 0 { t.Fatalf("NewContext called during preflight %d times", baseContextCalls) } contexts := contextsForScopes(preparedSuite, input, scopeNames) if baseContextCalls != 1 { t.Fatalf("NewContext calls = %d, want one", baseContextCalls) } if contexts["app"].Count != (Supplied[int]{Value: 0, Set: true}) || contexts["app"].Enabled != (Supplied[bool]{}) || contexts["app"].Other != (Supplied[int]{}) { t.Fatalf("app selectors = %#v", contexts["app"]) } if contexts["promptkit"].Count != (Supplied[int]{}) || contexts["promptkit"].Enabled != (Supplied[bool]{Value: false, Set: true}) || contexts["promptkit"].Runtime != "invocation cache" { t.Fatalf("promptkit selectors = %#v", contexts["promptkit"]) } } func TestConfigPreflightRejectsUnsupportedAggregateAndInapplicableSelector(t *testing.T) { type selectorInput struct{ Region Supplied[string] } newContextCalls := 0 suite := ConfigSuite[selectorInput]{ NewContext: func() selectorInput { newContextCalls++; return selectorInput{} }, Selectors: []Selector[selectorInput]{{Binding: ValueFlag[selectorInput](FlagMeta{Name: "region"}, nil, parseString, func(input *selectorInput, value Supplied[string]) { input.Region = value }), Scopes: []string{"promptkit"}}}, Scopes: []ConfigScope[selectorInput]{ {Name: "app", Operations: []ConfigOperation{ConfigValidate}, Acquire: noopConfigAcquire[selectorInput]}, {Name: "promptkit", Operations: []ConfigOperation{ConfigShow}, Acquire: noopConfigAcquire[selectorInput]}, }, } normalized, err := normalizeConfigSuite(suite) if err != nil { t.Fatal(err) } preparedSuite := prepareConfigSuite(normalized) prepared, err := prepareInput(CommandSpec[configInput[selectorInput]]{Flags: configInputFlags(normalized, "text")}, [][]string{{"all"}, {"text"}, nil, nil, nil}, nil, true) if err != nil { t.Fatal(err) } if _, err := preflightConfig(preparedSuite, prepared.build(), ConfigValidate); err == nil || !strings.Contains(err.Error(), "does not support validate") { t.Fatalf("unsupported aggregate error = %v", err) } if newContextCalls != 0 { t.Fatalf("unsupported aggregate created context %d times", newContextCalls) } prepared, err = prepareInput(CommandSpec[configInput[selectorInput]]{Flags: configInputFlags(normalized, "text")}, [][]string{{"app"}, {"text"}, nil, nil, {"west"}}, nil, true) if err != nil { t.Fatal(err) } if _, err := preflightConfig(preparedSuite, prepared.build(), ConfigValidate); err == nil || !strings.Contains(err.Error(), "not applicable") { t.Fatalf("inapplicable selector error = %v", err) } } func noopConfigAcquire[C any](context.Context, ConfigRequest, C) (Acquired[ConfigView], error) { return Acquired[ConfigView]{}, nil } func TestRequiredConfigSelectorOnlyAppliesToSelectedScopes(t *testing.T) { app, _ := New(Identity{Name: "example"}) calls := 0 acquire := func(context.Context, ConfigRequest, string) (Acquired[ConfigView], error) { calls++ return Acquired[ConfigView]{}, nil } err := RegisterConfig(app, ConfigSuite[string]{ Selectors: []Selector[string]{{Scopes: []string{"app"}, Binding: ValueFlag[string](FlagMeta{Name: "campaign", Required: true}, nil, parseString, func(c *string, v Supplied[string]) { *c = v.Value })}}, Scopes: []ConfigScope[string]{{Name: "app", Operations: []ConfigOperation{ConfigValidate}, Acquire: acquire}, {Name: "promptkit", Operations: []ConfigOperation{ConfigValidate}, Acquire: acquire}}, }) if err != nil { t.Fatal(err) } for _, tc := range []struct { args []string kind OutcomeKind calls int }{ {[]string{"--scope", "promptkit"}, OutcomeSuccess, 1}, {nil, OutcomeUsage, 0}, {[]string{"--scope", "all"}, OutcomeUsage, 0}, {[]string{"--scope", "all", "--campaign", "spring"}, OutcomeSuccess, 2}, {[]string{"--scope", "promptkit", "--campaign", "spring"}, OutcomeUsage, 0}, {[]string{"--help"}, OutcomeSuccess, 0}, } { calls = 0 var out bytes.Buffer result := app.Execute(context.Background(), append([]string{"config", "validate"}, tc.args...), Streams{Out: &out}) if result.Kind != tc.kind || calls != tc.calls { t.Fatalf("%v: result=%#v calls=%d", tc.args, result, calls) } if len(tc.args) > 0 && tc.args[0] == "--help" && !strings.Contains(out.String(), "required where applicable") { t.Fatalf("help=%q", out.String()) } } } func TestUnknownConfigScopeReportsChoicesWithoutRuntimeWork(t *testing.T) { for _, tc := range []struct { names []string scope []string choices string }{ {[]string{"promptkit"}, nil, "available: promptkit"}, {[]string{"zeta", "alpha", "promptkit", "app"}, []string{"--scope", "unknown"}, "available: app, promptkit, alpha, zeta"}, } { runtimeCalls := 0 suite := ConfigSuite[struct{}]{NewContext: func() struct{} { runtimeCalls++; return struct{}{} }} for _, name := range tc.names { suite.Scopes = append(suite.Scopes, ConfigScope[struct{}]{Name: name, Operations: []ConfigOperation{ConfigValidate}, Acquire: func(context.Context, ConfigRequest, struct{}) (Acquired[ConfigView], error) { runtimeCalls++ return Acquired[ConfigView]{}, nil }}) } app, _ := New(Identity{Name: "example"}) if err := RegisterConfig(app, suite); err != nil { t.Fatal(err) } var out, diagnostics bytes.Buffer result := app.Execute(context.Background(), append([]string{"config", "validate"}, tc.scope...), Streams{Out: &out, Err: &diagnostics}) if result.Kind != OutcomeUsage || runtimeCalls != 0 || out.Len() != 0 || !strings.Contains(diagnostics.String(), tc.choices) { t.Fatalf("result=%#v calls=%d out=%q diagnostics=%q", result, runtimeCalls, out.String(), diagnostics.String()) } } }