Move configuration reports to shared rendering
This commit is contained in:
152
integrations/ckconfig/shared_report_test.go
Normal file
152
integrations/ckconfig/shared_report_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user