Files
commandkit/integrations/ckconfig/shared_report_test.go

153 lines
6.2 KiB
Go
Raw Normal View History

package ckconfig
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"reflect"
"testing"
commandkit "go.maximumdirect.net/commandkit"
configkit "go.maximumdirect.net/configkit"
)
func TestShowSharedFormatsAndScopeShape(t *testing.T) {
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{{
Name: "app", Operations: []Operation{Show}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
return commandkit.Acquired[View]{Value: View{Effective: effectiveValue("'false'\n")}}, nil
},
}}}))
for _, tc := range []struct {
name string
args []string
want string
}{
{"automatic", []string{"config", "show"}, "false\n"},
{"text", []string{"config", "show", "--format=text"}, "false\n"},
{"yaml", []string{"config", "show", "--format=yaml"}, "\"false\"\n"},
{"json", []string{"config", "show", "--format=json"}, "\"false\"\n"},
} {
t.Run(tc.name, func(t *testing.T) {
var out bytes.Buffer
result := app.Execute(t.Context(), tc.args, commandkit.Streams{Out: &out, Err: io.Discard})
if tc.name == "yaml" {
value, err := commandkit.DecodeYAMLReport(&out)
if err != nil || value != "false" || result.Status != 0 {
t.Fatalf("%#v value=%#v err=%v", result, value, err)
}
return
}
if result.Status != 0 || out.String() != tc.want {
t.Fatalf("%#v output=%q", result, out.String())
}
})
}
var out bytes.Buffer
result := app.Execute(t.Context(), []string{"config", "show", "--scope=all", "--format=json"}, commandkit.Streams{Out: &out, Err: io.Discard})
var decoded map[string]any
if err := json.Unmarshal(out.Bytes(), &decoded); err != nil || result.Status != 0 || !reflect.DeepEqual(decoded, map[string]any{"app": "false"}) {
t.Fatalf("%#v output=%q decoded=%#v err=%v", result, out.String(), decoded, err)
}
}
func TestShowConversionFailureIsPresentationEvidence(t *testing.T) {
for _, quiet := range []bool{false, true} {
t.Run(map[bool]string{false: "ordinary", true: "quiet"}[quiet], func(t *testing.T) {
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{{Name: "app", Operations: []Operation{Show}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
return commandkit.Acquired[View]{Value: View{Effective: effectiveValue("x: [invalid\n")}}, nil
}}}}))
args := []string{"config", "show"}
if quiet {
args = append(args, "--quiet")
}
var out bytes.Buffer
result := app.Execute(t.Context(), args, commandkit.Streams{Out: &out, Err: io.Discard})
if result.Kind != commandkit.OutcomeOperational || result.Presentation.Output.RenderError == nil || out.Len() != 0 {
t.Fatalf("%#v output=%q", result, out.String())
}
})
}
}
func TestValidationSubmitsOneReportAfterAcquisitionStops(t *testing.T) {
failure := errors.New("invalid configuration")
calls := 0
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{
{Name: "app", Operations: []Operation{Validate}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
calls++
return commandkit.Acquired[View]{}, nil
}},
{Name: "middle", Operations: []Operation{Validate}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
calls++
return commandkit.Acquired[View]{}, failure
}},
{Name: "third", Operations: []Operation{Validate}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
calls++
return commandkit.Acquired[View]{}, nil
}},
}}))
var out bytes.Buffer
result := app.Execute(t.Context(), []string{"config", "validate", "--scope=all", "--format=json"}, commandkit.Streams{Out: &out, Err: io.Discard})
var report struct {
Scopes []struct{ Scope, Outcome string }
}
dec := json.NewDecoder(&out)
if err := dec.Decode(&report); err != nil {
t.Fatal(err)
}
if err := dec.Decode(new(any)); err != io.EOF {
t.Fatalf("additional JSON: %v", err)
}
if !errors.Is(result.Cause, failure) || calls != 2 || len(report.Scopes) != 2 || report.Scopes[0].Outcome != "valid" || report.Scopes[1].Outcome != "failed" {
t.Fatalf("result=%#v calls=%d report=%#v", result, calls, report)
}
}
func TestValidationDeliveryFailureDoesNotStopAcquisition(t *testing.T) {
writeErr := errors.New("write failed")
calls := 0
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{}]{{Name: "app", Operations: []Operation{Validate}, Acquire: acquire}, {Name: "second", Operations: []Operation{Validate}, Acquire: acquire}}}))
result := app.Execute(t.Context(), []string{"config", "validate", "--scope=all"}, commandkit.Streams{Out: failingOutput{err: writeErr}, Err: io.Discard})
if calls != 2 || !errors.Is(result.Presentation.Output.WriteError, writeErr) || result.Status == 0 {
t.Fatalf("result=%#v calls=%d", result, calls)
}
}
func TestConfigurationReportsSupportSharedFormats(t *testing.T) {
type settings struct {
Greeting string `yaml:"greeting"`
}
app := newConfigTestApp(t, AppCommand(func(context.Context, Selection) (*configkit.Result[settings], error) {
return configkit.Load(configkit.Mapping("fixture", map[string]any{"greeting": "hello"}), configkit.Options[settings]{})
}))
for _, args := range [][]string{{"scopes"}, {"show"}, {"sources"}, {"explain", "--path=/greeting"}, {"diff"}, {"validate"}} {
for _, format := range []string{"text", "yaml", "json"} {
t.Run(args[0]+"/"+format, func(t *testing.T) {
argv := append([]string{"config"}, args...)
argv = append(argv, "--format="+format)
var out bytes.Buffer
result := app.Execute(t.Context(), argv, commandkit.Streams{Out: &out, Err: io.Discard})
if result.Status != 0 {
t.Fatalf("%#v output=%q", result, out.String())
}
if format == "json" {
dec := json.NewDecoder(&out)
var value any
if err := dec.Decode(&value); err != nil {
t.Fatal(err)
}
if err := dec.Decode(&value); err != io.EOF {
t.Fatalf("extra JSON: %v", err)
}
}
})
}
}
}