Finish maintained consumer examples

This commit is contained in:
2026-10-11 01:50:48 +00:00
parent f7174a078e
commit e89cc2220f
17 changed files with 291 additions and 3097 deletions

View File

@@ -1,78 +0,0 @@
package ckconfig_test
import (
"bytes"
"context"
"errors"
"fmt"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"strings"
"testing"
)
func TestStandardConfigurationCancellationBoundaries(t *testing.T) {
for _, operation := range []string{"validate", "show", "sources", "explain"} {
for _, before := range []bool{true, false} {
t.Run(fmt.Sprintf("%s/before=%t", operation, before), func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if before {
cancel()
}
loads, contexts := 0, 0
scope, err := commandkit.NewConfigKitScope[struct{}, struct{}]("app", "", func(context.Context, commandkit.Selection, struct{}) (*configkit.Result[struct{}], error) {
loads++
cancel()
return configkit.Load(configkit.NoSource(), configkit.Options[struct{}]{})
})
if err != nil {
t.Fatal(err)
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{scope}, NewContext: func() struct{} { contexts++; return struct{}{} }}); err != nil {
t.Fatal(err)
}
args := []string{"config", operation}
if operation == "explain" {
args = append(args, "--path", "")
}
var out bytes.Buffer
result := app.Execute(ctx, args, commandkit.Streams{Out: &out})
want := 1
if before {
want = 0
}
if result.Kind != commandkit.OutcomeOperational || result.Status != 1 || !errors.Is(result.Cause, context.Canceled) || loads != want || contexts != want || out.Len() != 0 {
t.Fatalf("result=%#v loads=%d contexts=%d out=%q", result, loads, contexts, out.String())
}
})
}
}
}
func TestAggregateCancellationRetainsCompletedValidationAndDiagnostics(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
calls := 0
acquire := func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
calls++
if calls == 2 {
cancel()
}
return commandkit.Acquired[commandkit.ConfigView]{Diagnostics: []commandkit.Diagnostic{commandkit.MessageDiagnostic(configkit.SeverityWarning, "acquired warning")}}, nil
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
{Name: "promptkit", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
{Name: "zeta", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
}}); err != nil {
t.Fatal(err)
}
var out, diagnostics bytes.Buffer
result := app.Execute(ctx, []string{"config", "validate", "--scope", "all"}, commandkit.Streams{Out: &out, Err: &diagnostics})
if !errors.Is(result.Cause, context.Canceled) || !result.ReportIncomplete || calls != 2 || out.String() != "app: valid\n" || strings.Count(diagnostics.String(), "acquired warning") != 2 {
t.Fatalf("result=%#v calls=%d out=%q diagnostics=%q", result, calls, out.String(), diagnostics.String())
}
}

View File

@@ -1,359 +0,0 @@
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())
}
}
}

View File

@@ -1,720 +0,0 @@
package ckconfig_test
import (
"bytes"
"context"
"errors"
"io"
"reflect"
"strings"
"testing"
"testing/fstest"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"go.yaml.in/yaml/v4"
)
type effectiveYAML string
func (document effectiveYAML) WriteEffective(writer io.Writer) error {
_, err := io.WriteString(writer, string(document))
return err
}
type capturedSourceReader struct {
entries []configkit.SourceInfo
calls int
}
func TestConfigExplainPreservesConfigKitProvenanceAndRedaction(t *testing.T) {
type entry struct {
Enabled bool `yaml:"enabled"`
Credential configkit.Secret `yaml:"credential"`
}
type configuration struct {
Entries map[string]entry `yaml:"entries"`
Odd map[string]string `yaml:"odd"`
Value string `yaml:"value"`
Dependency string `yaml:"dependency"`
Secret configkit.Secret `yaml:"secret"`
}
const secretValue = "EXPLAIN_TEST_SECRET_VALUE"
t.Setenv("EXPLAIN_TEST_SECRET", secretValue)
input := []byte("entries:\n parent:\n enabled: false\n credential: EXPLAIN_TEST_SECRET\n child:\n extends: parent\nodd:\n \"key/name~\": escaped\nvalue: original\ndependency: source\nsecret: EXPLAIN_TEST_SECRET\n")
result, err := configkit.Load[configuration](configkit.Bytes("explanation.yml", input), configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
Secret: func(path configkit.Path) bool { return path.String() == "/entries/parent/credential" },
Resolve: func(value *configuration, resolution *configkit.Resolution) error {
resolution.Record(configkit.Keys("value"), configkit.ChangeNormalization, "upper case", configkit.Keys("dependency"))
value.Value = strings.ToUpper(value.Value)
return nil
},
}})
if err != nil {
t.Fatal(err)
}
loads, contexts := 0, 0
scope, err := commandkit.NewConfigKitScope[struct{}, configuration]("app", "", func(context.Context, commandkit.Selection, struct{}) (*configkit.Result[configuration], error) {
loads++
return result, nil
})
if err != nil {
t.Fatal(err)
}
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{NewContext: func() struct{} { contexts++; return struct{}{} }, Scopes: []commandkit.ConfigScope[struct{}]{scope}}); err != nil {
t.Fatal(err)
}
runYAML := func(pointer string) (map[string]any, commandkit.Outcome, string) {
var output bytes.Buffer
argv := []string{"config", "explain", "--path", pointer, "--format", "yaml"}
if pointer == "" {
argv = []string{"config", "explain", "--path=", "--format", "yaml"}
}
outcome := app.Execute(context.Background(), argv, commandkit.Streams{Out: &output, Err: io.Discard})
var report map[string]any
if outcome.Kind == commandkit.OutcomeSuccess {
if err := yaml.Unmarshal(output.Bytes(), &report); err != nil {
t.Fatalf("decode explanation YAML: %v; output=%q", err, output.String())
}
}
return report, outcome, output.String()
}
path := "/entries/child/enabled"
var native bytes.Buffer
if err := result.WriteExplanation(&native, configkit.Keys("entries", "child", "enabled")); err != nil {
t.Fatal(err)
}
var textOutput bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "explain", "--path", path}, commandkit.Streams{Out: &textOutput, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || textOutput.String() != native.String() {
t.Fatalf("native text outcome=%#v output=%q want=%q", outcome, textOutput.String(), native.String())
}
var inherited map[string]any
inherited, outcome, _ = runYAML(path)
if outcome.Kind != commandkit.OutcomeSuccess || inherited["path"] != path {
t.Fatalf("inherited explanation outcome=%#v report=%#v", outcome, inherited)
}
effective := inherited["effective"].(map[string]any)
if effective["present"] != true || effective["yaml"] != "false\n" {
t.Fatalf("inherited effective snapshot = %#v", effective)
}
origin := inherited["origin"].(map[string]any)
steps := origin["inheritance"].([]any)
step := steps[0].(map[string]any)
if step["from"] != "/entries/parent/enabled" || step["to"] != path || step["reference"] != "/entries/child/extends" {
t.Fatalf("inheritance paths = %#v", step)
}
escaped, outcome, _ := runYAML("/odd/key~1name~0")
if outcome.Kind != commandkit.OutcomeSuccess || escaped["path"] != "/odd/key~1name~0" || escaped["effective"].(map[string]any)["yaml"] != "escaped\n" {
t.Fatalf("escaped path outcome=%#v report=%#v", outcome, escaped)
}
historical, outcome, _ := runYAML("/entries/child/extends")
if outcome.Kind != commandkit.OutcomeSuccess || historical["effective"].(map[string]any)["present"] != false || len(historical["history"].([]any)) == 0 {
t.Fatalf("historical path outcome=%#v report=%#v", outcome, historical)
}
resolved, outcome, _ := runYAML("/value")
if outcome.Kind != commandkit.OutcomeSuccess || resolved["effective"].(map[string]any)["yaml"] != "ORIGINAL\n" {
t.Fatalf("resolved snapshot outcome=%#v report=%#v", outcome, resolved)
}
changes := resolved["history"].([]any)
last := changes[len(changes)-1].(map[string]any)
if last["reasons"].([]any)[0] != "upper case" || last["dependencies"].([]any)[0] != "/dependency" {
t.Fatalf("resolution change = %#v", last)
}
secretReport, outcome, secretText := runYAML("/entries/child/credential")
secretEffective := secretReport["effective"].(map[string]any)
if outcome.Kind != commandkit.OutcomeSuccess || secretEffective["redacted"] != true || strings.Contains(secretText, secretValue) {
t.Fatalf("secret report outcome=%#v report=%#v", outcome, secretReport)
}
for _, change := range secretReport["history"].([]any) {
entry := change.(map[string]any)
for _, key := range []string{"before", "after"} {
snapshot := entry[key].(map[string]any)
if snapshot["present"] == true && snapshot["redacted"] != true {
t.Fatalf("secret history was not redacted: %#v", entry)
}
}
}
directSecret, outcome, directSecretText := runYAML("/secret")
secretRef, refPresent := directSecret["secret_reference"].(map[string]any)
if !refPresent {
t.Fatalf("secret reference was absent: %#v", directSecret)
}
if outcome.Kind != commandkit.OutcomeSuccess || secretRef["name"] != "EXPLAIN_TEST_SECRET" || strings.Contains(directSecretText, secretValue) {
t.Fatalf("direct secret reference outcome=%#v report=%#v", outcome, directSecret)
}
root, outcome, _ := runYAML("")
if outcome.Kind != commandkit.OutcomeSuccess || root["path"] != "" {
t.Fatalf("root path outcome=%#v report=%#v", outcome, root)
}
loadsBeforeInvalid, contextsBeforeInvalid := loads, contexts
outcome = app.Execute(context.Background(), []string{"config", "explain"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage || loads != loadsBeforeInvalid || contexts != contextsBeforeInvalid {
t.Fatalf("missing path outcome=%#v loads=%d contexts=%d", outcome, loads, contexts)
}
outcome = app.Execute(context.Background(), []string{"config", "explain", "--path", "/bad~2path"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage || outcome.Status != 2 || loads != loadsBeforeInvalid || contexts != contextsBeforeInvalid {
t.Fatalf("malformed path outcome=%#v loads=%d contexts=%d", outcome, loads, contexts)
}
outcome = app.Execute(context.Background(), []string{"config", "explain", "--path", "/does-not-exist"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || outcome.Status != 1 {
t.Fatalf("unknown path outcome = %#v", outcome)
}
partial := &configKitShowPartialWriter{failure: io.ErrClosedPipe}
outcome = app.Execute(context.Background(), []string{"config", "explain", "--path", path}, commandkit.Streams{Out: partial, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.DeliveryError, io.ErrClosedPipe) {
t.Fatalf("explanation writer failure = %#v", outcome)
}
}
func (reader *capturedSourceReader) Sources() []configkit.SourceInfo {
reader.calls++
return reader.entries
}
func TestConfigSourcesReportsCapturedConfigKitSourceInventory(t *testing.T) {
type configuration struct {
Value string `yaml:"value"`
Shared string `yaml:"shared"`
Synthetic string `yaml:"synthetic"`
}
filesystem := configkit.NewFilesystem("fixture", fstest.MapFS{
"root/config/main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: ../shared.yml}]}\nvalue: base\n")},
"root/shared.yml": &fstest.MapFile{Data: []byte("shared: imported\n")},
"root/overlays/dev.yml": &fstest.MapFile{Data: []byte("value: overlay\n")},
})
filesystemContext := &configkit.FilesystemContext{Root: "root", FS: &filesystem}
overlayFilesystemContext := &configkit.FilesystemContext{Root: "root", BaseDir: "root/config", FS: &filesystem}
loaderCalls := 0
reader := &capturedSourceReader{}
scope, err := commandkit.NewConfigKitScope[struct{}, configuration]("app", "", func(context.Context, commandkit.Selection, struct{}) (*configkit.Result[configuration], error) {
loaderCalls++
result, err := configkit.LoadLayered(configkit.Input{
Source: filesystem.File("root/config/main.yml"),
Imports: true,
Filesystem: filesystemContext,
}, configkit.LayeredOptions[configuration]{
Definition: configkit.Definition[configuration]{},
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.Mapping("synthetic source", map[string]any{"synthetic": "underlay"})}}}},
Overlay: "dev",
OverlayFilesystem: overlayFilesystemContext,
})
if err != nil {
return nil, err
}
reader.entries = result.Sources()
return result, nil
})
if err != nil {
t.Fatal(err)
}
// Replace the result's reader after the owning loader has captured its source inventory.
originalAcquire := scope.Acquire
scope.Acquire = func(ctx context.Context, request commandkit.ConfigRequest, input struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
acquired, err := originalAcquire(ctx, request, input)
if err == nil {
acquired.Value.Sources = reader
}
return acquired, err
}
// Register the wrapped scope so the command reads the captured public facet once.
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{scope}}); err != nil {
t.Fatal(err)
}
var output bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "sources", "--format", "yaml"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || loaderCalls != 1 || reader.calls != 1 {
var details bytes.Buffer
for _, diagnostic := range outcome.Diagnostics {
_ = diagnostic.Write(&details)
}
t.Fatalf("sources outcome=%#v cause=%v details=%q loader calls=%d reader calls=%d", outcome, outcome.Cause, details.String(), loaderCalls, reader.calls)
}
var report struct {
Scope string `yaml:"scope"`
Sources []struct {
ID string `yaml:"id"`
Label string `yaml:"label"`
Path string `yaml:"path"`
Layer string `yaml:"layer"`
Kind string `yaml:"kind"`
UnderlayIndex int `yaml:"underlay_index"`
ImportedFrom *struct {
SourceID string `yaml:"source_id"`
Line int `yaml:"line"`
Column int `yaml:"column"`
} `yaml:"imported_from"`
} `yaml:"sources"`
}
if err := yaml.Unmarshal(output.Bytes(), &report); err != nil {
t.Fatal(err)
}
if report.Scope != "app" || len(report.Sources) != 4 {
t.Fatalf("source report = %#v", report)
}
if report.Sources[0].Path != "root/config/main.yml" || report.Sources[0].ID != "source-1" {
t.Fatalf("main source = %#v", report.Sources[0])
}
if report.Sources[0].ImportedFrom != nil {
t.Fatalf("root source imported_from = %#v, want null", report.Sources[0].ImportedFrom)
}
imported := report.Sources[1]
if imported.Path != "root/shared.yml" || imported.ImportedFrom == nil || imported.ImportedFrom.SourceID != report.Sources[0].ID || imported.ImportedFrom.Line == 0 {
t.Fatalf("import source correlation = %#v", imported)
}
synthetic := report.Sources[2]
if synthetic.Kind != string(configkit.SourceKindMapping) || synthetic.Path != "" || synthetic.Layer != string(configkit.LayerUnderlay) || synthetic.UnderlayIndex != 1 {
t.Fatalf("synthetic underlay source = %#v", synthetic)
}
if report.Sources[3].Layer != string(configkit.LayerOverlay) || report.Sources[3].Path != "root/overlays/dev.yml" {
t.Fatalf("overlay source = %#v", report.Sources[3])
}
}
func TestConfigSourcesHandlesEmptyInventoryAllPreflightAndWriterFailure(t *testing.T) {
reader := &capturedSourceReader{}
newContextCalls, acquireCalls := 0, 0
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
suite := commandkit.ConfigSuite[string]{
NewContext: func() string { newContextCalls++; return "context" },
Scopes: []commandkit.ConfigScope[string]{{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigSources}, Acquire: func(context.Context, commandkit.ConfigRequest, string) (commandkit.Acquired[commandkit.ConfigView], error) {
acquireCalls++
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Sources: reader}}, nil
}}},
}
if err := commandkit.RegisterConfig(app, suite); err != nil {
t.Fatal(err)
}
var output bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "sources", "--scope", "all"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage || newContextCalls != 0 || acquireCalls != 0 || output.Len() != 0 {
t.Fatalf("all preflight outcome=%#v contexts=%d acquires=%d output=%q", outcome, newContextCalls, acquireCalls, output.String())
}
outcome = app.Execute(context.Background(), []string{"config", "sources", "--format", "yaml"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || output.String() != "scope: app\nsources: []\n" || reader.calls != 1 {
t.Fatalf("empty source report outcome=%#v output=%q reader calls=%d", outcome, output.String(), reader.calls)
}
reader.entries = []configkit.SourceInfo{{ID: "source-1", Label: "label with \"quotes\"", Path: "path with spaces.yml", Layer: configkit.LayerBase, Kind: configkit.SourceKindFile}}
var textOutput bytes.Buffer
outcome = app.Execute(context.Background(), []string{"config", "sources"}, commandkit.Streams{Out: &textOutput, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(textOutput.String(), `label: "label with \"quotes\""`) || !strings.Contains(textOutput.String(), `path: "path with spaces.yml"`) {
t.Fatalf("quoted text source output outcome=%#v output=%q", outcome, textOutput.String())
}
partial := &configKitShowPartialWriter{failure: io.ErrClosedPipe}
outcome = app.Execute(context.Background(), []string{"config", "sources"}, commandkit.Streams{Out: partial, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.DeliveryError, io.ErrClosedPipe) {
t.Fatalf("source writer failure = %#v", outcome)
}
}
func TestConfigShowWritesNativeSingleScopeAndFramesAggregateYAML(t *testing.T) {
const native = "%YAML 1.1\n# authored comment\n---\nvalue: null\nitems: []\nmissing: {}\n"
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
var calls []string
suite := commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
{Name: "zeta", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
calls = append(calls, "zeta")
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Effective: effectiveYAML("%YAML 1.1\n# zeta comment\nvalue: zeta\n")}}, nil
}},
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
calls = append(calls, "app")
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Effective: effectiveYAML(native)}}, nil
}},
}}
if err := commandkit.RegisterConfig(app, suite); err != nil {
t.Fatal(err)
}
var single bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "show"}, commandkit.Streams{Out: &single, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || single.String() != native {
t.Fatalf("single show outcome=%#v output=%q", outcome, single.String())
}
calls = nil
outcome = app.Execute(context.Background(), []string{"config", "show", "--format", "text"}, commandkit.Streams{Out: &single, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage || len(calls) != 0 {
t.Fatalf("text format outcome=%#v acquisitions=%v", outcome, calls)
}
calls = nil
var aggregate bytes.Buffer
outcome = app.Execute(context.Background(), []string{"config", "show", "--scope", "all"}, commandkit.Streams{Out: &aggregate, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !reflect.DeepEqual(calls, []string{"app", "zeta"}) {
t.Fatalf("aggregate outcome=%#v acquisitions=%v", outcome, calls)
}
if !strings.Contains(aggregate.String(), "%YAML 1.1\n# authored comment\n---\n# scope: app\n") || !strings.Contains(aggregate.String(), "...\n%YAML 1.1\n# zeta comment\n---\n# scope: zeta\nvalue: zeta\n") {
t.Fatalf("aggregate framing = %q", aggregate.String())
}
decoder := yaml.NewDecoder(bytes.NewReader(aggregate.Bytes()))
var first, second map[string]any
if err := decoder.Decode(&first); err != nil {
t.Fatal(err)
}
if err := decoder.Decode(&second); err != nil {
t.Fatal(err)
}
var extra any
if err := decoder.Decode(&extra); err != io.EOF {
t.Fatalf("third YAML document decode error = %v, value=%#v", err, extra)
}
if first["value"] != nil || first["items"] == nil || first["missing"] == nil || second["value"] != "zeta" {
t.Fatalf("decoded documents = %#v and %#v", first, second)
}
if _, exists := first["absent"]; exists {
t.Fatalf("absent field was added to native document: %#v", first)
}
}
func TestConfigShowAcquiresAllBeforeOutputAndTracksWriterFailures(t *testing.T) {
failure := errors.New("second scope failed")
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
calls := 0
suite := commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
calls++
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Effective: effectiveYAML("value: app\n")}}, nil
}},
{Name: "promptkit", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
calls++
return commandkit.Acquired[commandkit.ConfigView]{Diagnostics: []commandkit.Diagnostic{commandkit.MessageDiagnostic(configkit.SeverityWarning, "provider warning")}}, failure
}},
}}
if err := commandkit.RegisterConfig(app, suite); err != nil {
t.Fatal(err)
}
var output bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "show", "--scope", "all"}, commandkit.Streams{Out: &output, Err: io.Discard})
if !errors.Is(outcome.Cause, failure) || outcome.ReportIncomplete || output.Len() != 0 || calls != 2 || len(outcome.Diagnostics) < 1 || outcome.Diagnostics[0].Severity != configkit.SeverityWarning {
t.Fatalf("acquisition failure outcome=%#v output=%q calls=%d", outcome, output.String(), calls)
}
app, _ = commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Effective: effectiveYAML("value: app\n")}}, nil
}}}}); err != nil {
t.Fatal(err)
}
partial := &configKitShowPartialWriter{failure: io.ErrClosedPipe}
outcome = app.Execute(context.Background(), []string{"config", "show", "--scope", "all"}, commandkit.Streams{Out: partial, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.Cause, io.ErrClosedPipe) || !errors.Is(outcome.DeliveryError, io.ErrClosedPipe) || len(partial.written) == 0 {
t.Fatalf("writer failure outcome = %#v", outcome)
}
}
type configKitShowPartialWriter struct {
written []byte
failure error
}
func (writer *configKitShowPartialWriter) Write(data []byte) (int, error) {
n := len(data)
if n > 4 {
n = 4
}
writer.written = append(writer.written, data[:n]...)
return n, writer.failure
}
func TestConfigKitScopeUsesDefaultsOverlayAndRecordedResolution(t *testing.T) {
type configuration struct {
Mode string `yaml:"mode"`
Defaulted string `yaml:"defaulted"`
}
filesystem := configkit.NewFilesystem("fixture", fstest.MapFS{
"config/main.yml": &fstest.MapFile{Data: []byte("mode: base\n")},
"config/overlays/dev.yml": &fstest.MapFile{Data: []byte("mode: overlay\n")},
})
definition := configkit.Definition[configuration]{
Defaults: func() configuration { return configuration{Defaulted: "from default"} },
Resolve: func(input *configuration, resolution *configkit.Resolution) error {
resolution.Record(configkit.Keys("mode"), configkit.ChangeNormalization, "normalized for use")
input.Mode = strings.ToUpper(input.Mode)
return nil
},
}
var received commandkit.Selection
scope, err := commandkit.NewConfigKitScope[struct{}, configuration]("app", "Application settings.", func(_ context.Context, selection commandkit.Selection, _ struct{}) (*configkit.Result[configuration], error) {
received = selection
return configkit.LoadLayered(configkit.Input{Source: filesystem.File(selection.Config.Value)}, configkit.LayeredOptions[configuration]{
Definition: definition,
Overlay: selection.Overlay.Value,
OverlayFilesystem: &configkit.FilesystemContext{Root: "config", BaseDir: "config", FS: &filesystem},
})
})
if err != nil {
t.Fatal(err)
}
selection := commandkit.Selection{
Config: commandkit.Supplied[string]{Value: "config/main.yml", Set: true},
Overlay: commandkit.Supplied[string]{Value: "dev", Set: true},
}
acquired, err := scope.Acquire(context.Background(), commandkit.ConfigRequest{Operation: commandkit.ConfigValidate, Selection: selection}, struct{}{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(received, selection) {
t.Fatalf("loader selection = %#v, want %#v", received, selection)
}
if acquired.Value.Effective == nil || acquired.Value.Sources == nil || acquired.Value.Explanation == nil {
t.Fatalf("ConfigKit result facets = %#v", acquired.Value)
}
if got := acquired.Value.Sources.Sources(); len(got) < 2 {
t.Fatalf("sources = %#v, want base and overlay", got)
}
var effective bytes.Buffer
if err := acquired.Value.Effective.WriteEffective(&effective); err != nil {
t.Fatal(err)
}
if !strings.Contains(effective.String(), "mode: OVERLAY") || !strings.Contains(effective.String(), "defaulted: from default") {
t.Fatalf("effective configuration = %q", effective.String())
}
if explanation, ok := acquired.Value.Explanation.Explain(configkit.Keys("mode")); !ok || len(explanation.History) == 0 {
t.Fatalf("resolution explanation = %#v, present=%t", explanation, ok)
}
}
func TestConfigKitScopePreservesAbsentAndEmptySelections(t *testing.T) {
type configuration struct {
Value string `yaml:"value"`
}
var seen []commandkit.Selection
scope, err := commandkit.NewConfigKitScope[struct{}, configuration]("app", "", func(_ context.Context, selection commandkit.Selection, _ struct{}) (*configkit.Result[configuration], error) {
seen = append(seen, selection)
return configkit.Load(configkit.NoSource(), configkit.Options[configuration]{Definition: configkit.Definition[configuration]{Defaults: func() configuration { return configuration{Value: "default"} }}})
})
if err != nil {
t.Fatal(err)
}
_, err = scope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{})
if err != nil {
t.Fatal(err)
}
empty := commandkit.Selection{Config: commandkit.Supplied[string]{Set: true}, Overlay: commandkit.Supplied[string]{Set: true}}
_, err = scope.Acquire(context.Background(), commandkit.ConfigRequest{Selection: empty}, struct{}{})
if err != nil {
t.Fatal(err)
}
want := []commandkit.Selection{{}, empty}
if !reflect.DeepEqual(seen, want) {
t.Fatalf("loader selections = %#v, want %#v", seen, want)
}
}
func TestConfigKitScopeRequiresLoaderAndNonNilResult(t *testing.T) {
if _, err := commandkit.NewConfigKitScope[struct{}, struct{}]("app", "", nil); err == nil {
t.Fatal("accepted a nil ConfigKit loader")
}
scope, err := commandkit.NewConfigKitScope[struct{}, struct{}]("app", "", func(context.Context, commandkit.Selection, struct{}) (*configkit.Result[struct{}], error) {
return nil, nil
})
if err != nil {
t.Fatal(err)
}
if _, err := scope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{}); err == nil || !strings.Contains(err.Error(), "nil result") {
t.Fatalf("nil result error = %v", err)
}
}
func TestConfigKitLoadErrorRetainsIdentityAndStructuredDiagnostics(t *testing.T) {
type configuration struct {
Name string `yaml:"name"`
}
var loadError error
scope, err := commandkit.NewConfigKitScope[struct{}, configuration]("app", "", func(context.Context, commandkit.Selection, struct{}) (*configkit.Result[configuration], error) {
_, loadError = configkit.Load(configkit.Mapping("bad source", map[string]any{"unknown": true}), configkit.Options[configuration]{Definition: configkit.Definition[configuration]{}})
return nil, loadError
})
if err != nil {
t.Fatal(err)
}
acquired, acquireError := scope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{})
var publicLoadError *configkit.LoadError
if acquireError != loadError || !errors.As(acquireError, &publicLoadError) {
t.Fatalf("load error identity = %v (original %v), public type=%T", acquireError, loadError, acquireError)
}
if len(acquired.Diagnostics) != 1 || acquired.Diagnostics[0].Severity != configkit.SeverityError {
t.Fatalf("structured diagnostics = %#v", acquired.Diagnostics)
}
var rendered bytes.Buffer
if err := acquired.Diagnostics[0].Write(&rendered); err != nil {
t.Fatal(err)
}
if !strings.Contains(rendered.String(), `Configuration scope "app":`) || !strings.Contains(rendered.String(), `"bad source"`) {
t.Fatalf("grouped diagnostics = %q", rendered.String())
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{scope}}); err != nil {
t.Fatal(err)
}
var output, diagnostic bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "validate"}, commandkit.Streams{Out: &output, Err: &diagnostic})
if outcome.Cause != loadError || len(outcome.Diagnostics) != 1 || strings.Count(diagnostic.String(), `Configuration scope "app":`) != 1 || output.Len() != 0 {
t.Fatalf("presented load failure outcome=%#v output=%q diagnostics=%q", outcome, output.String(), diagnostic.String())
}
}
func TestConfigValidationEmitsSuccessAsScopesCompleteAndStopsAtFailure(t *testing.T) {
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
var calls []string
newContextCalls := 0
failure := errors.New("promptkit could not load")
suite := commandkit.ConfigSuite[string]{
NewContext: func() string { newContextCalls++; return "runtime" },
Scopes: []commandkit.ConfigScope[string]{
{Name: "zeta", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: func(context.Context, commandkit.ConfigRequest, string) (commandkit.Acquired[commandkit.ConfigView], error) {
calls = append(calls, "zeta")
return commandkit.Acquired[commandkit.ConfigView]{}, nil
}},
{Name: "promptkit", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: func(context.Context, commandkit.ConfigRequest, string) (commandkit.Acquired[commandkit.ConfigView], error) {
calls = append(calls, "promptkit")
return commandkit.Acquired[commandkit.ConfigView]{}, failure
}},
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: func(context.Context, commandkit.ConfigRequest, string) (commandkit.Acquired[commandkit.ConfigView], error) {
calls = append(calls, "app")
return commandkit.Acquired[commandkit.ConfigView]{}, nil
}},
},
}
if err := commandkit.RegisterConfig(app, suite); err != nil {
t.Fatal(err)
}
var output, diagnostic bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "validate", "--scope", "all"}, commandkit.Streams{Out: &output, Err: &diagnostic})
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, failure) || !outcome.ReportIncomplete {
t.Fatalf("validation outcome = %#v", outcome)
}
if !reflect.DeepEqual(calls, []string{"app", "promptkit"}) || newContextCalls != 1 {
t.Fatalf("acquisitions = %v, NewContext calls=%d", calls, newContextCalls)
}
if output.String() != "app: valid\n" || !strings.Contains(diagnostic.String(), failure.Error()) {
t.Fatalf("output=%q diagnostics=%q", output.String(), diagnostic.String())
}
}
func TestConfigValidationYAMLWarningsAndShortWrites(t *testing.T) {
type configuration struct {
Name string `yaml:"name"`
}
warningScope, err := commandkit.NewConfigKitScope[struct{}, configuration]("app", "", func(context.Context, commandkit.Selection, struct{}) (*configkit.Result[configuration], error) {
return configkit.Load(configkit.NoSource(), configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
Validate: func(_ *configuration, validation *configkit.Validation) {
validation.Warning(configkit.Keys("name"), "uses a demonstration name")
},
}})
})
if err != nil {
t.Fatal(err)
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{warningScope}}); err != nil {
t.Fatal(err)
}
var output, diagnostic bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "validate", "--format", "yaml"}, commandkit.Streams{Out: &output, Err: &diagnostic})
if outcome.Kind != commandkit.OutcomeSuccess || outcome.Status != 0 || outcome.DiagnosticDelivery != commandkit.DeliveryComplete {
t.Fatalf("warning validation outcome = %#v", outcome)
}
if output.String() != "scopes:\n - name: app\n valid: true\n" || !strings.Contains(diagnostic.String(), "uses a demonstration name") {
t.Fatalf("output=%q diagnostics=%q", output.String(), diagnostic.String())
}
outcome = app.Execute(context.Background(), []string{"config", "validate"}, commandkit.Streams{Out: configKitPartialWriter{}, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.Cause, io.ErrShortWrite) || !errors.Is(outcome.DeliveryError, io.ErrShortWrite) {
t.Fatalf("report short write outcome = %#v", outcome)
}
var delivered bytes.Buffer
outcome = app.Execute(context.Background(), []string{"config", "validate"}, commandkit.Streams{Out: &delivered, Err: configKitPartialWriter{}})
if outcome.DiagnosticDelivery != commandkit.DeliveryIncomplete || !errors.Is(outcome.DeliveryError, io.ErrShortWrite) {
t.Fatalf("diagnostic short write outcome = %#v", outcome)
}
}
type configKitPartialWriter struct{}
func (configKitPartialWriter) Write(data []byte) (int, error) {
if len(data) == 0 {
return 0, nil
}
return len(data) - 1, nil
}
func TestValidationReportDeliveryErrorsAtEachWriteBoundary(t *testing.T) {
reportErr := errors.New("report failed")
diagnosticErr := errors.New("diagnostic failed")
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
acquire := func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
return commandkit.Acquired[commandkit.ConfigView]{}, nil
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
{Name: "promptkit", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: acquire},
}}); err != nil {
t.Fatal(err)
}
for _, format := range []string{"text", "yaml"} {
writes := 2
if format == "yaml" {
writes = 3
}
for at := 1; at <= writes; at++ {
for _, brokenErr := range []bool{false, true} {
out := &validationBoundaryWriter{failAt: at, err: reportErr}
errStream := io.Writer(io.Discard)
if brokenErr {
errStream = failingWriter{diagnosticErr}
}
result := app.Execute(context.Background(), []string{"config", "validate", "--scope", "all", "--format", format}, commandkit.Streams{Out: out, Err: errStream})
if result.Kind != commandkit.OutcomeOperational || !result.ReportIncomplete || !errors.Is(result.Cause, reportErr) || !errors.Is(result.DeliveryError, reportErr) || (brokenErr && !errors.Is(result.DeliveryError, diagnosticErr)) {
t.Fatalf("format=%s at=%d brokenErr=%t result=%#v", format, at, brokenErr, result)
}
if at > 1 && out.written.Len() == 0 {
t.Fatal("lost completed prefix")
}
}
}
}
failedApp, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterConfig(failedApp, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigValidate}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
return commandkit.Acquired[commandkit.ConfigView]{}, reportErr
}}}}); err != nil {
t.Fatal(err)
}
result := failedApp.Execute(context.Background(), []string{"config", "validate"}, commandkit.Streams{})
if !errors.Is(result.Cause, reportErr) || result.DeliveryError != nil {
t.Fatalf("acquisition failure=%#v", result)
}
}
type validationBoundaryWriter struct {
failAt, calls int
err error
written bytes.Buffer
}
func (w *validationBoundaryWriter) Write(p []byte) (int, error) {
w.calls++
if w.calls == w.failAt {
return 0, w.err
}
return w.written.Write(p)
}

View File

@@ -7,6 +7,7 @@ import (
"strconv"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
)
@@ -19,38 +20,28 @@ type greetInput struct {
Tags commandkit.Supplied[[]string]
}
func loadAppConfiguration(context.Context, commandkit.Selection, struct{}) (*configkit.Result[appConfiguration], error) {
return configkit.Load[appConfiguration](configkit.Bytes("simple example", []byte("greeting: hello\n")))
func loadAppConfiguration(_ context.Context, _ ckconfig.Selection) (*configkit.Result[appConfiguration], error) {
return configkit.Load[appConfiguration](configkit.Mapping("simple application settings", map[string]any{
"greeting": "Hello",
}))
}
func newApp() (*commandkit.App, error) {
app, err := commandkit.New(commandkit.Identity{Name: "simple-example", Version: "1.0"})
if err != nil {
return nil, err
}
scope, err := commandkit.NewConfigKitScope[struct{}, appConfiguration]("app", "Application settings.", loadAppConfiguration)
if err != nil {
return nil, err
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{scope}}); err != nil {
return nil, err
}
countDefault := 1
tagDefaults := []string{"example"}
greet, err := commandkit.NewCommand(commandkit.CommandSpec[greetInput]{
greet := commandkit.NewCommand(commandkit.CommandSpec[greetInput]{
Name: "greet",
Summary: "Show flag presence and repeated values.",
Flags: []commandkit.Flag[greetInput]{
commandkit.ValueFlag[greetInput](commandkit.FlagMeta{Name: "count", Usage: "Greeting count."}, &countDefault, strconv.Atoi, func(input *greetInput, value commandkit.Supplied[int]) {
commandkit.ValueFlag(commandkit.FlagMeta{Name: "count", Usage: "Greeting count."}, &countDefault, strconv.Atoi, func(input *greetInput, value commandkit.Supplied[int]) {
input.Count = value
}),
commandkit.RepeatFlag[greetInput](commandkit.FlagMeta{Name: "tag", Usage: "A repeatable label."}, tagDefaults, func(value string) (string, error) { return value, nil }, func(input *greetInput, value commandkit.Supplied[[]string]) {
commandkit.RepeatFlag(commandkit.FlagMeta{Name: "tag", Usage: "A repeatable label."}, tagDefaults, func(value string) (string, error) { return value, nil }, func(input *greetInput, value commandkit.Supplied[[]string]) {
input.Tags = value
}),
},
Run: func(invocation commandkit.Invocation, input greetInput) commandkit.Outcome {
configuration, err := loadAppConfiguration(invocation.Context, commandkit.Selection{}, struct{}{})
configuration, err := loadAppConfiguration(invocation.Context, ckconfig.Selection{})
if err != nil {
return commandkit.Failure(err)
}
@@ -68,13 +59,11 @@ func newApp() (*commandkit.App, error) {
return commandkit.Success()
},
})
if err != nil {
return nil, err
}
if err := app.Register(greet); err != nil {
return nil, err
}
return app, nil
return commandkit.New(
commandkit.Identity{Name: "simple-example", Version: "1.0"},
ckconfig.AppCommand(loadAppConfiguration),
greet,
)
}
func main() {

View File

@@ -1,52 +0,0 @@
package ckconfig_test
import (
"bytes"
"context"
"strings"
"testing"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
)
func TestParentHelpDiscoversMetadataOnlyGroups(t *testing.T) {
leaf := commandkit.Group("notes", "Read notes metadata.")
parent := commandkit.Group("topics", "Browse topic metadata.", leaf)
app, err := commandkit.New(commandkit.Identity{Name: "example"}, parent)
if err != nil {
t.Fatal(err)
}
loads := 0
if err := ckconfig.RegisterConfig(app, ckconfig.ConfigSuite[struct{}]{Scopes: []ckconfig.ConfigScope[struct{}]{{Name: "app", Operations: []ckconfig.ConfigOperation{ckconfig.ConfigValidate}, Acquire: func(context.Context, ckconfig.ConfigRequest, struct{}) (commandkit.Acquired[ckconfig.ConfigView], error) {
loads++
return commandkit.Acquired[ckconfig.ConfigView]{}, nil
}}}}); err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
args []string
content string
}{
{nil, "Browse topic metadata."},
{[]string{"topics"}, "Read notes metadata."},
{[]string{"topics", "--help"}, "Read notes metadata."},
{[]string{"help", "topics"}, "Read notes metadata."},
{[]string{"topics", "notes"}, "Read notes metadata."},
{[]string{"help", "topics", "notes"}, "Read notes metadata."},
} {
var out bytes.Buffer
result := app.Execute(context.Background(), tc.args, commandkit.Streams{Out: &out})
if result.Kind != commandkit.OutcomeSuccess || !strings.Contains(out.String(), tc.content) || loads != 0 {
t.Fatalf("%v: result=%#v output=%q loads=%d", tc.args, result, out.String(), loads)
}
if len(tc.args) == 1 && (!strings.Contains(out.String(), "Available Commands:") || !strings.Contains(out.String(), "[command]")) {
t.Fatalf("missing child inventory/usage: %q", out.String())
}
}
var diagnostic bytes.Buffer
result := app.Execute(context.Background(), []string{"topics", "unknown"}, commandkit.Streams{Err: &diagnostic})
if result.Kind != commandkit.OutcomeUsage || !strings.Contains(diagnostic.String(), "[command]") {
t.Fatalf("result=%#v diagnostic=%q", result, diagnostic.String())
}
}

View File

@@ -1,200 +0,0 @@
package ckconfig
import (
"context"
"fmt"
"io"
"strings"
"testing"
)
var performanceFramedBytes []byte
func BenchmarkPerformanceYAMLFraming(b *testing.B) {
fixtures := []struct {
name string
scope string
native []byte
}{
{name: "4k_ordinary", scope: "app", native: performanceNativeDocument(4<<10, false)},
{name: "1m_directive", scope: "promptkit", native: performanceNativeDocument(1<<20, true)},
}
for _, fixture := range fixtures {
b.Run("framing/"+fixture.name, func(b *testing.B) {
framed, hasDirective := frameEffectiveYAML(fixture.scope, fixture.native)
if len(framed) <= len(fixture.native) || hasDirective != strings.Contains(fixture.name, "directive") {
b.Fatalf("framing fixture result has unexpected size or directive classification: native=%d framed=%d", len(fixture.native), len(framed))
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
performanceFramedBytes, _ = frameEffectiveYAML(fixture.scope, fixture.native)
}
})
b.Run("aggregate/"+fixture.name, func(b *testing.B) {
app := performanceShowApp(b, fixture.native, fixture.native)
args := []string{"config", "show", "--scope", "all"}
verifyConfigReport(b, app, args, len(fixture.native)*2)
streams := Streams{Out: io.Discard, Err: io.Discard}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if outcome := app.Execute(context.Background(), args, streams); outcome.Kind != OutcomeSuccess {
b.Fatalf("aggregate config show outcome = %#v", outcome)
}
}
})
}
single := performanceNativeDocument(4<<10, false)
app := performanceShowApp(b, single)
args := []string{"config", "show"}
b.Run("single_scope/4k_ordinary", func(b *testing.B) {
verifyConfigReport(b, app, args, len(single))
streams := Streams{Out: io.Discard, Err: io.Discard}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if outcome := app.Execute(context.Background(), args, streams); outcome.Kind != OutcomeSuccess {
b.Fatalf("single-scope config show outcome = %#v", outcome)
}
}
})
}
func BenchmarkPerformanceConfigScopes(b *testing.B) {
for _, scopeCount := range []int{2, 32} {
suite := performanceConfigSuite(scopeCount, []ConfigOperation{ConfigValidate, ConfigShow})
app := performanceConfigApp(b, suite)
selected := fmt.Sprintf("scope%02d", scopeCount-1)
for _, operation := range []struct {
name string
args []string
}{
{name: "discovery", args: []string{"config", "scopes"}},
{name: "named_selection", args: []string{"config", "validate", "--scope", selected}},
{name: "aggregate_validation", args: []string{"config", "validate", "--scope", "all"}},
} {
b.Run(fmt.Sprintf("%s/scopes_%d", operation.name, scopeCount), func(b *testing.B) {
verifyConfigReport(b, app, operation.args, 1)
streams := Streams{Out: io.Discard, Err: io.Discard}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if outcome := app.Execute(context.Background(), operation.args, streams); outcome.Kind != OutcomeSuccess {
b.Fatalf("configuration operation outcome = %#v", outcome)
}
}
})
}
}
}
func BenchmarkPerformanceConfigRegistration(b *testing.B) {
for _, selectorCount := range []int{4, 64} {
suite := performanceSelectorSuite(selectorCount)
performanceConfigApp(b, suite)
b.Run(fmt.Sprintf("selectors_%d", selectorCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
app, err := New(Identity{Name: "bench"})
if err != nil {
b.Fatal(err)
}
if err := RegisterConfig(app, suite); err != nil {
b.Fatal(err)
}
}
})
}
}
func performanceNativeDocument(targetBytes int, directive bool) []byte {
preamble := "# generated native configuration\n"
if directive {
preamble = "%YAML 1.2\n---\n"
}
var body strings.Builder
body.Grow(targetBytes)
for index := 0; len(preamble)+body.Len() < targetBytes; index++ {
fmt.Fprintf(&body, "key%06d: value\n", index)
}
return []byte(preamble + body.String())
}
type performanceEffectiveWriter []byte
func (document performanceEffectiveWriter) WriteEffective(writer io.Writer) error {
_, err := writer.Write(document)
return err
}
func performanceShowApp(tb testing.TB, documents ...[]byte) *App {
tb.Helper()
scopes := make([]ConfigScope[struct{}], len(documents))
for index, document := range documents {
name := "app"
if index > 0 {
name = fmt.Sprintf("scope%02d", index)
}
writer := performanceEffectiveWriter(document)
scopes[index] = ConfigScope[struct{}]{
Name: name,
Operations: []ConfigOperation{ConfigShow},
Acquire: func(context.Context, ConfigRequest, struct{}) (Acquired[ConfigView], error) {
return Acquired[ConfigView]{Value: ConfigView{Effective: writer}}, nil
},
}
}
suite := ConfigSuite[struct{}]{Scopes: scopes}
return performanceConfigApp(tb, suite)
}
func performanceConfigSuite(scopeCount int, operations []ConfigOperation) ConfigSuite[struct{}] {
scopes := make([]ConfigScope[struct{}], scopeCount)
for index := range scopes {
name := "app"
if index > 0 {
name = fmt.Sprintf("scope%02d", index)
}
scopes[index] = ConfigScope[struct{}]{
Name: name,
Operations: append([]ConfigOperation(nil), operations...),
Acquire: func(context.Context, ConfigRequest, struct{}) (Acquired[ConfigView], error) {
return Acquired[ConfigView]{}, nil
},
}
}
return ConfigSuite[struct{}]{Scopes: scopes}
}
func performanceSelectorSuite(selectorCount int) ConfigSuite[struct{}] {
suite := performanceConfigSuite(1, []ConfigOperation{ConfigValidate})
suite.Selectors = make([]Selector[struct{}], selectorCount)
for index := range suite.Selectors {
flag := ValueFlag(FlagMeta{Name: fmt.Sprintf("selector%02d", index)}, (*string)(nil), parseString, func(*struct{}, Supplied[string]) {})
suite.Selectors[index] = Selector[struct{}]{Binding: flag, Scopes: []string{"app"}}
}
return suite
}
func performanceConfigApp(tb testing.TB, suite ConfigSuite[struct{}]) *App {
tb.Helper()
app, err := New(Identity{Name: "bench"})
if err != nil {
tb.Fatal(err)
}
if err := RegisterConfig(app, suite); err != nil {
tb.Fatal(err)
}
return app
}
func verifyConfigReport(tb testing.TB, app *App, args []string, minimumBytes int) {
tb.Helper()
var output bytes.Buffer
outcome := app.Execute(context.Background(), args, Streams{Out: &output, Err: io.Discard})
if outcome.Kind != OutcomeSuccess || output.Len() < minimumBytes {
tb.Fatalf("configuration report outcome = %#v, output bytes %d, want at least %d", outcome, output.Len(), minimumBytes)
}
}

View File

@@ -1,94 +0,0 @@
package commandkit
import (
"bytes"
"go.yaml.in/yaml/v4"
"io"
"reflect"
"strings"
"testing"
)
func TestEffectiveYAMLFramingPreservesOneNativeDocument(t *testing.T) {
for _, native := range []string{
"--- {setting: value}\n", "---\t{setting: value}\n", "--- value\n",
"--- |\n first\n second\n", "--- # authored\nsetting: value\n",
"setting: value\n", "%YAML 1.1\n--- {setting: value}\n",
} {
t.Run(native, func(t *testing.T) {
var want any
if err := yaml.Unmarshal([]byte(native), &want); err != nil {
t.Fatal(err)
}
framed, _ := frameEffectiveYAML("app", []byte(native))
withoutLabel := bytes.Replace(framed, []byte("# scope: app\n"), nil, 1)
if !strings.HasPrefix(native, "---") && !strings.HasPrefix(native, "%") {
withoutLabel = bytes.TrimPrefix(withoutLabel, []byte("---\n"))
}
if !bytes.Equal(withoutLabel, []byte(native)) {
t.Fatalf("native content changed: %q", framed)
}
if !bytes.Contains(framed, []byte("# scope: app\n")) {
t.Fatalf("missing label: %q", framed)
}
decoder := yaml.NewDecoder(bytes.NewReader(framed))
var got any
if err := decoder.Decode(&got); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("got=%#v want=%#v", got, want)
}
if err := decoder.Decode(new(any)); err != io.EOF {
t.Fatalf("extra document: %v", err)
}
})
}
}
func TestEffectiveYAMLFramingPreservesBoundariesAndDirectives(t *testing.T) {
for _, test := range []struct {
name string
scope string
native string
directive bool
label string
}{
{name: "no trailing newline", scope: "app", native: "value: plain", label: "# scope: app\n"},
{name: "CRLF", scope: "app", native: "# authored\r\n---\r\nvalue: yes\r\n", label: "# scope: app\n"},
{name: "directive preamble", scope: "app", native: "%YAML 1.1\n# authored\n---\nvalue: yes", directive: true, label: "# scope: app\n"},
{name: "control character label", scope: "line\nbreak", native: "value: yes", label: "# scope: \"line\\nbreak\"\n"},
} {
t.Run(test.name, func(t *testing.T) {
var want any
if err := yaml.Unmarshal([]byte(test.native), &want); err != nil {
t.Fatal(err)
}
framed, hasDirective := frameEffectiveYAML(test.scope, []byte(test.native))
if hasDirective != test.directive {
t.Fatalf("hasDirective = %t, want %t", hasDirective, test.directive)
}
withoutLabel := bytes.Replace(framed, []byte(test.label), nil, 1)
if !strings.HasPrefix(test.native, "---") && !strings.HasPrefix(test.native, "%") {
withoutLabel = bytes.TrimPrefix(withoutLabel, []byte("---\n"))
}
if !bytes.Equal(withoutLabel, []byte(test.native)) {
t.Fatalf("native content changed: got %q want %q", withoutLabel, test.native)
}
if !bytes.Contains(framed, []byte(test.label)) {
t.Fatalf("missing scope label %q in %q", test.label, framed)
}
decoder := yaml.NewDecoder(bytes.NewReader(framed))
var first any
if err := decoder.Decode(&first); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(first, want) {
t.Fatalf("decoded value = %#v, want %#v", first, want)
}
if err := decoder.Decode(new(any)); err != io.EOF {
t.Fatalf("unexpected extra YAML document: %v", err)
}
})
}
}

View File

@@ -1,53 +0,0 @@
package ckprompt_test
import (
"context"
"errors"
"fmt"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/promptkit"
"strings"
"testing"
)
func TestDefinitionInspectionCancellationBoundaries(t *testing.T) {
for _, resource := range []string{"prompt", "profile"} {
for _, before := range []bool{true, false} {
t.Run(fmt.Sprintf("%s/before=%t", resource, before), func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if before {
cancel()
}
calls, contexts := 0, 0
diagnostics := []commandkit.Diagnostic{commandkit.MessageDiagnostic(configkit.SeverityWarning, "acquired warning")}
suite := commandkit.PromptKitSuite[struct{}]{NewContext: func() struct{} { contexts++; return struct{}{} },
InspectPrompt: func(context.Context, commandkit.Selection, struct{}, string, string) (commandkit.Acquired[*promptkit.PromptInspection], error) {
calls++
cancel()
return commandkit.Acquired[*promptkit.PromptInspection]{Value: &promptkit.PromptInspection{}, Diagnostics: diagnostics}, nil
},
InspectProfile: func(context.Context, commandkit.Selection, struct{}, string) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
calls++
cancel()
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: &promptkit.ProfileInspection{}, Diagnostics: diagnostics}, nil
},
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
t.Fatal(err)
}
var out, errs bytes.Buffer
result := app.Execute(ctx, []string{"promptkit", "show", resource, "example"}, commandkit.Streams{Out: &out, Err: &errs})
want := 1
if before {
want = 0
}
if result.Kind != commandkit.OutcomeOperational || !errors.Is(result.Cause, context.Canceled) || calls != want || contexts != want || out.Len() != 0 || (!before && !strings.Contains(errs.String(), "acquired warning")) {
t.Fatalf("result=%#v calls=%d contexts=%d out=%q diagnostics=%q", result, calls, contexts, out.String(), errs.String())
}
})
}
}
}

View File

@@ -0,0 +1,110 @@
package ckprompt_test
import (
"bytes"
"context"
"io"
"testing"
"testing/fstest"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckprompt"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
promptkit "gitea.maximumdirect.net/eric/promptkit"
)
type compositionInput struct{ Campaign commandkit.Supplied[string] }
type compositionState struct{ token int }
func TestPublicConfigurationAndDefinitionFamiliesRemainIndependent(t *testing.T) {
inputs, err := promptkit.FilesystemInputs("composition fixture", fstest.MapFS{
"config/prompts.yml": &fstest.MapFile{Data: []byte(`prompts:
briefing:
"1":
messages:
- role: user
content: "Hello"
output:
format: text
validation_mode: none
backends:
local:
endpoint: "https://example.invalid/v1"
profiles:
default:
backend: local
model: example
`)}}, "config")
if err != nil {
t.Fatal(err)
}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{{Inputs: inputs}}}})
if err != nil {
t.Fatalf("construct engine: %v", err)
}
flag := commandkit.ValueFlag(commandkit.FlagMeta{Name: "campaign"}, (*string)(nil), func(value string) (string, error) { return value, nil },
func(input **compositionInput, value commandkit.Supplied[string]) { (*input).Campaign = value })
newInputCalls, newStateCalls := 0, 0
newInput := func() *compositionInput { newInputCalls++; return &compositionInput{} }
newState := func() *compositionState { newStateCalls++; return &compositionState{token: newStateCalls} }
var appInput, engineInput *compositionInput
var appState, configEngineState *compositionState
appAcquisitions, engineScopeAcquisitions, loaderCalls := 0, 0, 0
loadEngine := func(_ context.Context, _ ckconfig.Selection, input *compositionInput, state *compositionState) (*promptkit.Engine, error) {
loaderCalls++
if !input.Campaign.Set || input.Campaign.Value != "spring" {
t.Errorf("engine loader input = %#v", input)
}
if loaderCalls == 1 {
engineInput, configEngineState = input, state
}
return engine, nil
}
appScope := ckconfig.Scope[*compositionInput, *compositionState]{
Name: "app", Operations: []ckconfig.Operation{ckconfig.Validate},
Acquire: func(_ context.Context, _ ckconfig.Request, input *compositionInput, state *compositionState) (commandkit.Acquired[ckconfig.View], error) {
appAcquisitions++
appInput, appState = input, state
return commandkit.Acquired[ckconfig.View]{}, nil
},
}
engineScope := ckprompt.ConfigScope(loadEngine)
acquireEngineScope := engineScope.Acquire
engineScope.Acquire = func(ctx context.Context, request ckconfig.Request, input *compositionInput, state *compositionState) (commandkit.Acquired[ckconfig.View], error) {
engineScopeAcquisitions++
return acquireEngineScope(ctx, request, input, state)
}
configCommand := ckconfig.Command(ckconfig.Suite[*compositionInput, *compositionState]{
NewInput: newInput, NewState: newState,
Selectors: []ckconfig.Selector[*compositionInput]{{Binding: flag, Scopes: []string{"app", "promptkit"}}},
Scopes: []ckconfig.Scope[*compositionInput, *compositionState]{appScope, engineScope},
})
definitions := ckprompt.FromEngine(loadEngine)
definitions.NewInput = newInput
definitions.NewState = newState
definitions.Selectors = []commandkit.Flag[*compositionInput]{flag}
command, err := commandkit.New(commandkit.Identity{Name: "composition"}, configCommand, ckprompt.Command(definitions))
if err != nil {
t.Fatal(err)
}
var output bytes.Buffer
outcome := command.Execute(context.Background(), []string{"config", "validate", "--scope", "all", "--campaign", "spring"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || appAcquisitions != 1 || engineScopeAcquisitions != 1 || loaderCalls != 1 {
t.Fatalf("configuration run = (%#v, output=%q, counts=%d/%d/%d)", outcome, output.String(), appAcquisitions, engineScopeAcquisitions, loaderCalls)
}
if appInput == nil || engineInput == nil || appInput == engineInput || appState == nil || appState != configEngineState || appState.token != 1 {
t.Fatalf("scope values were not isolated/shared as selected: inputs=%p/%p states=%p/%p", appInput, engineInput, appState, configEngineState)
}
output.Reset()
outcome = command.Execute(context.Background(), []string{"promptkit", "show", "profile", "default", "--campaign", "spring", "--format", "yaml"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || appAcquisitions != 1 || engineScopeAcquisitions != 1 || loaderCalls != 2 || !bytes.Contains(output.Bytes(), []byte("profile_id: default")) {
t.Fatalf("independent profile run = (%#v, output=%q, counts=%d/%d/%d)", outcome, output.String(), appAcquisitions, engineScopeAcquisitions, loaderCalls)
}
if newInputCalls != 3 || newStateCalls != 2 {
t.Fatalf("invocation factory calls input/state=%d/%d", newInputCalls, newStateCalls)
}
}

View File

@@ -5,10 +5,13 @@ import (
"errors"
"fmt"
"os"
"strconv"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckprompt"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/promptkit"
promptkit "gitea.maximumdirect.net/eric/promptkit"
)
type campaign struct {
@@ -23,20 +26,17 @@ type appConfiguration struct {
Campaigns map[string]campaign `yaml:"campaigns"`
}
type invocationCache struct {
type inspectionInput struct {
Campaign commandkit.Supplied[string]
Limit commandkit.Supplied[int]
}
// runCache belongs to one invocation and is shared only where the application
// chooses to pass it to its loaders.
type runCache struct {
application *configkit.Result[appConfiguration]
}
type configContext struct {
Cache *invocationCache
Campaign commandkit.Supplied[string]
}
type definitionContext struct {
Cache *invocationCache
Campaign commandkit.Supplied[string]
}
type restoreInput struct {
Campaign commandkit.Supplied[string]
Session string
@@ -48,16 +48,19 @@ type session struct {
Summary string
}
type sessionStore struct {
active string
records map[string]session
func campaignSelector() commandkit.Flag[*inspectionInput] {
return commandkit.ValueFlag(commandkit.FlagMeta{Name: "campaign", Usage: "Select campaign inputs."}, (*string)(nil), parseString,
func(input **inspectionInput, value commandkit.Supplied[string]) { (*input).Campaign = value })
}
func loadApplicationConfig(_ context.Context, _ commandkit.Selection, cache *invocationCache) (*configkit.Result[appConfiguration], error) {
func loadApplicationConfig(ctx context.Context, _ ckconfig.Selection, _ *inspectionInput, cache *runCache) (*configkit.Result[appConfiguration], error) {
if err := ctx.Err(); err != nil {
return nil, err
}
if cache != nil && cache.application != nil {
return cache.application, nil
}
result, err := configkit.Load[appConfiguration](configkit.Mapping("composed app config", map[string]any{
result, err := configkit.Load[appConfiguration](configkit.Mapping("composed application settings", map[string]any{
"default_campaign": "spring",
"campaigns": map[string]any{
"spring": map[string]any{"prompt_id": "briefing", "prompt_version": "1", "profile_id": "spring", "model": "spring-model"},
@@ -70,24 +73,12 @@ func loadApplicationConfig(_ context.Context, _ commandkit.Selection, cache *inv
return result, err
}
func selectedCampaign(configuration appConfiguration, supplied commandkit.Supplied[string]) (string, campaign, error) {
name := configuration.DefaultCampaign
if supplied.Set {
name = supplied.Value
}
value, ok := configuration.Campaigns[name]
if !ok {
return "", campaign{}, fmt.Errorf("unknown campaign %q", name)
}
return name, value, nil
}
func loadPromptEngine(ctx context.Context, selection commandkit.Selection, cache *invocationCache, selected commandkit.Supplied[string]) (*promptkit.Engine, error) {
application, err := loadApplicationConfig(ctx, selection, cache)
func loadPromptEngine(ctx context.Context, selection ckconfig.Selection, input *inspectionInput, cache *runCache) (*promptkit.Engine, error) {
application, err := loadApplicationConfig(ctx, selection, input, cache)
if err != nil {
return nil, err
}
name, chosen, err := selectedCampaign(application.Config, selected)
name, chosen, err := selectedCampaign(application.Config, input.Campaign)
if err != nil {
return nil, err
}
@@ -111,104 +102,93 @@ func loadPromptEngine(ctx context.Context, selection commandkit.Selection, cache
return promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{Source: engineConfig}}})
}
func campaignSelector[C any](set func(*C, commandkit.Supplied[string])) commandkit.Selector[C] {
return commandkit.Selector[C]{
Binding: commandkit.ValueFlag[C](commandkit.FlagMeta{Name: "campaign", Usage: "Select a configured campaign."}, nil, func(value string) (string, error) { return value, nil }, set),
Scopes: []string{"app", "promptkit"},
func selectedCampaign(configuration appConfiguration, supplied commandkit.Supplied[string]) (string, campaign, error) {
name := configuration.DefaultCampaign
if supplied.Set {
name = supplied.Value
}
chosen, ok := configuration.Campaigns[name]
if !ok {
return "", campaign{}, fmt.Errorf("unknown campaign %q", name)
}
return name, chosen, nil
}
func newApp() (*commandkit.App, error) {
app, err := commandkit.New(commandkit.Identity{Name: "composed-example", Version: "1.0"})
if err != nil {
return nil, err
}
appScope, err := commandkit.NewConfigKitScope[configContext, appConfiguration]("app", "Application campaigns.", func(ctx context.Context, selection commandkit.Selection, input configContext) (*configkit.Result[appConfiguration], error) {
return loadApplicationConfig(ctx, selection, input.Cache)
})
if err != nil {
return nil, err
}
promptScope, err := commandkit.NewPromptKitScope(func(ctx context.Context, selection commandkit.Selection, input configContext) (*promptkit.Engine, error) {
return loadPromptEngine(ctx, selection, input.Cache, input.Campaign)
})
if err != nil {
return nil, err
}
configSuite := commandkit.ConfigSuite[configContext]{
NewContext: func() configContext { return configContext{Cache: &invocationCache{}} },
Selectors: []commandkit.Selector[configContext]{campaignSelector(func(input *configContext, value commandkit.Supplied[string]) {
input.Campaign = value
})},
Scopes: []commandkit.ConfigScope[configContext]{appScope, promptScope},
}
if err := commandkit.RegisterConfig(app, configSuite); err != nil {
return nil, err
}
limitDefault := 0
limitFlag := commandkit.ValueFlag(commandkit.FlagMeta{Name: "limit", Usage: "Override the application item limit."}, &limitDefault, strconv.Atoi,
func(input **inspectionInput, value commandkit.Supplied[int]) { (*input).Limit = value })
inputFactory := func() *inspectionInput { return &inspectionInput{} }
stateFactory := func() *runCache { return &runCache{} }
definitionSuite, err := commandkit.NewPromptKitSuite(func(ctx context.Context, selection commandkit.Selection, input definitionContext) (*promptkit.Engine, error) {
return loadPromptEngine(ctx, selection, input.Cache, input.Campaign)
appScope := ckconfig.FromConfigKit("app", "Application settings.", loadApplicationConfig)
engineScope := ckprompt.ConfigScope(loadPromptEngine)
configCommand := ckconfig.Command(ckconfig.Suite[*inspectionInput, *runCache]{
NewInput: inputFactory,
NewState: stateFactory,
CheckInput: func(scope string, _ ckconfig.Selection, input *inspectionInput) error {
if scope == "app" && input.Campaign.Set && !input.Limit.Set {
return errors.New("application campaign inspection requires --limit")
}
return nil
},
Selectors: []ckconfig.Selector[*inspectionInput]{
{Binding: campaignSelector(), Scopes: []string{"app", "promptkit"}},
{Binding: limitFlag, Scopes: []string{"app"}},
},
Scopes: []ckconfig.Scope[*inspectionInput, *runCache]{appScope, engineScope},
})
if err != nil {
return nil, err
}
definitionSuite.NewContext = func() definitionContext { return definitionContext{Cache: &invocationCache{}} }
definitionSuite.Selectors = []commandkit.Selector[definitionContext]{campaignSelector(func(input *definitionContext, value commandkit.Supplied[string]) {
input.Campaign = value
})}
if err := commandkit.RegisterPromptKit(app, definitionSuite); err != nil {
return nil, err
}
store := &sessionStore{records: map[string]session{"session-42": {ID: "session-42", Campaign: "spring", Summary: "A synthetic planning session."}}}
restore, err := commandkit.NewCommand(commandkit.CommandSpec[restoreInput]{
definitions := ckprompt.FromEngine(loadPromptEngine)
definitions.NewInput = inputFactory
definitions.NewState = stateFactory
definitions.Selectors = []commandkit.Flag[*inspectionInput]{campaignSelector()}
definitionCommand := ckprompt.Command(definitions)
store := map[string]session{
"session-42": {ID: "session-42", Campaign: "spring", Summary: "A synthetic planning session."},
}
restore := commandkit.NewCommand(commandkit.CommandSpec[restoreInput]{
Name: "restore",
Summary: "Restore one saved session.",
Flags: []commandkit.Flag[restoreInput]{commandkit.ValueFlag[restoreInput](commandkit.FlagMeta{Name: "campaign", Usage: "Campaign owning the session."}, nil, func(value string) (string, error) { return value, nil }, func(input *restoreInput, value commandkit.Supplied[string]) {
input.Campaign = value
})},
Arguments: []commandkit.Argument[restoreInput]{commandkit.ValueArgument[restoreInput](commandkit.ArgumentMeta{Name: "ID"}, true, func(value string) (string, error) { return value, nil }, func(input *restoreInput, value commandkit.Supplied[string]) {
input.Session = value.Value
})},
Summary: "Restore a saved session.",
Flags: []commandkit.Flag[restoreInput]{commandkit.ValueFlag(commandkit.FlagMeta{Name: "campaign", Usage: "Campaign owning the session."}, (*string)(nil), parseString,
func(input *restoreInput, value commandkit.Supplied[string]) { input.Campaign = value })},
Arguments: []commandkit.Argument[restoreInput]{commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "ID", Usage: "Session identifier."}, true, parseString,
func(input *restoreInput, value commandkit.Supplied[string]) { input.Session = value.Value })},
Run: func(invocation commandkit.Invocation, input restoreInput) commandkit.Outcome {
return restoreSession(invocation, store, input)
if err := invocation.Context.Err(); err != nil {
return commandkit.Failure(err)
}
application, err := loadApplicationConfig(invocation.Context, ckconfig.Selection{}, nil, nil)
if err != nil {
return commandkit.Failure(err)
}
name, _, err := selectedCampaign(application.Config, input.Campaign)
if err != nil {
return commandkit.Failure(err)
}
record, ok := store[input.Session]
if !ok {
return commandkit.Outcome{Kind: commandkit.OutcomeDomain, Status: 4, Domain: "session_not_found", Cause: errors.New("saved session was not found")}
}
if record.Campaign != name {
return commandkit.Outcome{Kind: commandkit.OutcomeDomain, Status: 3, Domain: "campaign_mismatch", Cause: fmt.Errorf("session belongs to campaign %q", record.Campaign)}
}
if _, err := fmt.Fprintf(invocation.Streams.Out, "restored %s: %s\n", record.ID, record.Summary); err != nil {
return commandkit.Failure(err)
}
return commandkit.Success()
},
})
if err != nil {
return nil, err
}
sessionGroup, err := commandkit.Group("session", "Manage saved sessions.", restore)
if err != nil {
return nil, err
}
if err := app.Register(sessionGroup); err != nil {
return nil, err
}
return app, nil
return commandkit.New(
commandkit.Identity{Name: "composed-example", Version: "1.0"},
configCommand,
definitionCommand,
commandkit.Group("session", "Manage saved sessions.", restore),
)
}
func restoreSession(invocation commandkit.Invocation, store *sessionStore, input restoreInput) commandkit.Outcome {
application, err := loadApplicationConfig(invocation.Context, commandkit.Selection{}, nil)
if err != nil {
return commandkit.Failure(err)
}
name, _, err := selectedCampaign(application.Config, input.Campaign)
if err != nil {
return commandkit.Failure(err)
}
record, ok := store.records[input.Session]
if !ok {
return commandkit.Outcome{Kind: commandkit.OutcomeDomain, Status: 4, Domain: "session_not_found", Cause: errors.New("saved session was not found")}
}
if record.Campaign != name {
return commandkit.Outcome{Kind: commandkit.OutcomeDomain, Status: 3, Domain: "campaign_mismatch", Cause: fmt.Errorf("session belongs to campaign %q", record.Campaign)}
}
store.active = record.ID
if _, err := fmt.Fprintf(invocation.Streams.Out, "restored %s: %s\n", record.ID, record.Summary); err != nil {
return commandkit.Failure(err)
}
return commandkit.Success()
}
func parseString(raw string) (string, error) { return raw, nil }
func main() {
app, err := newApp()

View File

@@ -1,161 +0,0 @@
package ckprompt
import (
"bytes"
"context"
"fmt"
"io"
"strings"
"testing"
promptkit "gitea.maximumdirect.net/eric/promptkit"
)
func BenchmarkPerformancePromptReports(b *testing.B) {
for _, inputCount := range []int{4, 128} {
inspection := performancePromptInspection(inputCount)
app := performancePromptApp(b, inspection, performanceProfileInspection(nil))
for _, format := range []string{"text", "yaml"} {
name := fmt.Sprintf("prompt/inputs_%d/%s", inputCount, format)
args := []string{"promptkit", "show", "prompt", "briefing", "--format", format}
b.Run(name, func(b *testing.B) {
verifyPromptReport(b, app, args, format, "prompt_id")
streams := Streams{Out: io.Discard, Err: io.Discard}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if outcome := app.Execute(context.Background(), args, streams); outcome.Kind != OutcomeSuccess {
b.Fatalf("prompt report outcome = %#v", outcome)
}
}
})
}
}
for _, fixture := range []struct {
name string
extra map[string]any
}{
{name: "small", extra: performanceExtraParams(2, 3)},
{name: "large", extra: performanceExtraParams(24, 8)},
} {
profile := performanceProfileInspection(fixture.extra)
app := performancePromptApp(b, performancePromptInspection(4), profile)
for _, format := range []string{"text", "yaml"} {
name := fmt.Sprintf("profile/extra_%s/%s", fixture.name, format)
args := []string{"promptkit", "show", "profile", "baseline", "--format", format}
b.Run(name, func(b *testing.B) {
verifyPromptReport(b, app, args, format, "profile_id")
streams := Streams{Out: io.Discard, Err: io.Discard}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if outcome := app.Execute(context.Background(), args, streams); outcome.Kind != OutcomeSuccess {
b.Fatalf("profile report outcome = %#v", outcome)
}
}
})
}
}
}
func performancePromptInspection(inputCount int) *promptkit.PromptInspection {
inputs := make([]promptkit.PromptInputDefinition, inputCount)
for index := range inputs {
inputs[index] = promptkit.PromptInputDefinition{
Name: fmt.Sprintf("input%03d", index),
Required: index%2 == 0,
ContentType: "text/plain",
Description: "A representative prompt input.",
}
}
return &promptkit.PromptInspection{
PromptID: "briefing",
PromptVersion: "1",
PromptHash: "fixed-hash",
DefaultProfileID: "baseline",
Inputs: inputs,
OutputContract: promptkit.OutputContract{
Format: promptkit.FormatText,
ValidationMode: promptkit.ValidationBasic,
RepairAttempts: 1,
},
}
}
func performanceProfileInspection(extra map[string]any) *promptkit.ProfileInspection {
return &promptkit.ProfileInspection{
ProfileID: "baseline",
EffectiveModelParams: promptkit.ExecutionTarget{
BackendID: "benchmark-backend",
Endpoint: "https://example.invalid/v1",
Model: "benchmark-model",
Temperature: 0.5,
MaxTokens: 512,
ExtraParams: extra,
},
}
}
func performanceExtraParams(keys, width int) map[string]any {
values := make(map[string]any, keys)
for keyIndex := 0; keyIndex < keys; keyIndex++ {
nested := make(map[string]any, width)
for valueIndex := 0; valueIndex < width; valueIndex++ {
nested[fmt.Sprintf("value%02d", valueIndex)] = fmt.Sprintf("sample-%02d-%02d", keyIndex, valueIndex)
}
values[fmt.Sprintf("group%02d", keyIndex)] = map[string]any{
"enabled": keyIndex%2 == 0,
"nested": nested,
"sequence": []any{
"first",
keyIndex,
map[string]any{"leaf": true},
},
}
}
return values
}
func performancePromptApp(tb testing.TB, prompt *promptkit.PromptInspection, profile *promptkit.ProfileInspection) *App {
tb.Helper()
app, err := New(Identity{Name: "bench"})
if err != nil {
tb.Fatal(err)
}
suite := PromptKitSuite[struct{}]{
InspectPrompt: func(context.Context, Selection, struct{}, string, string) (Acquired[*promptkit.PromptInspection], error) {
return Acquired[*promptkit.PromptInspection]{Value: prompt}, nil
},
InspectProfile: func(context.Context, Selection, struct{}, string) (Acquired[*promptkit.ProfileInspection], error) {
return Acquired[*promptkit.ProfileInspection]{Value: profile}, nil
},
}
if err := RegisterPromptKit(app, suite); err != nil {
tb.Fatal(err)
}
return app
}
func verifyPromptReport(tb testing.TB, app *App, args []string, format, marker string) {
tb.Helper()
var output bytes.Buffer
outcome := app.Execute(context.Background(), args, Streams{Out: &output, Err: io.Discard})
wantMarker := marker
if format == "text" {
if marker == "prompt_id" {
wantMarker = "Prompt ID:"
} else {
wantMarker = "Profile ID:"
}
}
if outcome.Kind != OutcomeSuccess || !strings.Contains(output.String(), wantMarker) {
tb.Fatalf("%s report outcome = %#v, output length %d", format, outcome, output.Len())
}
if format == "yaml" && !strings.Contains(output.String(), ":") {
tb.Fatalf("YAML report lacks mapping entries: %q", output.String())
}
if format == "text" && !strings.Contains(output.String(), " ID:") {
tb.Fatalf("text report has unexpected shape: %q", output.String()[:min(output.Len(), 80)])
}
}

View File

@@ -1,478 +0,0 @@
package ckprompt
import (
"bytes"
"context"
"errors"
"fmt"
"strconv"
"strings"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/promptkit"
)
// PromptKitSuite declares optional PromptKit prompt and profile inspection
// commands for an App.
type PromptKitSuite[C any] struct {
// NewContext creates command-local application context when an inspection runs.
NewContext func() C
// Selectors declares context flags accepted by the installed commands. Each
// explicit scope list must include "promptkit"; an empty list applies here.
Selectors []Selector[C]
// CheckInput validates standard configuration selections and selector values.
CheckInput func(Selection, C) error
// InspectPrompt inspects an ID and exact or omitted version.
InspectPrompt func(context.Context, Selection, C, string, string) (Acquired[*promptkit.PromptInspection], error)
// InspectProfile inspects one profile ID.
InspectProfile func(context.Context, Selection, C, string) (Acquired[*promptkit.ProfileInspection], error)
}
// NewPromptKitSuite creates prompt and profile inspection callbacks backed by
// a PromptKit Engine loader. The loader runs once per selected command. Each
// callback captures configuration diagnostics before calling the corresponding
// PromptKit inspection method, and returns those diagnostics with lookup errors.
func NewPromptKitSuite[C any](
load func(context.Context, Selection, C) (*promptkit.Engine, error),
) (PromptKitSuite[C], error) {
if load == nil {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit engine loader is nil")
}
return PromptKitSuite[C]{
InspectPrompt: func(ctx context.Context, selection Selection, appContext C, id, version string) (Acquired[*promptkit.PromptInspection], error) {
engine, _, diagnostics, err := loadPromptKitInspection(ctx, selection, appContext, load)
if err != nil {
return Acquired[*promptkit.PromptInspection]{Diagnostics: diagnostics}, err
}
inspection, err := engine.InspectPrompt(ctx, id, version)
return Acquired[*promptkit.PromptInspection]{Value: inspection, Diagnostics: diagnostics}, err
},
InspectProfile: func(ctx context.Context, selection Selection, appContext C, id string) (Acquired[*promptkit.ProfileInspection], error) {
engine, _, diagnostics, err := loadPromptKitInspection(ctx, selection, appContext, load)
if err != nil {
return Acquired[*promptkit.ProfileInspection]{Diagnostics: diagnostics}, err
}
inspection, err := engine.InspectProfile(ctx, id)
return Acquired[*promptkit.ProfileInspection]{Value: inspection, Diagnostics: diagnostics}, err
},
}, nil
}
func loadPromptKitInspection[C any](ctx context.Context, selection Selection, appContext C, load func(context.Context, Selection, C) (*promptkit.Engine, error)) (*promptkit.Engine, promptkit.ConfigurationInspection, []Diagnostic, error) {
engine, err := load(ctx, selection, appContext)
if err != nil {
var loadErr *configkit.LoadError
if errors.As(err, &loadErr) {
return nil, promptkit.ConfigurationInspection{}, ConfigKitDiagnostics("promptkit", loadErr.Sources(), loadErr.Diagnostics()), err
}
return nil, promptkit.ConfigurationInspection{}, nil, err
}
if engine == nil {
return nil, promptkit.ConfigurationInspection{}, nil, fmt.Errorf("PromptKit configuration loader returned a nil engine")
}
if ctx != nil {
if err := ctx.Err(); err != nil {
return nil, promptkit.ConfigurationInspection{}, nil, err
}
}
inspection, err := engine.InspectConfiguration()
if err != nil {
return nil, promptkit.ConfigurationInspection{}, nil, err
}
sources := inspection.Sources()
diagnostics := ConfigKitDiagnostics("promptkit", sources, inspection.Diagnostics())
return engine, inspection, diagnostics, nil
}
// RegisterPromptKit installs the supplied PromptKit inspection commands under
// promptkit show. Each nonnil callback installs only its matching command. A
// suite with no inspection callbacks installs no PromptKit namespace.
func RegisterPromptKit[C any](app *App, suite PromptKitSuite[C]) error {
if suite.InspectPrompt == nil && suite.InspectProfile == nil {
return nil
}
if app == nil {
return fmt.Errorf("cannot register PromptKit commands on a nil app")
}
normalized, err := normalizePromptKitSuite(suite)
if err != nil {
return err
}
showCommands := make([]Command, 0, 2)
if normalized.InspectPrompt != nil {
command, err := newPromptKitPromptCommand(normalized)
if err != nil {
return err
}
showCommands = append(showCommands, command)
}
if normalized.InspectProfile != nil {
command, err := newPromptKitProfileCommand(normalized)
if err != nil {
return err
}
showCommands = append(showCommands, command)
}
show, err := Group("show", "Inspect PromptKit definitions.", showCommands...)
if err != nil {
return err
}
root, err := Group("promptkit", "Inspect PromptKit definitions.", show)
if err != nil {
return err
}
return app.Register(root)
}
func normalizePromptKitSuite[C any](suite PromptKitSuite[C]) (PromptKitSuite[C], error) {
normalized := suite
normalized.Selectors = append([]Selector[C](nil), suite.Selectors...)
standardNames := map[string]bool{"help": true}
for _, flag := range promptKitInputFlags(PromptKitSuite[C]{}, suite.InspectPrompt != nil) {
standardNames[flag.binding.meta.Name] = true
}
seenNames := make(map[string]struct{}, len(normalized.Selectors))
seenShorts := make(map[string]struct{}, len(normalized.Selectors))
for i, selector := range normalized.Selectors {
binding := selector.Binding.binding
meta := binding.meta
if !binding.valid || binding.clear == nil || meta.Name == "" {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector %d has an invalid flag binding", i)
}
if !validFlagName(meta.Name) {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector flag name %q is invalid", meta.Name)
}
if standardNames[meta.Name] {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector flag --%s conflicts with a standard flag", meta.Name)
}
if meta.Short == "h" {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector short flag -%s conflicts with help", meta.Short)
}
if meta.Short != "" && (len(meta.Short) != 1 || meta.Short == "-") {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector short spelling %q must be one byte", meta.Short)
}
if _, exists := seenNames[meta.Name]; exists {
return PromptKitSuite[C]{}, fmt.Errorf("duplicate PromptKit selector flag --%s", meta.Name)
}
seenNames[meta.Name] = struct{}{}
if meta.Short != "" {
if _, exists := seenShorts[meta.Short]; exists {
return PromptKitSuite[C]{}, fmt.Errorf("duplicate PromptKit selector short flag -%s", meta.Short)
}
seenShorts[meta.Short] = struct{}{}
}
applies := len(selector.Scopes) == 0
for _, scope := range selector.Scopes {
if scope == "promptkit" {
applies = true
break
}
}
if !applies {
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector --%s must apply to scope %q", meta.Name, "promptkit")
}
normalized.Selectors[i].Scopes = append([]string(nil), selector.Scopes...)
}
return normalized, nil
}
type promptKitInput[C any] struct {
Selection Selection
Format string
ID string
Version string
SelectorContext C
selectors []selectorState[C]
}
func promptKitInputFlags[C any](suite PromptKitSuite[C], prompt bool) []Flag[promptKitInput[C]] {
formatDefault := "text"
flags := []Flag[promptKitInput[C]]{
ValueFlag[promptKitInput[C]](FlagMeta{Name: "format", Usage: "Output format."}, &formatDefault, parseString, func(input *promptKitInput[C], value Supplied[string]) {
input.Format = value.Value
}),
}
flags = append(flags, configurationSelectionFlags(
func(input *promptKitInput[C], value Supplied[string]) { input.Selection.Config = value },
func(input *promptKitInput[C], value Supplied[string]) { input.Selection.Overlay = value },
)...)
if prompt {
versionDefault := ""
flags = append(flags, ValueFlag[promptKitInput[C]](FlagMeta{Name: "version", Usage: "Exact prompt version; empty selects the owning default."}, &versionDefault, parseString, func(input *promptKitInput[C], value Supplied[string]) {
input.Version = value.Value
}))
}
for _, selector := range suite.Selectors {
flags = append(flags, adaptContextSelector(selector,
func(input *promptKitInput[C]) *C { return &input.SelectorContext },
func(input *promptKitInput[C], state selectorState[C]) {
input.selectors = append(input.selectors, state)
},
))
}
return flags
}
func checkPromptKitInput[C any](suite PromptKitSuite[C], input promptKitInput[C]) error {
if _, err := parseConfigFormat(input.Format); err != nil {
return err
}
if suite.CheckInput != nil {
return suite.CheckInput(input.Selection, input.SelectorContext)
}
return nil
}
func promptKitContext[C any](suite PromptKitSuite[C], input promptKitInput[C]) C {
var result C
if suite.NewContext != nil {
result = suite.NewContext()
}
for _, selector := range input.selectors {
if selectorApplies(selector.scopes, "promptkit") {
selector.apply(&result)
} else {
selector.clear(&result)
}
}
return result
}
func newPromptKitPromptCommand[C any](suite PromptKitSuite[C]) (Command, error) {
inputSpec := CommandSpec[promptKitInput[C]]{
Name: "prompt",
Summary: "Inspect one PromptKit prompt definition.",
Flags: promptKitInputFlags(suite, true),
Arguments: []Argument[promptKitInput[C]]{ValueArgument[promptKitInput[C]](ArgumentMeta{Name: "ID"}, true, parseString, func(input *promptKitInput[C], value Supplied[string]) {
input.ID = value.Value
})},
CheckInput: func(input promptKitInput[C]) error { return checkPromptKitInput(suite, input) },
Run: func(invocation Invocation, input promptKitInput[C]) Outcome {
return runPromptKitPrompt(invocation, suite, input)
},
}
return NewCommand(inputSpec)
}
func newPromptKitProfileCommand[C any](suite PromptKitSuite[C]) (Command, error) {
inputSpec := CommandSpec[promptKitInput[C]]{
Name: "profile",
Summary: "Inspect one PromptKit profile definition.",
Flags: promptKitInputFlags(suite, false),
Arguments: []Argument[promptKitInput[C]]{ValueArgument[promptKitInput[C]](ArgumentMeta{Name: "ID"}, true, parseString, func(input *promptKitInput[C], value Supplied[string]) {
input.ID = value.Value
})},
CheckInput: func(input promptKitInput[C]) error { return checkPromptKitInput(suite, input) },
Run: func(invocation Invocation, input promptKitInput[C]) Outcome {
return runPromptKitProfile(invocation, suite, input)
},
}
return NewCommand(inputSpec)
}
func runPromptKitPrompt[C any](invocation Invocation, suite PromptKitSuite[C], input promptKitInput[C]) Outcome {
if err := invocation.Context.Err(); err != nil {
return Failure(err)
}
contextValue := promptKitContext(suite, input)
if err := invocation.Context.Err(); err != nil {
return Failure(err)
}
acquired, err := suite.InspectPrompt(invocation.Context, input.Selection, contextValue, input.ID, input.Version)
if err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
if err := invocation.Context.Err(); err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
if acquired.Value == nil {
return promptKitFailure(fmt.Errorf("PromptKit prompt inspection returned no result"), acquired.Diagnostics)
}
report := promptReportFromInspection(acquired.Value)
var output []byte
if input.Format == "yaml" {
var buffer bytes.Buffer
if err := encodeYAML(&buffer, report); err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
output = buffer.Bytes()
} else {
output = []byte(formatPromptText(report))
}
return writePromptKitReport(invocation, output, acquired.Diagnostics)
}
func runPromptKitProfile[C any](invocation Invocation, suite PromptKitSuite[C], input promptKitInput[C]) Outcome {
if err := invocation.Context.Err(); err != nil {
return Failure(err)
}
contextValue := promptKitContext(suite, input)
if err := invocation.Context.Err(); err != nil {
return Failure(err)
}
acquired, err := suite.InspectProfile(invocation.Context, input.Selection, contextValue, input.ID)
if err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
if err := invocation.Context.Err(); err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
if acquired.Value == nil {
return promptKitFailure(fmt.Errorf("PromptKit profile inspection returned no result"), acquired.Diagnostics)
}
report := profileReportFromInspection(acquired.Value)
var output []byte
if input.Format == "yaml" {
var buffer bytes.Buffer
if err := encodeYAML(&buffer, report); err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
output = buffer.Bytes()
} else {
textReport, err := formatProfileText(report)
if err != nil {
return promptKitFailure(err, acquired.Diagnostics)
}
output = []byte(textReport)
}
return writePromptKitReport(invocation, output, acquired.Diagnostics)
}
func promptKitFailure(err error, diagnostics []Diagnostic) Outcome {
outcome := Failure(err)
outcome.Diagnostics = diagnostics
return outcome
}
func writePromptKitReport(invocation Invocation, output []byte, diagnostics []Diagnostic) Outcome {
if err := invocation.Context.Err(); err != nil {
return promptKitFailure(err, diagnostics)
}
if err := writeReport(invocation.Streams.Out, output); err != nil {
outcome := incompleteReport(err)
outcome.Diagnostics = diagnostics
return outcome
}
outcome := Success()
outcome.Diagnostics = diagnostics
return outcome
}
type promptInputReport struct {
Name string `yaml:"name"`
Required bool `yaml:"required"`
ContentType string `yaml:"content_type"`
Description string `yaml:"description"`
}
type promptOutputContractReport struct {
Format string `yaml:"format"`
ValidationMode string `yaml:"validation_mode"`
SchemaPath string `yaml:"schema_path"`
RepairAttempts int `yaml:"repair_attempts"`
}
type promptInspectionReport struct {
PromptID string `yaml:"prompt_id"`
PromptVersion string `yaml:"prompt_version"`
PromptHash string `yaml:"prompt_hash"`
DefaultProfileID string `yaml:"default_profile_id"`
Inputs []promptInputReport `yaml:"inputs"`
OutputContract promptOutputContractReport `yaml:"output_contract"`
}
func promptReportFromInspection(inspection *promptkit.PromptInspection) promptInspectionReport {
report := promptInspectionReport{
PromptID: inspection.PromptID,
PromptVersion: inspection.PromptVersion,
PromptHash: inspection.PromptHash,
DefaultProfileID: inspection.DefaultProfileID,
Inputs: make([]promptInputReport, 0, len(inspection.Inputs)),
OutputContract: promptOutputContractReport{
Format: string(inspection.OutputContract.Format),
ValidationMode: string(inspection.OutputContract.ValidationMode),
SchemaPath: inspection.OutputContract.SchemaPath,
RepairAttempts: inspection.OutputContract.RepairAttempts,
},
}
for _, input := range inspection.Inputs {
report.Inputs = append(report.Inputs, promptInputReport{Name: input.Name, Required: input.Required, ContentType: input.ContentType, Description: input.Description})
}
return report
}
type modelParamsReport struct {
BackendID string `yaml:"backend_id"`
Endpoint string `yaml:"endpoint"`
Model string `yaml:"model"`
Temperature float64 `yaml:"temperature"`
MaxTokens int `yaml:"max_tokens"`
TopP float64 `yaml:"top_p"`
TimeoutSeconds int `yaml:"timeout_seconds"`
ServiceTier string `yaml:"service_tier"`
ReasoningEffort string `yaml:"reasoning_effort"`
APIKeyEnv string `yaml:"api_key_env"`
ExtraParams map[string]any `yaml:"extra_params"`
}
type profileInspectionReport struct {
ProfileID string `yaml:"profile_id"`
EffectiveModelParams modelParamsReport `yaml:"effective_model_params"`
APIKeyRequired bool `yaml:"api_key_required"`
}
func profileReportFromInspection(inspection *promptkit.ProfileInspection) profileInspectionReport {
params := inspection.EffectiveModelParams
extra := params.ExtraParams
if extra == nil {
extra = map[string]any{}
}
return profileInspectionReport{
ProfileID: inspection.ProfileID,
EffectiveModelParams: modelParamsReport{
BackendID: params.BackendID, Endpoint: params.Endpoint, Model: params.Model,
Temperature: params.Temperature, MaxTokens: params.MaxTokens, TopP: params.TopP,
TimeoutSeconds: params.TimeoutSeconds, ServiceTier: params.ServiceTier,
ReasoningEffort: params.ReasoningEffort, APIKeyEnv: params.APIKeyEnv, ExtraParams: extra,
},
APIKeyRequired: inspection.APIKeyRequired,
}
}
func formatPromptText(report promptInspectionReport) string {
var output strings.Builder
fmt.Fprintf(&output, "Prompt ID: %s\n", strconv.Quote(report.PromptID))
fmt.Fprintf(&output, "Prompt version: %s\n", strconv.Quote(report.PromptVersion))
fmt.Fprintf(&output, "Prompt hash: %s\n", strconv.Quote(report.PromptHash))
fmt.Fprintf(&output, "Default profile ID: %s\n", strconv.Quote(report.DefaultProfileID))
output.WriteString("Inputs:\n")
for _, input := range report.Inputs {
fmt.Fprintf(&output, " - name: %s\n required: %t\n content_type: %s\n description: %s\n", strconv.Quote(input.Name), input.Required, strconv.Quote(input.ContentType), strconv.Quote(input.Description))
}
if len(report.Inputs) == 0 {
output.WriteString(" []\n")
}
fmt.Fprintf(&output, "Output contract:\n format: %s\n validation_mode: %s\n schema_path: %s\n repair_attempts: %d\n", strconv.Quote(report.OutputContract.Format), strconv.Quote(report.OutputContract.ValidationMode), strconv.Quote(report.OutputContract.SchemaPath), report.OutputContract.RepairAttempts)
return output.String()
}
func formatProfileText(report profileInspectionReport) (string, error) {
params := report.EffectiveModelParams
var output strings.Builder
fmt.Fprintf(&output, "Profile ID: %s\n", strconv.Quote(report.ProfileID))
output.WriteString("Effective model params:\n")
fmt.Fprintf(&output, " backend_id: %s\n endpoint: %s\n model: %s\n temperature: %s\n max_tokens: %d\n top_p: %s\n timeout_seconds: %d\n service_tier: %s\n reasoning_effort: %s\n api_key_env: %s\n", strconv.Quote(params.BackendID), strconv.Quote(params.Endpoint), strconv.Quote(params.Model), strconv.FormatFloat(params.Temperature, 'g', -1, 64), params.MaxTokens, strconv.FormatFloat(params.TopP, 'g', -1, 64), params.TimeoutSeconds, strconv.Quote(params.ServiceTier), strconv.Quote(params.ReasoningEffort), strconv.Quote(params.APIKeyEnv))
output.WriteString(" extra_params:\n")
var nested strings.Builder
if err := encodeYAML(&nested, params.ExtraParams); err != nil {
return "", err
}
for _, line := range strings.Split(strings.TrimSuffix(nested.String(), "\n"), "\n") {
output.WriteString(" ")
output.WriteString(line)
output.WriteByte('\n')
}
fmt.Fprintf(&output, "API key required: %t\n", report.APIKeyRequired)
return output.String(), nil
}

View File

@@ -1,220 +0,0 @@
package ckprompt_test
import (
"bytes"
"context"
"errors"
"io"
"strings"
"testing"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/promptkit"
)
func TestPromptKitScopeUsesCapturedInspectionAndSelectedInputs(t *testing.T) {
type appContext struct{ marker string }
type contextKey struct{}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
Main: &configkit.Input{Source: configkit.Mapping("local fixture", map[string]any{
"backends": map[string]any{"local": map[string]any{"endpoint": "https://example.invalid/v1"}},
"profiles": map[string]any{"base.v1": map[string]any{"backend": "local", "model": "fixture-model"}},
})},
}})
if err != nil {
t.Fatalf("construct PromptKit engine: %v", err)
}
inspection, err := engine.InspectConfiguration()
if err != nil {
t.Fatalf("inspect PromptKit engine: %v", err)
}
ctx := context.WithValue(context.Background(), contextKey{}, "caller-context")
selection := commandkit.Selection{Config: commandkit.Supplied[string]{Value: "main.yml", Set: true}, Overlay: commandkit.Supplied[string]{Value: "dev", Set: true}}
loaderCalls := 0
var receivedSelections []commandkit.Selection
scope, err := commandkit.NewPromptKitScope(func(gotContext context.Context, gotSelection commandkit.Selection, gotAppContext appContext) (*promptkit.Engine, error) {
loaderCalls++
receivedSelections = append(receivedSelections, gotSelection)
if gotContext != ctx || gotContext.Value(contextKey{}) != "caller-context" {
t.Fatal("PromptKit loader did not receive the caller context")
}
if gotAppContext.marker != "app-state" {
t.Fatalf("loader app context = %+v", gotAppContext)
}
return engine, nil
})
if err != nil {
t.Fatal(err)
}
if scope.Name != "promptkit" || len(scope.Operations) != 4 {
t.Fatalf("scope declaration = %+v", scope)
}
acquired, err := scope.Acquire(ctx, commandkit.ConfigRequest{Operation: commandkit.ConfigShow, Selection: selection}, appContext{marker: "app-state"})
if err != nil {
t.Fatalf("acquire PromptKit scope: %v", err)
}
var gotEffective, wantEffective bytes.Buffer
if err := acquired.Value.Effective.WriteEffective(&gotEffective); err != nil {
t.Fatal(err)
}
if err := inspection.WriteEffective(&wantEffective); err != nil || gotEffective.String() != wantEffective.String() {
t.Fatalf("native effective output = (%q, %v), want %q", gotEffective.String(), err, wantEffective.String())
}
if len(acquired.Value.Sources.Sources()) != len(inspection.Sources()) {
t.Fatalf("source inventory = %#v, want %#v", acquired.Value.Sources.Sources(), inspection.Sources())
}
path := configkit.Keys("profiles", "base.v1", "model")
var gotExplanation, wantExplanation bytes.Buffer
if err := acquired.Value.Explanation.WriteExplanation(&gotExplanation, path); err != nil {
t.Fatal(err)
}
if err := inspection.WriteExplanation(&wantExplanation, path); err != nil || gotExplanation.String() != wantExplanation.String() {
t.Fatalf("native explanation = (%q, %v), want %q", gotExplanation.String(), err, wantExplanation.String())
}
if loaderCalls != 1 {
t.Fatalf("loader called %d times, want once", loaderCalls)
}
if len(receivedSelections) != 1 || receivedSelections[0] != selection {
t.Fatalf("loader selection = %#v, want %#v", receivedSelections, selection)
}
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[appContext]{
NewContext: func() appContext { return appContext{marker: "app-state"} },
Scopes: []commandkit.ConfigScope[appContext]{scope},
}); err != nil {
t.Fatal(err)
}
var show bytes.Buffer
outcome := app.Execute(ctx, []string{"config", "show", "--scope", "promptkit", "--config", "main.yml", "--overlay", "dev"}, commandkit.Streams{Out: &show, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || show.String() != wantEffective.String() {
t.Fatalf("config show = (%#v, %q), want native output %q", outcome, show.String(), wantEffective.String())
}
var sources bytes.Buffer
outcome = app.Execute(ctx, []string{"config", "sources", "--scope", "promptkit", "--format", "yaml"}, commandkit.Streams{Out: &sources, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(sources.String(), "label: local fixture") {
t.Fatalf("config sources = (%#v, %q), want captured PromptKit source", outcome, sources.String())
}
var explanation bytes.Buffer
outcome = app.Execute(ctx, []string{"config", "explain", "--scope", "promptkit", "--path", path.String()}, commandkit.Streams{Out: &explanation, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || explanation.String() != wantExplanation.String() {
t.Fatalf("config explain = (%#v, %q), want native output %q", outcome, explanation.String(), wantExplanation.String())
}
callsBeforeDiscovery := loaderCalls
outcome = app.Execute(ctx, []string{"config", "scopes"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || loaderCalls != callsBeforeDiscovery {
t.Fatalf("metadata discovery outcome=%#v loader calls=%d", outcome, loaderCalls)
}
}
func TestPromptKitScopeAggregateOrderAndConfigurationOnlyRegistration(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{})
if err != nil {
t.Fatal(err)
}
scope, err := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
return engine, nil
})
if err != nil {
t.Fatal(err)
}
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{
{Name: "app", Operations: []commandkit.ConfigOperation{commandkit.ConfigShow}, Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
return commandkit.Acquired[commandkit.ConfigView]{Value: commandkit.ConfigView{Effective: effectiveYAML("app: true\n")}}, nil
}}, scope,
}}); err != nil {
t.Fatal(err)
}
var output bytes.Buffer
outcome := app.Execute(context.Background(), []string{"config", "show", "--scope", "all"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("aggregate show outcome = %#v", outcome)
}
appIndex, promptkitIndex := strings.Index(output.String(), "# scope: app"), strings.Index(output.String(), "# scope: promptkit")
if appIndex < 0 || promptkitIndex <= appIndex {
t.Fatalf("aggregate order = %q", output.String())
}
var scopes bytes.Buffer
outcome = app.Execute(context.Background(), []string{"config", "scopes"}, commandkit.Streams{Out: &scopes, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(scopes.String(), "promptkit: validate, show, sources, explain") {
t.Fatalf("configuration scope discovery = (%#v, %q)", outcome, scopes.String())
}
outcome = app.Execute(context.Background(), []string{"help", "promptkit"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage {
t.Fatalf("configuration-only install exposed promptkit definition commands: %#v", outcome)
}
}
func TestPromptKitScopeErrorsPreserveIdentityAndRejectNilInputs(t *testing.T) {
if _, err := commandkit.NewPromptKitScope[struct{}](nil); err == nil {
t.Fatal("nil loader was accepted")
}
var nilEngine *promptkit.Engine
if _, err := nilEngine.InspectConfiguration(); !errors.Is(err, promptkit.ErrInvalidConfig) {
t.Fatalf("PromptKit nil engine identity = %v", err)
}
loadErr := errors.Join(promptkit.ErrInvalidConfig, errors.New("loader context"))
scope, err := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
return nil, loadErr
})
if err != nil {
t.Fatal(err)
}
_, err = scope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{})
if err != loadErr || !errors.Is(err, promptkit.ErrInvalidConfig) {
t.Fatalf("loader error = %v, want original error identity %v", err, loadErr)
}
nilScope, err := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
return nil, nil
})
if err != nil {
t.Fatal(err)
}
if _, err := nilScope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{}); err == nil || !strings.Contains(err.Error(), "nil engine") {
t.Fatalf("nil engine acquisition error = %v", err)
}
negativeRetries := -1
invalidEngine, err := promptkit.NewEngine(promptkit.Config{TransientRetries: &negativeRetries})
if invalidEngine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
t.Fatalf("invalid PromptKit construction = (%v, %v)", invalidEngine, err)
}
invalidEngine, err = promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("invalid inheritance", map[string]any{
"profiles": map[string]any{"child": map[string]any{"extends": "missing"}},
}),
}}})
if invalidEngine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
t.Fatalf("invalid PromptKit profile construction = (%v, %v)", invalidEngine, err)
}
var configLoadErr *configkit.LoadError
if !errors.As(err, &configLoadErr) {
t.Fatalf("invalid PromptKit construction did not preserve ConfigKit LoadError: %v", err)
}
failedScope, scopeErr := commandkit.NewPromptKitScope(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
return nil, err
})
if scopeErr != nil {
t.Fatal(scopeErr)
}
failed, scopeErr := failedScope.Acquire(context.Background(), commandkit.ConfigRequest{}, struct{}{})
var acquiredLoadErr *configkit.LoadError
if scopeErr != err || !errors.As(scopeErr, &acquiredLoadErr) || acquiredLoadErr != configLoadErr || len(failed.Diagnostics) == 0 {
t.Fatalf("scope load error = (%v, %+v), want original LoadError and diagnostics", scopeErr, failed)
}
}

View File

@@ -1,450 +0,0 @@
package ckprompt_test
import (
"bytes"
"context"
"errors"
"io"
"reflect"
"strings"
"testing"
"testing/fstest"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/promptkit"
"go.yaml.in/yaml/v4"
)
const promptKitInspectionFixture = `prompts:
"team/summary":
"2":
default_profile: child
inputs:
- name: topic
required: true
content_type: text/plain
description: "Topic to summarize"
- name: audience
required: false
content_type: text/plain
description: ""
messages:
- role: user
content: "Summarize {{.topic}}"
output:
format: json
validation_mode: basic
repair_attempts: 2
"3":
messages:
- role: user
content: "Version three"
output:
format: text
validation_mode: none
empty:
"1":
messages:
- role: user
content: "No inputs"
output:
format: text
validation_mode: none
`
func promptKitInspectionEngine(t *testing.T) *promptkit.Engine {
t.Helper()
inputs, err := promptkit.FilesystemInputs("prompt fixture", fstest.MapFS{
"config/prompts.yml": &fstest.MapFile{Data: []byte(promptKitInspectionFixture)},
}, "config")
if err != nil {
t.Fatal(err)
}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
Underlays: []configkit.Underlay{
{Inputs: inputs},
{Inputs: []configkit.Input{{Source: configkit.Mapping("profile fixture", map[string]any{
"backends": map[string]any{"local": map[string]any{"endpoint": "https://example.invalid/v1"}},
"profiles": map[string]any{
"parent": map[string]any{
"backend": "local", "model": "base-model", "temperature": 0.7,
"max_tokens": 100, "top_p": 0.8,
"extra_params": map[string]any{
"token": "provider-private-sentinel",
"nested": map[string]any{"null_value": nil, "visible": "kept"},
"sequence": []any{"first", nil, 3},
},
},
"child": map[string]any{"extends": "parent", "temperature": 0, "max_tokens": 0, "api_key_env": "PROMPTKIT_TEST_KEY"},
"requires-key": map[string]any{"backend": "local", "model": "key-model", "api_key_required": true},
"no-extra": map[string]any{"backend": "local", "model": "empty-params"},
},
})}}},
},
}})
if err != nil {
t.Fatalf("construct PromptKit fixture engine: %v", err)
}
return engine
}
func TestPromptKitInspectionCommandsReportRealCapturedDefinitions(t *testing.T) {
t.Setenv("PROMPTKIT_TEST_KEY", "credential-private-sentinel")
engine := promptKitInspectionEngine(t)
loadCalls := 0
suite, err := commandkit.NewPromptKitSuite(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
loadCalls++
return engine, nil
})
if err != nil {
t.Fatal(err)
}
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
t.Fatal(err)
}
var promptYAML bytes.Buffer
outcome := app.Execute(context.Background(), []string{"promptkit", "show", "prompt", "team/summary", "--version", "2", "--format", "yaml"}, commandkit.Streams{Out: &promptYAML, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || loadCalls != 1 {
t.Fatalf("prompt inspection outcome=%#v calls=%d report=%q", outcome, loadCalls, promptYAML.String())
}
var promptReport map[string]any
if err := yaml.Unmarshal(promptYAML.Bytes(), &promptReport); err != nil {
t.Fatalf("decode prompt YAML: %v", err)
}
if !reflect.DeepEqual(sortedKeys(promptReport), []string{"default_profile_id", "inputs", "output_contract", "prompt_hash", "prompt_id", "prompt_version"}) {
t.Fatalf("prompt report keys = %v", sortedKeys(promptReport))
}
if promptReport["prompt_id"] != "team/summary" || promptReport["prompt_version"] != "2" || promptReport["default_profile_id"] != "child" || promptReport["prompt_hash"] == "" {
t.Fatalf("prompt report identity = %#v", promptReport)
}
inputs := promptReport["inputs"].([]any)
if len(inputs) != 2 || inputs[0].(map[string]any)["name"] != "topic" || inputs[1].(map[string]any)["name"] != "audience" {
t.Fatalf("prompt inputs/order = %#v", inputs)
}
outputContract := promptReport["output_contract"].(map[string]any)
if outputContract["format"] != "json" || outputContract["validation_mode"] != "basic" || outputContract["repair_attempts"] != 2 {
t.Fatalf("output contract = %#v", outputContract)
}
var promptText bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "prompt", "team/summary", "--version", "2"}, commandkit.Streams{Out: &promptText, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(promptText.String(), `Prompt hash: "`) || !strings.Contains(promptText.String(), `- name: "topic"`) || !strings.Contains(promptText.String(), `validation_mode: "basic"`) {
t.Fatalf("prompt text = (%#v, %q)", outcome, promptText.String())
}
var omittedVersion bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "prompt", "empty", "--format", "yaml"}, commandkit.Streams{Out: &omittedVersion, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(omittedVersion.String(), "prompt_version: \"1\"") {
t.Fatalf("omitted singleton prompt version = (%#v, %q)", outcome, omittedVersion.String())
}
var profileYAML bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "child", "--format", "yaml"}, commandkit.Streams{Out: &profileYAML, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("profile inspection outcome=%#v report=%q", outcome, profileYAML.String())
}
var profileReport map[string]any
if err := yaml.Unmarshal(profileYAML.Bytes(), &profileReport); err != nil {
t.Fatalf("decode profile YAML: %v", err)
}
if !reflect.DeepEqual(sortedKeys(profileReport), []string{"api_key_required", "effective_model_params", "profile_id"}) || profileReport["profile_id"] != "child" || profileReport["api_key_required"] != false {
t.Fatalf("profile report = %#v", profileReport)
}
params := profileReport["effective_model_params"].(map[string]any)
if !reflect.DeepEqual(sortedKeys(params), []string{"api_key_env", "backend_id", "endpoint", "extra_params", "max_tokens", "model", "reasoning_effort", "service_tier", "temperature", "timeout_seconds", "top_p"}) {
t.Fatalf("model parameter keys = %v", sortedKeys(params))
}
if params["temperature"] != 0 || params["max_tokens"] != 0 || params["api_key_env"] != "PROMPTKIT_TEST_KEY" {
t.Fatalf("effective owning values = %#v", params)
}
extra := params["extra_params"].(map[string]any)
nested := extra["nested"].(map[string]any)
sequence := extra["sequence"].([]any)
if nested["null_value"] != nil || nested["visible"] != "kept" || len(sequence) != 3 || sequence[1] != nil || sequence[2] != 3 {
t.Fatalf("nested provider parameters changed: %#v", extra)
}
if extra["token"] != "provider-private-sentinel" || strings.Contains(profileYAML.String(), "credential-private-sentinel") {
t.Fatalf("provider parameters or credential reference changed: %q", profileYAML.String())
}
var profileText bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "child"}, commandkit.Streams{Out: &profileText, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(profileText.String(), "temperature: 0\n") || !strings.Contains(profileText.String(), "api_key_env: \"PROMPTKIT_TEST_KEY\"") || !strings.Contains(profileText.String(), "null_value: null") {
t.Fatalf("profile text = (%#v, %q)", outcome, profileText.String())
}
var requiredKey bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "requires-key", "--format", "yaml"}, commandkit.Streams{Out: &requiredKey, Err: io.Discard})
var requiredReport map[string]any
if outcome.Kind != commandkit.OutcomeSuccess || yaml.Unmarshal(requiredKey.Bytes(), &requiredReport) != nil || requiredReport["api_key_required"] != true {
t.Fatalf("credential requirement report = (%#v, %q)", outcome, requiredKey.String())
}
var emptyPrompt bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "prompt", "empty", "--version", "1", "--format", "yaml"}, commandkit.Streams{Out: &emptyPrompt, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(emptyPrompt.String(), "inputs: []") {
t.Fatalf("empty inputs report = (%#v, %q)", outcome, emptyPrompt.String())
}
var zeroExtra bytes.Buffer
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "no-extra", "--format", "yaml"}, commandkit.Streams{Out: &zeroExtra, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || !strings.Contains(zeroExtra.String(), "extra_params: {}") {
t.Fatalf("nil extra params report = (%#v, %q)", outcome, zeroExtra.String())
}
}
func TestPromptKitInspectionSelectionErrorsHelpAndOptionalCapabilities(t *testing.T) {
engine := promptKitInspectionEngine(t)
loaderCalls := 0
suite, err := commandkit.NewPromptKitSuite(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
loaderCalls++
return engine, nil
})
if err != nil {
t.Fatal(err)
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
t.Fatal(err)
}
badTarget := suite
badTarget.Selectors = []commandkit.Selector[struct{}]{{
Binding: commandkit.ValueFlag[struct{}](commandkit.FlagMeta{Name: "campaign"}, nil, func(value string) (string, error) { return value, nil }, func(*struct{}, commandkit.Supplied[string]) {}),
Scopes: []string{"app"},
}}
invalidApp, _ := commandkit.New(commandkit.Identity{Name: "invalid"})
if err := commandkit.RegisterPromptKit(invalidApp, badTarget); err == nil {
t.Fatal("selector for another scope was accepted")
}
for _, tc := range []struct {
args []string
want error
}{
{args: []string{"promptkit", "show", "prompt", "team/summary"}, want: promptkit.ErrPromptLoad},
{args: []string{"promptkit", "show", "prompt", "missing", "--version", "1"}, want: promptkit.ErrPromptNotFound},
{args: []string{"promptkit", "show", "profile", "missing"}, want: promptkit.ErrProfileNotFound},
{args: []string{"promptkit", "show", "prompt", "", "--version", "2"}, want: promptkit.ErrInvalidRequest},
} {
outcome := app.Execute(context.Background(), tc.args, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, tc.want) {
t.Errorf("execute %q = (%#v), want operational error matching %v", tc.args, outcome, tc.want)
}
}
cancelled, cancel := context.WithCancel(context.Background())
cancel()
outcome := app.Execute(cancelled, []string{"promptkit", "show", "prompt", "empty", "--version", "1"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) {
t.Fatalf("cancelled prompt inspection = %#v", outcome)
}
beforeHelp := loaderCalls
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "prompt", "--help"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || loaderCalls != beforeHelp {
t.Fatalf("help acquired engine: outcome=%#v calls=%d", outcome, loaderCalls)
}
beforeMissing := loaderCalls
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "prompt"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage || loaderCalls != beforeMissing {
t.Fatalf("missing ID = (%#v, calls=%d), want usage before acquisition", outcome, loaderCalls)
}
beforeScopeFlag := loaderCalls
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "child", "--scope", "promptkit"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage || loaderCalls != beforeScopeFlag {
t.Fatalf("definition command accepted --scope: outcome=%#v calls=%d", outcome, loaderCalls)
}
profileOnlyApp, _ := commandkit.New(commandkit.Identity{Name: "profile-only"})
if err := commandkit.RegisterPromptKit(profileOnlyApp, commandkit.PromptKitSuite[struct{}]{InspectProfile: func(ctx context.Context, _ commandkit.Selection, _ struct{}, id string) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
value, err := engine.InspectProfile(ctx, id)
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: value}, err
}}); err != nil {
t.Fatal(err)
}
outcome = profileOnlyApp.Execute(context.Background(), []string{"promptkit", "show", "profile", "child"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("profile-only capability was not installed: %#v", outcome)
}
outcome = profileOnlyApp.Execute(context.Background(), []string{"promptkit", "show", "prompt", "empty"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage {
t.Fatalf("profile-only suite installed prompt inspection: %#v", outcome)
}
promptOnlyApp, _ := commandkit.New(commandkit.Identity{Name: "prompt-only"})
if err := commandkit.RegisterPromptKit(promptOnlyApp, commandkit.PromptKitSuite[struct{}]{InspectPrompt: func(ctx context.Context, _ commandkit.Selection, _ struct{}, id, version string) (commandkit.Acquired[*promptkit.PromptInspection], error) {
value, err := engine.InspectPrompt(ctx, id, version)
return commandkit.Acquired[*promptkit.PromptInspection]{Value: value}, err
}}); err != nil {
t.Fatal(err)
}
outcome = promptOnlyApp.Execute(context.Background(), []string{"promptkit", "show", "prompt", "empty"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("prompt-only capability was not installed: %#v", outcome)
}
outcome = promptOnlyApp.Execute(context.Background(), []string{"promptkit", "show", "profile", "child"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage {
t.Fatalf("prompt-only suite installed profile inspection: %#v", outcome)
}
emptyApp, _ := commandkit.New(commandkit.Identity{Name: "empty"})
if err := commandkit.RegisterPromptKit(emptyApp, commandkit.PromptKitSuite[struct{}]{}); err != nil {
t.Fatalf("empty PromptKit suite = %v", err)
}
outcome = emptyApp.Execute(context.Background(), []string{"help"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("empty PromptKit suite changed command family: %#v", outcome)
}
outcome = emptyApp.Execute(context.Background(), []string{"help", "promptkit"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeUsage {
t.Fatalf("empty PromptKit suite installed its namespace: %#v", outcome)
}
}
func TestPromptKitSuitePassesSelectionAndProjectedContext(t *testing.T) {
type appContext struct {
Campaign commandkit.Supplied[string]
Runtime string
}
type contextKey struct{}
engine := promptKitInspectionEngine(t)
ctx := context.WithValue(context.Background(), contextKey{}, "caller")
wantSelection := commandkit.Selection{
Config: commandkit.Supplied[string]{Value: "main.yml", Set: true},
Overlay: commandkit.Supplied[string]{Value: "dev", Set: true},
}
loaderCalls, contexts, checks := 0, 0, 0
var gotSelection commandkit.Selection
var gotContext appContext
suite, err := commandkit.NewPromptKitSuite(func(gotCtx context.Context, selection commandkit.Selection, input appContext) (*promptkit.Engine, error) {
loaderCalls++
gotSelection, gotContext = selection, input
if gotCtx != ctx || gotCtx.Value(contextKey{}) != "caller" {
t.Fatal("engine loader did not receive invocation context")
}
return engine, nil
})
if err != nil {
t.Fatal(err)
}
suite.NewContext = func() appContext { contexts++; return appContext{Runtime: "invocation cache"} }
suite.Selectors = []commandkit.Selector[appContext]{{
Binding: commandkit.ValueFlag[appContext](commandkit.FlagMeta{Name: "campaign"}, nil, func(value string) (string, error) { return value, nil }, func(input *appContext, value commandkit.Supplied[string]) { input.Campaign = value }),
Scopes: []string{"promptkit"},
}}
suite.CheckInput = func(selection commandkit.Selection, input appContext) error {
checks++
if selection != wantSelection || input.Campaign != (commandkit.Supplied[string]{Value: "spring", Set: true}) {
t.Errorf("preflight inputs = (%+v, %+v)", selection, input)
}
return nil
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
t.Fatal(err)
}
outcome := app.Execute(ctx, []string{"promptkit", "show", "profile", "child", "--config", "main.yml", "--overlay", "dev", "--campaign", "spring"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || loaderCalls != 1 || contexts != 1 || checks != 1 {
t.Fatalf("inspection = (%#v), loader/context/check calls = %d/%d/%d", outcome, loaderCalls, contexts, checks)
}
if gotSelection != wantSelection || gotContext.Campaign != (commandkit.Supplied[string]{Value: "spring", Set: true}) || gotContext.Runtime != "invocation cache" {
t.Fatalf("loader selection/context = (%+v, %+v)", gotSelection, gotContext)
}
}
func TestPromptKitDirectSuiteRetainsDiagnosticsOnNamedLookupFailure(t *testing.T) {
warning := commandkit.Diagnostic{Severity: configkit.SeverityWarning, Write: func(w io.Writer) error { _, err := io.WriteString(w, "configuration warning\n"); return err }}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
err := commandkit.RegisterPromptKit(app, commandkit.PromptKitSuite[struct{}]{InspectPrompt: func(context.Context, commandkit.Selection, struct{}, string, string) (commandkit.Acquired[*promptkit.PromptInspection], error) {
return commandkit.Acquired[*promptkit.PromptInspection]{Diagnostics: []commandkit.Diagnostic{warning}}, promptkit.ErrPromptNotFound
}})
if err != nil {
t.Fatal(err)
}
outcome := app.Execute(context.Background(), []string{"promptkit", "show", "prompt", "missing"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, promptkit.ErrPromptNotFound) || len(outcome.Diagnostics) < 1 || outcome.Diagnostics[0].Severity != configkit.SeverityWarning {
t.Fatalf("named lookup outcome did not retain acquisition diagnostic: %#v", outcome)
}
}
func TestPromptKitInspectionReportsOutputDeliveryFailure(t *testing.T) {
engine := promptKitInspectionEngine(t)
suite, err := commandkit.NewPromptKitSuite(func(context.Context, commandkit.Selection, struct{}) (*promptkit.Engine, error) {
return engine, nil
})
if err != nil {
t.Fatal(err)
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
t.Fatal(err)
}
writeErr := errors.New("report output failed")
outcome := app.Execute(context.Background(), []string{"promptkit", "show", "profile", "child", "--format", "yaml"}, commandkit.Streams{Out: promptKitFailingWriter{err: writeErr}, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeOperational || !outcome.ReportIncomplete || !errors.Is(outcome.DeliveryError, writeErr) {
t.Fatalf("promptkit output failure = %#v", outcome)
}
}
type promptKitFailingWriter struct{ err error }
func (writer promptKitFailingWriter) Write([]byte) (int, error) { return 0, writer.err }
func sortedKeys(values map[string]any) []string {
keys := make([]string, 0, len(values))
for key := range values {
keys = append(keys, key)
}
for i := 0; i < len(keys); i++ {
for j := i + 1; j < len(keys); j++ {
if keys[j] < keys[i] {
keys[i], keys[j] = keys[j], keys[i]
}
}
}
return keys
}
func TestPromptKitSelectorNamesCollideOnlyWithInstalledFlags(t *testing.T) {
for _, name := range []string{"version", "scope"} {
var captured commandkit.Supplied[string]
suite := commandkit.PromptKitSuite[commandkit.Supplied[string]]{
Selectors: []commandkit.Selector[commandkit.Supplied[string]]{{Binding: commandkit.ValueFlag[commandkit.Supplied[string]](commandkit.FlagMeta{Name: name}, nil, func(s string) (string, error) { return s, nil }, func(c *commandkit.Supplied[string], v commandkit.Supplied[string]) { *c = v })}},
InspectProfile: func(_ context.Context, _ commandkit.Selection, c commandkit.Supplied[string], _ string) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
captured = c
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: &promptkit.ProfileInspection{}}, nil
},
}
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
t.Fatalf("%s: %v", name, err)
}
result := app.Execute(context.Background(), []string{"promptkit", "show", "profile", "example", "--" + name, "selected"}, commandkit.Streams{})
if result.Kind != commandkit.OutcomeSuccess || captured != (commandkit.Supplied[string]{Value: "selected", Set: true}) {
t.Fatalf("%s: result=%#v captured=%#v", name, result, captured)
}
suite.InspectPrompt = func(context.Context, commandkit.Selection, commandkit.Supplied[string], string, string) (commandkit.Acquired[*promptkit.PromptInspection], error) {
return commandkit.Acquired[*promptkit.PromptInspection]{Value: &promptkit.PromptInspection{}}, nil
}
other, _ := commandkit.New(commandkit.Identity{Name: "example"})
err := commandkit.RegisterPromptKit(other, suite)
if name == "version" && err == nil {
t.Fatal("accepted collision with installed prompt version flag")
}
if name == "scope" && err != nil {
t.Fatalf("scope selector conflicts with no installed flag: %v", err)
}
}
for _, name := range []string{"config", "overlay", "format", "help"} {
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
suite := commandkit.PromptKitSuite[struct{}]{
InspectProfile: func(context.Context, commandkit.Selection, struct{}, string) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
return commandkit.Acquired[*promptkit.ProfileInspection]{}, nil
},
Selectors: []commandkit.Selector[struct{}]{{Binding: commandkit.ValueFlag[struct{}](commandkit.FlagMeta{Name: name}, nil, func(s string) (string, error) { return s, nil }, func(*struct{}, commandkit.Supplied[string]) {})}},
}
if err := commandkit.RegisterPromptKit(app, suite); err == nil {
t.Fatalf("accepted standard flag collision --%s", name)
}
}
}

View File

@@ -1,85 +0,0 @@
package ckprompt_test
import (
"bytes"
"context"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/promptkit"
"io"
"strings"
"testing"
)
func TestBridgeTreeRefreshesAfterSuccessfulRegistration(t *testing.T) {
app, err := commandkit.New(commandkit.Identity{Name: "example"})
if err != nil {
t.Fatal(err)
}
var output bytes.Buffer
streams := commandkit.Streams{Out: &output, Err: io.Discard}
renderRootHelp := func() string {
t.Helper()
output.Reset()
outcome := app.Execute(context.Background(), []string{"--help"}, streams)
if outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("root help outcome = %#v", outcome)
}
return output.String()
}
initial := renderRootHelp()
if strings.Contains(initial, "custom") || strings.Contains(initial, "config") || strings.Contains(initial, "promptkit") {
t.Fatalf("initial help unexpectedly lists future registrations: %q", initial)
}
custom, err := NewCommand(CommandSpec[struct{}]{Name: "custom", Run: func(Invocation, struct{}) Outcome { return commandkit.Success() }})
if err != nil {
t.Fatal(err)
}
if err := app.Register(custom); err != nil {
t.Fatal(err)
}
providerCalls := 0
if err := commandkit.RegisterConfig(app, commandkit.ConfigSuite[struct{}]{Scopes: []commandkit.ConfigScope[struct{}]{{
Name: "app",
Operations: []ConfigOperation{commandkit.ConfigShow},
Acquire: func(context.Context, commandkit.ConfigRequest, struct{}) (commandkit.Acquired[commandkit.ConfigView], error) {
providerCalls++
return commandkit.Acquired[commandkit.ConfigView]{}, nil
},
}}}); err != nil {
t.Fatal(err)
}
if err := commandkit.RegisterPromptKit(app, commandkit.PromptKitSuite[struct{}]{InspectPrompt: func(context.Context, commandkit.Selection, struct{}, string, string) (commandkit.Acquired[*promptkit.PromptInspection], error) {
providerCalls++
return commandkit.Acquired[*promptkit.PromptInspection]{}, nil
}}); err != nil {
t.Fatal(err)
}
updated := renderRootHelp()
for _, name := range []string{"custom", "config", "promptkit"} {
if !strings.Contains(updated, name) {
t.Errorf("updated help omits %q: %q", name, updated)
}
}
if providerCalls != 0 {
t.Fatalf("help invoked inspection or configuration callbacks %d times", providerCalls)
}
late, err := NewCommand(CommandSpec[struct{}]{Name: "late"})
if err != nil {
t.Fatal(err)
}
if err := app.Register(late, custom); err == nil {
t.Fatal("Register accepted a batch with an existing-name collision")
}
if err := app.Register(); err != nil {
t.Fatalf("empty registration failed: %v", err)
}
final := renderRootHelp()
if !strings.Contains(final, "custom") || !strings.Contains(final, "config") || !strings.Contains(final, "promptkit") || strings.Contains(final, "late") {
t.Fatalf("help after failed and empty registration = %q", final)
}
if outcome := app.Execute(context.Background(), []string{"custom"}, streams); outcome.Kind != commandkit.OutcomeSuccess {
t.Fatalf("new command did not parse after registration: %#v", outcome)
}
}