2026-10-11 21:52:47 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"io"
|
2026-10-26 03:44:19 +00:00
|
|
|
"strings"
|
2026-10-11 21:52:47 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
|
|
|
|
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
|
|
|
|
|
"go.yaml.in/yaml/v4"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestSimpleAppComparesFixturesAndKeepsDefaultCommands(t *testing.T) {
|
|
|
|
|
app, err := newApp()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var output bytes.Buffer
|
|
|
|
|
outcome := app.Execute(context.Background(), []string{"config", "diff", "--config", "fixtures/before.yml", "--against-config", "fixtures/after.yml"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
|
|
|
if outcome.Kind != commandkit.OutcomeSuccess {
|
|
|
|
|
t.Fatalf("fixture comparison outcome = %#v", outcome)
|
|
|
|
|
}
|
|
|
|
|
var report yaml.Node
|
|
|
|
|
if err := yaml.Unmarshal(output.Bytes(), &report); err != nil {
|
|
|
|
|
t.Fatalf("comparison report is not YAML: %q: %v", output.String(), err)
|
|
|
|
|
}
|
|
|
|
|
changes := field(report.Content[0], "changes")
|
|
|
|
|
greeting := field(changes, "greeting")
|
|
|
|
|
if greeting == nil || greeting.Value != "Goodbye" {
|
|
|
|
|
t.Fatalf("fixture comparison report = %q", output.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output.Reset()
|
|
|
|
|
outcome = app.Execute(context.Background(), []string{"config", "diff", "--config", "fixtures/before.yml", "--against-config", "fixtures/before.yml"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
|
|
|
if outcome.Kind != commandkit.OutcomeSuccess {
|
|
|
|
|
t.Fatalf("equal fixture comparison outcome = %#v", outcome)
|
|
|
|
|
}
|
|
|
|
|
if reportNode, err := decodeReport(output.Bytes()); err != nil || len(field(reportNode, "additions").Content) != 0 || len(field(reportNode, "changes").Content) != 0 || len(field(reportNode, "deletions").Content) != 0 {
|
|
|
|
|
t.Fatalf("equal fixture report = %q, decode error %v", output.String(), err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output.Reset()
|
|
|
|
|
outcome = app.Execute(context.Background(), []string{"config", "show"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
2026-10-26 03:44:19 +00:00
|
|
|
if outcome.Kind != commandkit.OutcomeSuccess || output.String() != "greeting: Hello\nschema:\n version: 2\n" {
|
2026-10-11 21:52:47 +00:00
|
|
|
t.Fatalf("default show outcome/output = %#v / %q", outcome, output.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output.Reset()
|
|
|
|
|
outcome = app.Execute(context.Background(), []string{"greet"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
|
|
|
|
if outcome.Kind != commandkit.OutcomeSuccess || len(output.String()) == 0 || output.String()[:5] != "Hello" {
|
|
|
|
|
t.Fatalf("default greeting outcome/output = %#v / %q", outcome, output.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSimpleLoaderDoesNotReplaceExplicitEmptyConfiguration(t *testing.T) {
|
|
|
|
|
_, err := loadAppConfiguration(context.Background(), ckconfig.Selection{Config: commandkit.Supplied[string]{Set: true}})
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("explicit empty configuration fell back to the default source")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-26 03:44:19 +00:00
|
|
|
func TestSimpleConfigurationReportingModes(t *testing.T) {
|
|
|
|
|
for _, test := range []struct {
|
|
|
|
|
name string
|
|
|
|
|
args []string
|
|
|
|
|
wantOutput string
|
|
|
|
|
wantDiagnostic string
|
|
|
|
|
quiet bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "ordinary output hides provider warning",
|
|
|
|
|
args: []string{"config", "show", "--config", "fixtures/before.yml"},
|
|
|
|
|
wantOutput: "greeting: Hello\nschema:\n version: 1\n",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "quiet suppresses configuration report",
|
|
|
|
|
args: []string{"config", "show", "--config", "fixtures/before.yml", "--quiet"},
|
|
|
|
|
quiet: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "verbose shows ConfigKit warning",
|
|
|
|
|
args: []string{"config", "show", "--config", "fixtures/before.yml", "--verbose"},
|
|
|
|
|
wantOutput: "greeting: Hello\nschema:\n version: 1\n",
|
|
|
|
|
wantDiagnostic: "schema version 1 is supported",
|
|
|
|
|
},
|
|
|
|
|
} {
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
outcome, output, diagnostics := executeSimple(t, test.args...)
|
|
|
|
|
if outcome.Kind != commandkit.OutcomeSuccess || outcome.Cause != nil {
|
|
|
|
|
t.Fatalf("outcome = %#v", outcome)
|
|
|
|
|
}
|
|
|
|
|
if output != test.wantOutput {
|
|
|
|
|
t.Errorf("output = %q, want %q", output, test.wantOutput)
|
|
|
|
|
}
|
|
|
|
|
if test.wantDiagnostic != "" && !strings.Contains(diagnostics, test.wantDiagnostic) {
|
|
|
|
|
t.Errorf("diagnostics = %q, want provider warning %q", diagnostics, test.wantDiagnostic)
|
|
|
|
|
}
|
|
|
|
|
if test.wantDiagnostic == "" && diagnostics != "" {
|
|
|
|
|
t.Errorf("diagnostics = %q, want no warning outside verbose mode", diagnostics)
|
|
|
|
|
}
|
|
|
|
|
if strings.Contains(diagnostics, "ERROR:") {
|
|
|
|
|
t.Errorf("diagnostics %q unexpectedly contain a terminal failure", diagnostics)
|
|
|
|
|
}
|
|
|
|
|
if test.quiet && (outcome.Presentation.Output.State != commandkit.DeliverySuppressed || outcome.Presentation.Diagnostics.State != commandkit.DeliverySuppressed) {
|
|
|
|
|
t.Errorf("quiet presentation = %#v, want report and provider warning suppressed", outcome.Presentation)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSimpleCustomCommandUsesSharedReportingFlags(t *testing.T) {
|
|
|
|
|
outcome, output, diagnostics := executeSimple(t, "greet", "--quiet")
|
|
|
|
|
if outcome.Kind != commandkit.OutcomeSuccess || output != "" || diagnostics != "" {
|
|
|
|
|
t.Fatalf("quiet greet outcome/output/diagnostics = %#v / %q / %q", outcome, output, diagnostics)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func executeSimple(t *testing.T, args ...string) (commandkit.Outcome, string, string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
app, err := newApp()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var output, diagnostics bytes.Buffer
|
|
|
|
|
outcome := app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: &diagnostics})
|
|
|
|
|
return outcome, output.String(), diagnostics.String()
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-11 21:52:47 +00:00
|
|
|
func field(mapping *yaml.Node, name string) *yaml.Node {
|
|
|
|
|
if mapping == nil || mapping.Kind != yaml.MappingNode {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
for index := 0; index+1 < len(mapping.Content); index += 2 {
|
|
|
|
|
if mapping.Content[index].Value == name {
|
|
|
|
|
return mapping.Content[index+1]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func decodeReport(data []byte) (*yaml.Node, error) {
|
|
|
|
|
var document yaml.Node
|
|
|
|
|
if err := yaml.Unmarshal(data, &document); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return document.Content[0], nil
|
|
|
|
|
}
|