2026-11-07 04:44:01 +00:00
|
|
|
package ckconfig
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"io"
|
2026-11-07 12:14:11 +00:00
|
|
|
"reflect"
|
2026-11-07 04:44:01 +00:00
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
commandkit "go.maximumdirect.net/commandkit"
|
|
|
|
|
configkit "go.maximumdirect.net/configkit"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestExplainDiscoveryModesAndMetadata(t *testing.T) {
|
|
|
|
|
loads, inputs, states := 0, 0, 0
|
|
|
|
|
scope := FromConfigKit("app", "", func(_ context.Context, selection Selection, _, _ struct{}) (*configkit.Result[map[string]any], error) {
|
|
|
|
|
loads++
|
2026-11-07 12:14:11 +00:00
|
|
|
return configkit.Load(configkit.Mapping("fixture", map[string]any{"a/b": map[string]any{"x": 1}, "a~b": 4, "ab": 2, "abacus": 3, "gone": map[string]any{"x": 1}}), configkit.Options[map[string]any]{Overrides: selection.Overrides})
|
2026-11-07 04:44:01 +00:00
|
|
|
})
|
|
|
|
|
scope.Features.Overrides = true
|
|
|
|
|
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{NewInput: func() struct{} { inputs++; return struct{}{} }, NewState: func() struct{} { states++; return struct{}{} }, Scopes: []Scope[struct{}, struct{}]{scope}}))
|
|
|
|
|
run := func(args ...string) (commandkit.Outcome, string) {
|
|
|
|
|
var out bytes.Buffer
|
|
|
|
|
result := app.Execute(context.Background(), append([]string{"config", "explain"}, args...), commandkit.Streams{Out: &out, Err: io.Discard})
|
|
|
|
|
return result, out.String()
|
|
|
|
|
}
|
|
|
|
|
result, out := run()
|
|
|
|
|
if result.Kind != commandkit.OutcomeSuccess || !strings.Contains(out, "--list") || !strings.Contains(out, "--path") || loads != 0 || inputs != 0 || states != 0 {
|
|
|
|
|
t.Fatalf("bare help: %#v %q, %d/%d/%d", result, out, loads, inputs, states)
|
|
|
|
|
}
|
|
|
|
|
for _, args := range [][]string{{"--list", "--path=/ab"}, {"--historical"}, {"--under=/ab"}, {"--explicit=false"}, {"--path=/ab", "--under=/ab"}, {"--list", "--under=bad"}} {
|
|
|
|
|
result, _ := run(args...)
|
|
|
|
|
if result.Kind != commandkit.OutcomeUsage || loads != 0 || inputs != 0 || states != 0 {
|
|
|
|
|
t.Fatalf("invalid %v: %#v, %d/%d/%d", args, result, loads, inputs, states)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result, out = run("--list", "--under=/a~1b", "--format=json")
|
|
|
|
|
var report pathDiscoveryReport
|
2026-11-07 12:14:11 +00:00
|
|
|
decode := func(output string) error {
|
|
|
|
|
report = pathDiscoveryReport{}
|
|
|
|
|
return json.Unmarshal([]byte(output), &report)
|
|
|
|
|
}
|
|
|
|
|
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil || len(report.Paths) != 2 {
|
2026-11-07 04:44:01 +00:00
|
|
|
t.Fatalf("escaped subtree: %#v %q", result, out)
|
|
|
|
|
}
|
|
|
|
|
seen := map[string]bool{}
|
|
|
|
|
for _, entry := range report.Paths {
|
|
|
|
|
seen[entry.Path] = true
|
|
|
|
|
}
|
|
|
|
|
if !seen["/a~1b"] || !seen["/a~1b/x"] {
|
|
|
|
|
t.Fatalf("escaped paths: %#v", report.Paths)
|
|
|
|
|
}
|
|
|
|
|
result, out = run("--list", "--under=/ab", "--format=json")
|
2026-11-07 12:14:11 +00:00
|
|
|
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil || len(report.Paths) != 1 || report.Paths[0].Path != "/ab" {
|
2026-11-07 04:44:01 +00:00
|
|
|
t.Fatalf("token boundary: %#v %q", result, out)
|
|
|
|
|
}
|
2026-11-07 12:14:11 +00:00
|
|
|
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
|
prefix string
|
|
|
|
|
paths []string
|
|
|
|
|
}{
|
|
|
|
|
{"/a~1b/x", []string{"/a~1b/x"}},
|
|
|
|
|
{"/a~0b", []string{"/a~0b"}},
|
|
|
|
|
{"/a", nil},
|
|
|
|
|
{"/a~1b/x/unknown", nil},
|
|
|
|
|
{"/ab/" + strings.Repeat("too-deep/", 20) + "unknown", nil},
|
|
|
|
|
} {
|
|
|
|
|
result, out = run("--list", "--under="+tc.prefix, "--format=json")
|
|
|
|
|
if result.Status != 0 || decode(out) != nil {
|
|
|
|
|
t.Fatalf("prefix %q: %#v %s", tc.prefix, result, out)
|
|
|
|
|
}
|
|
|
|
|
var paths []string
|
|
|
|
|
for _, entry := range report.Paths {
|
|
|
|
|
paths = append(paths, entry.Path)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(paths, tc.paths) {
|
|
|
|
|
t.Fatalf("prefix %q: paths=%v want=%v", tc.prefix, paths, tc.paths)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result, baseline := run("--list", "--format=json")
|
|
|
|
|
if result.Status != 0 {
|
|
|
|
|
t.Fatalf("unfiltered discovery=%#v", result)
|
|
|
|
|
}
|
|
|
|
|
var all pathDiscoveryReport
|
|
|
|
|
if err := json.Unmarshal([]byte(baseline), &all); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
result, out = run("--list", "--under=", "--format=json")
|
|
|
|
|
if result.Status != 0 || decode(out) != nil || !reflect.DeepEqual(report, all) {
|
|
|
|
|
t.Fatalf("root subtree changed discovery: %#v %s", result, out)
|
|
|
|
|
}
|
2026-11-07 04:44:01 +00:00
|
|
|
result, out = run("--list", "--historical", "--set=/gone=null", "--format=json")
|
2026-11-07 12:14:11 +00:00
|
|
|
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil {
|
2026-11-07 04:44:01 +00:00
|
|
|
t.Fatalf("historical: %#v %q", result, out)
|
|
|
|
|
}
|
|
|
|
|
removed := false
|
|
|
|
|
for _, entry := range report.Paths {
|
|
|
|
|
if entry.Path == "/gone/x" && !entry.Present && entry.Metadata == nil {
|
|
|
|
|
removed = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !removed {
|
|
|
|
|
t.Fatalf("missing historical entry: %q", out)
|
|
|
|
|
}
|
2026-11-07 12:14:11 +00:00
|
|
|
result, out = run("--list", "--under=/gone", "--historical", "--explicit=false", "--set=/gone=null", "--format=json")
|
|
|
|
|
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil {
|
2026-11-07 04:44:01 +00:00
|
|
|
t.Fatalf("explicit filter: %#v %q", result, out)
|
|
|
|
|
}
|
|
|
|
|
for _, entry := range report.Paths {
|
|
|
|
|
if !entry.Present || entry.Metadata == nil || entry.Metadata.Explicit {
|
|
|
|
|
t.Fatalf("invalid filtered entry: %#v", entry)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result, out = run("--path=", "--format=json")
|
|
|
|
|
if result.Kind != commandkit.OutcomeSuccess || !strings.Contains(out, `"path": ""`) || !strings.Contains(out, `"metadata"`) {
|
|
|
|
|
t.Fatalf("root explanation: %#v %q", result, out)
|
|
|
|
|
}
|
|
|
|
|
result, out = run("--path=/gone/x", "--set=/gone=null", "--format=json")
|
|
|
|
|
if result.Kind != commandkit.OutcomeSuccess || !strings.Contains(out, `"metadata"`) || !strings.Contains(out, `"history"`) {
|
|
|
|
|
t.Fatalf("historical explanation: %#v %q", result, out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestExplainDiscoveryCapabilityBeforeAcquisition(t *testing.T) {
|
|
|
|
|
calls := 0
|
|
|
|
|
scope := Scope[struct{}, struct{}]{Name: "app", Operations: []Operation{Explain}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
|
|
|
|
calls++
|
|
|
|
|
return commandkit.Acquired[View]{}, nil
|
|
|
|
|
}}
|
|
|
|
|
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{scope}}))
|
|
|
|
|
result := app.Execute(context.Background(), []string{"config", "explain", "--list"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
|
|
|
|
if result.Kind != commandkit.OutcomeUsage || calls != 0 {
|
|
|
|
|
t.Fatalf("unsupported discovery: %#v calls=%d", result, calls)
|
|
|
|
|
}
|
|
|
|
|
scope.Features.PathDiscovery = true
|
|
|
|
|
app = newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{scope}}))
|
|
|
|
|
result = app.Execute(context.Background(), []string{"config", "explain", "--list"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
|
|
|
|
if result.Kind != commandkit.OutcomeOperational || calls != 1 {
|
|
|
|
|
t.Fatalf("missing metadata: %#v calls=%d", result, calls)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestExplainBareHelpRecordsDeliveryFailure(t *testing.T) {
|
|
|
|
|
inputs, states, calls := 0, 0, 0
|
|
|
|
|
scope := Scope[struct{}, struct{}]{Name: "app", Operations: []Operation{Explain}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
|
|
|
|
calls++
|
|
|
|
|
return commandkit.Acquired[View]{}, nil
|
|
|
|
|
}}
|
|
|
|
|
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{NewInput: func() struct{} { inputs++; return struct{}{} }, NewState: func() struct{} { states++; return struct{}{} }, Scopes: []Scope[struct{}, struct{}]{scope}}))
|
|
|
|
|
result := app.Execute(context.Background(), []string{"config", "explain"}, commandkit.Streams{Out: shortWriter{}, Err: io.Discard})
|
|
|
|
|
if result.Presentation.Output.State != commandkit.DeliveryIncomplete || inputs != 0 || states != 0 || calls != 0 {
|
|
|
|
|
t.Fatalf("help delivery = %#v, input/state/acquire=%d/%d/%d", result, inputs, states, calls)
|
|
|
|
|
}
|
|
|
|
|
}
|