Files
commandkit/integrations/ckconfig/discovery_test.go

157 lines
6.9 KiB
Go

package ckconfig
import (
"bytes"
"context"
"encoding/json"
"io"
"reflect"
"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++
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})
})
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
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 {
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")
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil || len(report.Paths) != 1 || report.Paths[0].Path != "/ab" {
t.Fatalf("token boundary: %#v %q", result, out)
}
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)
}
result, out = run("--list", "--historical", "--set=/gone=null", "--format=json")
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil {
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)
}
result, out = run("--list", "--under=/gone", "--historical", "--explicit=false", "--set=/gone=null", "--format=json")
if result.Kind != commandkit.OutcomeSuccess || decode(out) != nil {
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)
}
}