2026-10-11 21:51:54 +00:00
package ckconfig
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"strings"
"testing"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"go.yaml.in/yaml/v4"
)
func TestConfigDiffExecutesWithDefaultAndExplicitYAMLAndInheritedSelections ( t * testing . T ) {
type settings struct {
Greeting string ` yaml:"greeting" `
}
var selections [ ] Selection
app := newConfigTestApp ( t , AppCommand ( func ( _ context . Context , selection Selection ) ( * configkit . Result [ settings ] , error ) {
selections = append ( selections , selection )
value := "Hello"
if selection . Config . Set && selection . Config . Value == "after.yml" {
value = "Goodbye"
}
return configkit . Load ( configkit . Mapping ( "selected configuration" , map [ string ] any { "greeting" : value } ) , configkit . Options [ settings ] { } )
} ) )
var output bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--config" , "before.yml" , "--overlay" , "base" , "--against-config" , "after.yml" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess {
t . Fatalf ( "default YAML comparison outcome = %#v" , outcome )
}
if len ( selections ) != 2 || ! selections [ 0 ] . Config . Set || selections [ 0 ] . Config . Value != "before.yml" || ! selections [ 1 ] . Config . Set || selections [ 1 ] . Config . Value != "after.yml" || selections [ 1 ] . Overlay != selections [ 0 ] . Overlay {
t . Fatalf ( "comparison selections = %#v" , selections )
}
var document yaml . Node
if err := yaml . Unmarshal ( output . Bytes ( ) , & document ) ; err != nil {
t . Fatalf ( "default report is not YAML: %q: %v" , output . String ( ) , err )
}
changes := reportField ( document . Content [ 0 ] , "changes" )
if value := reportField ( changes , "greeting" ) ; value == nil || value . Value != "Goodbye" {
t . Fatalf ( "public comparison output = %#v" , document . Content [ 0 ] )
}
output . Reset ( )
outcome = app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--format" , "yaml" , "--config" , "before.yml" , "--against-config" , "before.yml" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess {
t . Fatalf ( "explicit YAML comparison outcome = %#v" , outcome )
}
var equal yaml . Node
if err := yaml . Unmarshal ( output . Bytes ( ) , & equal ) ; err != nil {
t . Fatalf ( "explicit YAML report is not YAML: %q: %v" , output . String ( ) , err )
}
if len ( reportField ( equal . Content [ 0 ] , "additions" ) . Content ) != 0 || len ( reportField ( equal . Content [ 0 ] , "changes" ) . Content ) != 0 || len ( reportField ( equal . Content [ 0 ] , "deletions" ) . Content ) != 0 {
t . Fatalf ( "equal comparison did not return empty categories: %q" , output . String ( ) )
}
loadsBeforeText := len ( selections )
output . Reset ( )
outcome = app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--format" , "text" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeUsage || len ( selections ) != loadsBeforeText || output . Len ( ) != 0 {
t . Fatalf ( "text format outcome/work/output = %#v / %d / %q" , outcome , len ( selections ) - loadsBeforeText , output . String ( ) )
}
}
func TestConfigDiffRunsComparisonOnlyScopeAndRejectsUnsupportedScopeBeforeWork ( t * testing . T ) {
var inputCalls , stateCalls , acquireCalls , compareCalls int
app := newConfigTestApp ( t , Command ( Suite [ struct { } , struct { } ] {
NewInput : func ( ) struct { } { inputCalls ++ ; return struct { } { } } ,
NewState : func ( ) struct { } { stateCalls ++ ; return struct { } { } } ,
Scopes : [ ] Scope [ struct { } , struct { } ] {
{ Name : "app" , Operations : [ ] Operation { Diff } , Compare : func ( context . Context , ComparisonRequest [ struct { } ] , struct { } ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
compareCalls ++
return commandkit . Acquired [ [ ] configkit . Difference ] { } , nil
} } ,
{ Name : "plain" , Operations : [ ] Operation { Show } , Acquire : func ( context . Context , Request , struct { } , struct { } ) ( commandkit . Acquired [ View ] , error ) {
acquireCalls ++
return commandkit . Acquired [ View ] { } , nil
} } ,
} ,
} ) )
var output bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "scopes" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || ! strings . Contains ( output . String ( ) , "app: diff" ) {
t . Fatalf ( "comparison-only discovery = %#v, %q" , outcome , output . String ( ) )
}
output . Reset ( )
outcome = app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--scope" , "app" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || compareCalls != 1 {
t . Fatalf ( "comparison-only execution = %#v calls=%d" , outcome , compareCalls )
}
inputsBefore , statesBefore := inputCalls , stateCalls
output . Reset ( )
outcome = app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--scope" , "plain" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeUsage || inputCalls != inputsBefore || stateCalls != statesBefore || acquireCalls != 0 || compareCalls != 1 || output . Len ( ) != 0 {
t . Fatalf ( "unsupported scope outcome/work/output = %#v / %d,%d,%d,%d / %q" , outcome , inputCalls , stateCalls , acquireCalls , compareCalls , output . String ( ) )
}
}
func TestConfigDiffProviderFailureRetainsDiagnosticsAndCause ( t * testing . T ) {
wantErr := errors . New ( "provider unavailable" )
2026-10-26 03:20:17 +00:00
diagnostic := commandkit . Diagnostic { Severity : commandkit . SeverityWarning , Write : func ( writer io . Writer ) error {
2026-10-11 21:51:54 +00:00
_ , err := io . WriteString ( writer , "provider warning\n" )
return err
} }
app := newConfigTestApp ( t , Command ( Suite [ struct { } , struct { } ] { Scopes : [ ] Scope [ struct { } , struct { } ] { {
Name : "app" , Operations : [ ] Operation { Diff } ,
Compare : func ( context . Context , ComparisonRequest [ struct { } ] , struct { } ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
2026-10-26 03:20:17 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] {
Diagnostics : [ ] commandkit . Diagnostic { diagnostic } ,
Explanation : commandkit . Explanation { Details : [ ] commandkit . Detail { func ( writer io . Writer ) error {
_ , err := io . WriteString ( writer , "native comparison context\n" )
return err
} } } ,
} , fmt . Errorf ( "comparison setup: %w" , wantErr )
2026-10-11 21:51:54 +00:00
} ,
} } } ) )
2026-10-26 03:20:17 +00:00
var output , diagnosticOutput bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--verbose" } , commandkit . Streams { Out : & output , Err : & diagnosticOutput } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Cause , wantErr ) || output . Len ( ) != 0 {
2026-10-11 21:51:54 +00:00
t . Fatalf ( "provider failure outcome/output = %#v / %q" , outcome , output . String ( ) )
}
2026-10-26 03:20:17 +00:00
if got := diagnosticOutput . String ( ) ; ! strings . Contains ( got , "provider warning" ) || ! strings . Contains ( got , "native comparison context" ) || strings . Index ( got , "provider warning" ) > strings . Index ( got , "native comparison context" ) || strings . Contains ( got , wantErr . Error ( ) ) {
t . Fatalf ( "provider messages and terminal context = %q" , got )
}
2026-10-11 21:51:54 +00:00
}
func TestConfigDiffReportPreparationFailureWritesNoReport ( t * testing . T ) {
app := newConfigTestApp ( t , Command ( Suite [ struct { } , struct { } ] { Scopes : [ ] Scope [ struct { } , struct { } ] { {
Name : "app" , Operations : [ ] Operation { Diff } ,
Compare : func ( context . Context , ComparisonRequest [ struct { } ] , struct { } ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
return commandkit . Acquired [ [ ] configkit . Difference ] { Value : [ ] configkit . Difference { {
Path : configkit . Keys ( "invalid" ) , Right : configkit . ValueSnapshot { Present : true , YAML : "bad: [" } ,
} } } , nil
} ,
} } } ) )
var output bytes . Buffer
2026-10-26 03:20:17 +00:00
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--quiet" } , commandkit . Streams { Out : failingOutput { err : errors . New ( "quiet destination should not be called" ) } , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeOperational || outcome . Cause != nil || outcome . Presentation . Output . State != commandkit . DeliveryIncomplete || outcome . Presentation . Output . RenderError == nil || output . Len ( ) != 0 {
2026-10-11 21:51:54 +00:00
t . Fatalf ( "report preparation failure outcome/output = %#v / %q" , outcome , output . String ( ) )
}
}
func TestConfigDiffCancellationAfterAcquisitionRetainsDiagnosticsWithoutWriting ( t * testing . T ) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
diagnostic := commandkit . Diagnostic { Severity : commandkit . SeverityWarning , Write : func ( io . Writer ) error { return nil } }
app := newConfigTestApp ( t , Command ( Suite [ struct { } , struct { } ] { Scopes : [ ] Scope [ struct { } , struct { } ] { {
Name : "app" , Operations : [ ] Operation { Diff } ,
Compare : func ( context . Context , ComparisonRequest [ struct { } ] , struct { } ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
cancel ( )
return commandkit . Acquired [ [ ] configkit . Difference ] { Diagnostics : [ ] commandkit . Diagnostic { diagnostic } } , nil
} ,
} } } ) )
defer cancel ( )
var output bytes . Buffer
2026-10-26 03:20:17 +00:00
outcome := app . Execute ( ctx , [ ] string { "config" , "diff" , "--verbose" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Cause , context . Canceled ) || output . Len ( ) != 0 {
2026-10-11 21:51:54 +00:00
t . Fatalf ( "cancelled comparison outcome/output = %#v / %q" , outcome , output . String ( ) )
}
}
func TestConfigDiffOutputErrorsCarryIncompleteDeliveryOutcome ( t * testing . T ) {
app := newConfigTestApp ( t , Command ( Suite [ struct { } , struct { } ] { Scopes : [ ] Scope [ struct { } , struct { } ] { {
Name : "app" , Operations : [ ] Operation { Diff } ,
Compare : func ( context . Context , ComparisonRequest [ struct { } ] , struct { } ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
return commandkit . Acquired [ [ ] configkit . Difference ] { } , nil
} ,
} } } ) )
wantErr := errors . New ( "output failed" )
for _ , test := range [ ] struct {
name string
writer io . Writer
want error
} {
{ name : "writer error" , writer : failingOutput { err : wantErr } , want : wantErr } ,
{ name : "short write" , writer : shortWriter { } , want : io . ErrShortWrite } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" } , commandkit . Streams { Out : test . writer , Err : io . Discard } )
2026-10-26 03:20:17 +00:00
if outcome . Kind != commandkit . OutcomeOperational || outcome . Cause != nil || ! errors . Is ( outcome . Presentation . Output . WriteError , test . want ) || outcome . Presentation . Output . State != commandkit . DeliveryIncomplete {
2026-10-11 21:51:54 +00:00
t . Fatalf ( "delivery failure outcome = %#v" , outcome )
}
} )
}
}
2026-10-26 03:20:17 +00:00
func TestConfigDiffDiagnosticDestinationFailureIsPresentationEvidence ( t * testing . T ) {
app := newConfigTestApp ( t , Command ( Suite [ struct { } , struct { } ] { Scopes : [ ] Scope [ struct { } , struct { } ] { {
Name : "app" , Operations : [ ] Operation { Diff } ,
Compare : func ( context . Context , ComparisonRequest [ struct { } ] , struct { } ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
return commandkit . Acquired [ [ ] configkit . Difference ] {
Diagnostics : [ ] commandkit . Diagnostic { { Severity : commandkit . SeverityWarning , Write : func ( writer io . Writer ) error {
_ , err := io . WriteString ( writer , "provider warning\n" )
return err
} } } ,
} , nil
} ,
} } } ) )
writeErr := errors . New ( "diagnostic destination failed" )
var output bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--verbose" } , commandkit . Streams { Out : & output , Err : failingOutput { err : writeErr } } )
if outcome . Kind != commandkit . OutcomeOperational || outcome . Cause != nil || ! errors . Is ( outcome . Presentation . Diagnostics . WriteError , writeErr ) || outcome . Presentation . Diagnostics . State != commandkit . DeliveryIncomplete {
t . Fatalf ( "diagnostic delivery failure outcome = %#v" , outcome )
}
if output . Len ( ) == 0 {
t . Fatal ( "healthy result destination did not receive the diff report" )
}
}
2026-10-11 21:51:54 +00:00
func TestConfigDiffRepeatedInvocationsPrepareFreshInputsAndState ( t * testing . T ) {
type input struct {
ID int
Label string
}
type state struct { ID int }
label := commandkit . ValueFlag ( commandkit . FlagMeta { Name : "label" } , ( * string ) ( nil ) , parseConfigString ,
func ( target * input , value commandkit . Supplied [ string ] ) { target . Label = value . Value } )
var inputID , stateID int
var observed [ ] struct {
left , right input
state * state
}
app := newConfigTestApp ( t , Command ( Suite [ input , * state ] {
NewInput : func ( ) input { inputID ++ ; return input { ID : inputID } } ,
NewState : func ( ) * state { stateID ++ ; return & state { ID : stateID } } ,
Selectors : [ ] Selector [ input ] { { Binding : label , Scopes : [ ] string { "app" } } } ,
Scopes : [ ] Scope [ input , * state ] { { Name : "app" , Operations : [ ] Operation { Diff } , Compare : func ( _ context . Context , request ComparisonRequest [ input ] , shared * state ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
observed = append ( observed , struct {
left , right input
state * state
} { request . Left . Input , request . Right . Input , shared } )
left , err := configkit . Load [ struct {
Label string ` yaml:"label" `
} ] ( configkit . Mapping ( "left" , map [ string ] any { "label" : request . Left . Input . Label } ) )
if err != nil {
return commandkit . Acquired [ [ ] configkit . Difference ] { } , err
}
right , err := configkit . Load [ struct {
Label string ` yaml:"label" `
} ] ( configkit . Mapping ( "right" , map [ string ] any { "label" : request . Right . Input . Label + "-right" } ) )
if err != nil {
return commandkit . Acquired [ [ ] configkit . Difference ] { } , err
}
differences , err := configkit . Diff ( left , right )
return commandkit . Acquired [ [ ] configkit . Difference ] { Value : differences } , err
} } } ,
} ) )
for range 2 {
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--label" , "current" } , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess {
t . Fatalf ( "comparison invocation outcome = %#v" , outcome )
}
}
if len ( observed ) != 2 || observed [ 0 ] . left . ID == observed [ 0 ] . right . ID || observed [ 1 ] . left . ID == observed [ 1 ] . right . ID || observed [ 0 ] . left . ID == observed [ 1 ] . left . ID || observed [ 0 ] . right . ID == observed [ 1 ] . right . ID {
t . Fatalf ( "input instances were reused: %#v" , observed )
}
if observed [ 0 ] . left . Label != "current" || observed [ 0 ] . right . Label != "current" || observed [ 1 ] . left . Label != "current" || observed [ 1 ] . right . Label != "current" {
t . Fatalf ( "comparison inputs missed captured flag values: %#v" , observed )
}
if observed [ 0 ] . state == nil || observed [ 1 ] . state == nil || observed [ 0 ] . state == observed [ 1 ] . state || observed [ 0 ] . state . ID != 1 || observed [ 1 ] . state . ID != 2 {
t . Fatalf ( "state was not fresh per invocation: %#v" , observed )
}
}
func TestConfigDiffExecutesConcealedChangesAndEmptyResults ( t * testing . T ) {
type settings struct {
Secret string ` yaml:"secret" `
}
app := newConfigTestApp ( t , AppCommand ( func ( _ context . Context , selection Selection ) ( * configkit . Result [ settings ] , error ) {
value := "same"
if selection . Config . Set && selection . Config . Value == "after" {
value = "changed-secret"
}
return configkit . Load ( configkit . Mapping ( "selected" , map [ string ] any { "secret" : value } ) , configkit . Options [ settings ] { Definition : configkit . Definition [ settings ] { Secret : func ( path configkit . Path ) bool { return path . String ( ) == "/secret" } } } )
} ) )
var output bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--config" , "before" , "--against-config" , "after" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || ! strings . Contains ( output . String ( ) , "[REDACTED]" ) || strings . Contains ( output . String ( ) , "changed-secret" ) {
t . Fatalf ( "concealed comparison outcome/report = %#v / %q" , outcome , output . String ( ) )
}
output . Reset ( )
outcome = app . Execute ( context . Background ( ) , [ ] string { "config" , "diff" , "--config" , "before" , "--against-config" , "before" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || ! strings . Contains ( output . String ( ) , "additions:" ) || ! strings . Contains ( output . String ( ) , "changes:" ) || ! strings . Contains ( output . String ( ) , "deletions:" ) {
t . Fatalf ( "empty comparison outcome/report = %#v / %q" , outcome , output . String ( ) )
}
}