153 lines
6.2 KiB
Go
153 lines
6.2 KiB
Go
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)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|