package commandkit 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\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 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) } 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(normalized, 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(normalized, 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) } 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(normalized, 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(normalized, 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 }