2026-10-12 22:16:44 +00:00
package ckprompt
import (
"bytes"
"context"
2026-10-12 22:18:50 +00:00
"errors"
2026-10-12 22:16:44 +00:00
"io"
"reflect"
2026-10-12 22:18:50 +00:00
"strings"
2026-10-12 22:16:44 +00:00
"testing"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
2026-10-12 22:20:07 +00:00
configkit "gitea.maximumdirect.net/eric/ConfigKit"
2026-10-12 22:16:44 +00:00
promptkit "gitea.maximumdirect.net/eric/promptkit"
"go.yaml.in/yaml/v4"
)
func TestValidationCommandComposesWithRequirementsShow ( t * testing . T ) {
type input struct { campaign commandkit . Supplied [ string ] }
type state struct { sequence int }
campaignFlag := commandkit . ValueFlag ( commandkit . FlagMeta { Name : "campaign" , Usage : "Campaign selector." } , ( * string ) ( nil ) , parseDefinitionString ,
func ( target * input , value commandkit . Supplied [ string ] ) { target . campaign = value } )
wantSelection := ckconfig . Selection {
Config : commandkit . Supplied [ string ] { Value : "config.yml" , Set : true } ,
Overlay : commandkit . Supplied [ string ] { Value : "overlay.yml" , Set : true } ,
}
wantInput := input { campaign : commandkit . Supplied [ string ] { Value : "spring" , Set : true } }
var received ValidationRequest
var receivedInput input
validateCalls := 0
suite := Suite [ input , state ] {
NewInput : func ( ) input { return input { } } ,
NewState : func ( ) state { return state { sequence : 1 } } ,
Selectors : [ ] commandkit . Flag [ input ] { campaignFlag } ,
CheckInput : func ( selection ckconfig . Selection , value input ) error {
if selection != wantSelection || ! reflect . DeepEqual ( value , wantInput ) {
t . Errorf ( "checked input = (%#v, %#v)" , selection , value )
}
return nil
} ,
InspectRequirements : func ( context . Context , RequirementsRequest , input , state ) ( commandkit . Acquired [ * promptkit . RequirementInspection ] , error ) {
return commandkit . Acquired [ * promptkit . RequirementInspection ] { Value : & promptkit . RequirementInspection { PromptID : "briefing" } } , nil
} ,
ValidateRequest : func ( _ context . Context , request ValidationRequest , value input , gotState state ) ( commandkit . Acquired [ struct { } ] , error ) {
validateCalls ++
received = request
receivedInput = value
if gotState . sequence != 1 {
t . Errorf ( "state = %#v" , gotState )
}
return commandkit . Acquired [ struct { } ] { } , nil
} ,
}
app := newPromptTestApp ( t , Command ( suite ) )
var help bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "help" , "promptkit" , "requirements" } , commandkit . Streams { Out : & help , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || ! bytes . Contains ( help . Bytes ( ) , [ ] byte ( "show" ) ) || ! bytes . Contains ( help . Bytes ( ) , [ ] byte ( "validate" ) ) {
t . Fatalf ( "requirements help = (%#v, %q)" , outcome , help . String ( ) )
}
var output bytes . Buffer
outcome = app . Execute ( context . Background ( ) , [ ] string {
"promptkit" , "requirements" , "validate" , "briefing" ,
"--config" , "config.yml" , "--overlay" , "overlay.yml" , "--campaign" , "spring" ,
2026-10-17 18:39:38 +00:00
"--profile" , "editorial" , "--session-id" , "session-1" ,
2026-10-12 22:16:44 +00:00
"--var" , "topic=roadmap" , "--input" , "notes=fixtures/notes=final.txt" , "--format" , "yaml" ,
} , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || outcome . Status != 0 || validateCalls != 1 {
t . Fatalf ( "validation outcome = (%#v, %q), calls=%d" , outcome , output . String ( ) , validateCalls )
}
2026-10-17 18:39:38 +00:00
if received . Selection != wantSelection || received . Request . PromptID != "briefing" || received . Request . ProfileID != "editorial" || received . Request . SessionID != "session-1" {
2026-10-12 22:16:44 +00:00
t . Errorf ( "validation request = %#v" , received )
}
if received . Request . Vars [ "topic" ] != "roadmap" || len ( received . Request . Vars ) != 1 {
t . Errorf ( "request vars = %#v" , received . Request . Vars )
}
if ref , ok := received . Request . Inputs [ "notes" ] ; ! ok || ref != promptkit . File ( "fixtures/notes=final.txt" ) || len ( received . Request . Inputs ) != 1 {
t . Errorf ( "request inputs = %#v" , received . Request . Inputs )
}
if ! reflect . DeepEqual ( receivedInput , wantInput ) {
t . Errorf ( "provider input = %#v, want %#v" , receivedInput , wantInput )
}
var report map [ string ] any
if err := yaml . Unmarshal ( output . Bytes ( ) , & report ) ; err != nil {
t . Fatalf ( "decode validation report %q: %v" , output . String ( ) , err )
}
if len ( report ) != 2 || report [ "prompt_id" ] != "briefing" || report [ "valid" ] != true {
t . Fatalf ( "validation confirmation = %#v" , report )
}
}
2026-10-12 22:18:50 +00:00
func TestValidationCommandIsAvailableIndependentlyAndOwnsItsFamily ( t * testing . T ) {
validationOnly := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
return commandkit . Acquired [ struct { } ] { } , nil
} } ) )
var help bytes . Buffer
outcome := validationOnly . Execute ( context . Background ( ) , [ ] string { "help" , "promptkit" , "requirements" } , commandkit . Streams { Out : & help , Err : io . Discard } )
2026-10-26 03:35:39 +00:00
if outcome . Kind != commandkit . OutcomeSuccess || ! strings . Contains ( help . String ( ) , "validate" ) || strings . Contains ( help . String ( ) , "\n show " ) {
2026-10-12 22:18:50 +00:00
t . Fatalf ( "validation-only availability = (%#v, %q)" , outcome , help . String ( ) )
}
declaration := Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
return commandkit . Acquired [ struct { } ] { } , nil
} } )
other := commandkit . Group ( "promptkit" , "An application command tree." , commandkit . NewCommand ( commandkit . CommandSpec [ struct { } ] { Name : "child" , Summary : "A child." } ) )
app := newPromptTestApp ( t , other )
if err := app . Register ( declaration ) ; err == nil {
t . Fatal ( "validation-only command installed over an existing PromptKit namespace" )
}
if err := newPromptTestApp ( t , declaration ) . Register ( other ) ; err == nil {
t . Fatal ( "later application command installed over validation-only PromptKit namespace" )
}
if _ , err := commandkit . New ( commandkit . Identity { Name : "nested" } , commandkit . Group ( "outer" , "Outer group." , declaration ) ) ; err == nil {
t . Fatal ( "validation-only root family was accepted beneath another group" )
}
}
func TestValidationCommandConstructionAndCaptureLifecycle ( t * testing . T ) {
type input struct { campaign commandkit . Supplied [ string ] }
type state struct { }
newInputCalls , newStateCalls , checkCalls := 0 , 0 , 0
buildCalls , loadCalls := 0 , 0
engine := adapterEngine ( t )
oldSelector := commandkit . ValueFlag ( commandkit . FlagMeta { Name : "campaign" , Required : true } , ( * string ) ( nil ) , parseDefinitionString ,
func ( target * input , value commandkit . Supplied [ string ] ) { target . campaign = value } )
newSelector := commandkit . ValueFlag ( commandkit . FlagMeta { Name : "replacement" } , ( * string ) ( nil ) , parseDefinitionString ,
func ( * input , commandkit . Supplied [ string ] ) { } )
suite := WithRequestValidation ( Suite [ input , state ] {
NewInput : func ( ) input { newInputCalls ++ ; return input { } } ,
NewState : func ( ) state { newStateCalls ++ ; return state { } } ,
Selectors : [ ] commandkit . Flag [ input ] { oldSelector } ,
CheckInput : func ( _ ckconfig . Selection , value input ) error {
checkCalls ++
if ! value . campaign . Set || value . campaign . Value != "spring" {
return errors . New ( "captured campaign selector was not applied" )
}
return nil
} ,
} , func ( context . Context , ckconfig . Selection , input , state ) ( * promptkit . Engine , error ) {
loadCalls ++
return engine , nil
} , func ( _ context . Context , request ValidationRequest , _ input , _ state ) ( promptkit . RunRequest , error ) {
buildCalls ++
return request . Request , nil
} )
declaration := Command ( suite )
if newInputCalls != 0 || newStateCalls != 0 || checkCalls != 0 || buildCalls != 0 || loadCalls != 0 {
t . Fatal ( "command construction invoked application or adapter callbacks" )
}
// Command captured the original callback and binding metadata.
suite . ValidateRequest = func ( context . Context , ValidationRequest , input , state ) ( commandkit . Acquired [ struct { } ] , error ) {
return commandkit . Acquired [ struct { } ] { } , errors . New ( "replacement callback was captured" )
}
suite . Selectors [ 0 ] = newSelector
app := newPromptTestApp ( t , declaration )
for _ , args := range [ ] [ ] string { { "help" , "promptkit" , "requirements" , "validate" } , { "version" } } {
outcome := app . Execute ( context . Background ( ) , args , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess {
t . Fatalf ( "construction lifecycle command %v = %#v" , args , outcome )
}
}
if newInputCalls != 0 || newStateCalls != 0 || checkCalls != 0 || buildCalls != 0 || loadCalls != 0 {
t . Fatal ( "installation, help, or version invoked application or adapter callbacks" )
}
var output bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "empty" , "--profile" , "default" , "--campaign" , "spring" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || newInputCalls != 1 || newStateCalls != 1 || checkCalls != 1 || buildCalls != 1 || loadCalls != 1 {
t . Fatalf ( "captured command = %#v, callback counts input/state/check/build/load=%d/%d/%d/%d/%d" , outcome , newInputCalls , newStateCalls , checkCalls , buildCalls , loadCalls )
}
}
func TestValidationCommandPreservesPresenceAndRepeatedAssignmentsPerInvocation ( t * testing . T ) {
type input struct { campaign commandkit . Supplied [ string ] }
type state struct { sequence int }
campaignFlag := commandkit . ValueFlag ( commandkit . FlagMeta { Name : "campaign" } , ( * string ) ( nil ) , parseDefinitionString ,
func ( target * input , value commandkit . Supplied [ string ] ) { target . campaign = value } )
var requests [ ] ValidationRequest
var selectedInputs [ ] input
newInputCalls , checkCalls , newStateCalls := 0 , 0 , 0
suite := Suite [ input , state ] {
NewInput : func ( ) input { newInputCalls ++ ; return input { } } ,
NewState : func ( ) state { newStateCalls ++ ; return state { sequence : newStateCalls } } ,
Selectors : [ ] commandkit . Flag [ input ] { campaignFlag } ,
CheckInput : func ( ckconfig . Selection , input ) error { checkCalls ++ ; return nil } ,
ValidateRequest : func ( _ context . Context , request ValidationRequest , value input , _ state ) ( commandkit . Acquired [ struct { } ] , error ) {
requests = append ( requests , request )
selectedInputs = append ( selectedInputs , value )
return commandkit . Acquired [ struct { } ] { } , nil
} ,
}
app := newPromptTestApp ( t , Command ( suite ) )
firstArgs := [ ] string {
"promptkit" , "requirements" , "validate" , "briefing" , "--config" , "" , "--overlay" , "" ,
2026-10-17 18:39:38 +00:00
"--profile" , "" , "--session-id" , "" , "--campaign" , "spring" ,
2026-10-12 22:18:50 +00:00
"--var" , "=" , "--var" , "phrase= two words, three " , "--var" , "duplicate=first" , "--var" , "duplicate=last=value" ,
"--input" , "=" , "--input" , "file= path, with commas=and=equals " , "--input" , "duplicate=first.txt" , "--input" , "duplicate=last=path.txt" ,
}
outcome := app . Execute ( context . Background ( ) , firstArgs , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || len ( requests ) != 1 {
t . Fatalf ( "first repeated assignment invocation = %#v, requests=%d" , outcome , len ( requests ) )
}
first := requests [ 0 ]
if first . Selection . Config . Value != "" || ! first . Selection . Config . Set || first . Selection . Overlay . Value != "" || ! first . Selection . Overlay . Set {
t . Errorf ( "explicit empty selection = %#v" , first . Selection )
}
2026-10-17 18:39:38 +00:00
if first . Request . PromptID != "briefing" || first . Request . ProfileID != "" || first . Request . SessionID != "" {
2026-10-12 22:18:50 +00:00
t . Errorf ( "empty scalar values = %#v" , first . Request )
}
wantVars := map [ string ] string { "" : "" , "phrase" : " two words, three " , "duplicate" : "last=value" }
if ! reflect . DeepEqual ( first . Request . Vars , wantVars ) {
t . Errorf ( "request vars = %#v, want %#v" , first . Request . Vars , wantVars )
}
wantInputs := map [ string ] promptkit . ArtifactRef {
"" : promptkit . File ( "" ) ,
"file" : promptkit . File ( " path, with commas=and=equals " ) ,
"duplicate" : promptkit . File ( "last=path.txt" ) ,
}
if ! reflect . DeepEqual ( first . Request . Inputs , wantInputs ) {
t . Errorf ( "request inputs = %#v, want %#v" , first . Request . Inputs , wantInputs )
}
if ! selectedInputs [ 0 ] . campaign . Set || selectedInputs [ 0 ] . campaign . Value != "spring" {
t . Errorf ( "first selector input = %#v" , selectedInputs [ 0 ] )
}
outcome = app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" } , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || len ( requests ) != 2 {
t . Fatalf ( "second invocation = %#v, requests=%d" , outcome , len ( requests ) )
}
second := requests [ 1 ]
2026-10-17 18:39:38 +00:00
if second . Selection . Config . Set || second . Selection . Overlay . Set || second . Request . PromptID != "briefing" || second . Request . ProfileID != "" || second . Request . SessionID != "" || len ( second . Request . Vars ) != 0 || len ( second . Request . Inputs ) != 0 {
2026-10-12 22:18:50 +00:00
t . Errorf ( "first invocation values leaked into second request: %#v" , second )
}
if selectedInputs [ 1 ] . campaign . Set || selectedInputs [ 1 ] . campaign . Value != "" {
t . Errorf ( "first selector leaked into second input: %#v" , selectedInputs [ 1 ] )
}
if newInputCalls != 2 || checkCalls != 2 || newStateCalls != 2 {
t . Errorf ( "per-invocation counts input/check/state = %d/%d/%d" , newInputCalls , checkCalls , newStateCalls )
}
}
func TestValidationCommandUsageAndInputFailuresStopProviderWork ( t * testing . T ) {
type input struct { campaign commandkit . Supplied [ string ] }
selector := commandkit . ValueFlag ( commandkit . FlagMeta { Name : "campaign" , Required : true } , ( * string ) ( nil ) , parseDefinitionString ,
func ( target * input , value commandkit . Supplied [ string ] ) { target . campaign = value } )
for _ , test := range [ ] struct {
name string
args [ ] string
} {
{ name : "missing operand" , args : [ ] string { "promptkit" , "requirements" , "validate" } } ,
{ name : "extra operand" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "extra" , "--campaign" , "spring" } } ,
{ name : "unsupported format" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--campaign" , "spring" , "--format" , "text" } } ,
{ name : "unknown flag" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--campaign" , "spring" , "--unknown" , "value" } } ,
2026-10-17 18:39:38 +00:00
{ name : "removed prompt version flag" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--campaign" , "spring" , "--version" , "2" } } ,
2026-10-12 22:18:50 +00:00
{ name : "malformed variable before valid assignment" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--campaign" , "spring" , "--var" , "broken" , "--var" , "mode=full" } } ,
{ name : "malformed input before valid assignment" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--campaign" , "spring" , "--input" , "broken" , "--input" , "notes=file.txt" } } ,
{ name : "missing required selector" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" } } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
newInputCalls , checkCalls , newStateCalls , providerCalls := 0 , 0 , 0 , 0
suite := Suite [ input , struct { } ] {
NewInput : func ( ) input { newInputCalls ++ ; return input { } } ,
NewState : func ( ) struct { } { newStateCalls ++ ; return struct { } { } } ,
Selectors : [ ] commandkit . Flag [ input ] { selector } ,
CheckInput : func ( ckconfig . Selection , input ) error { checkCalls ++ ; return nil } ,
ValidateRequest : func ( context . Context , ValidationRequest , input , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
providerCalls ++
return commandkit . Acquired [ struct { } ] { } , nil
} ,
}
outcome := newPromptTestApp ( t , Command ( suite ) ) . Execute ( context . Background ( ) , test . args , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
2026-10-12 23:42:18 +00:00
if outcome . Kind != commandkit . OutcomeUsage || outcome . Status != 2 || checkCalls != 0 || newStateCalls != 0 || providerCalls != 0 {
2026-10-12 22:18:50 +00:00
t . Fatalf ( "preflight failure = %#v, input/check/state/provider=%d/%d/%d/%d" , outcome , newInputCalls , checkCalls , newStateCalls , providerCalls )
}
2026-10-12 23:42:18 +00:00
if test . name != "missing required selector" && newInputCalls != 0 {
t . Fatalf ( "syntax or format failure invoked input factory %d times" , newInputCalls )
}
2026-10-12 22:18:50 +00:00
} )
}
newStateCalls , providerCalls := 0 , 0
suite := Suite [ input , struct { } ] {
NewInput : func ( ) input { return input { } } ,
NewState : func ( ) struct { } { newStateCalls ++ ; return struct { } { } } ,
Selectors : [ ] commandkit . Flag [ input ] { selector } ,
CheckInput : func ( ckconfig . Selection , input ) error { return errors . New ( "input rejected" ) } ,
ValidateRequest : func ( context . Context , ValidationRequest , input , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
providerCalls ++
return commandkit . Acquired [ struct { } ] { } , nil
} ,
}
outcome := newPromptTestApp ( t , Command ( suite ) ) . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--campaign" , "spring" } , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeUsage || newStateCalls != 0 || providerCalls != 0 {
t . Fatalf ( "input check failure reached state/provider: %#v state=%d provider=%d" , outcome , newStateCalls , providerCalls )
}
}
func TestValidationCommandSuppliesZeroValueFactoriesIncludingNilPointers ( t * testing . T ) {
var gotInput * int
var gotState * int
suite := Suite [ * int , * int ] { ValidateRequest : func ( _ context . Context , _ ValidationRequest , input , state * int ) ( commandkit . Acquired [ struct { } ] , error ) {
gotInput , gotState = input , state
return commandkit . Acquired [ struct { } ] { } , nil
} }
app := newPromptTestApp ( t , Command ( suite ) )
outcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" } , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || gotInput != nil || gotState != nil {
t . Fatalf ( "omitted factories = outcome %#v, input=%v state=%v" , outcome , gotInput , gotState )
}
}
2026-10-12 22:20:07 +00:00
func TestValidationCommandUsesNativeOutcomesAndMinimalConfirmation ( t * testing . T ) {
client := & validationFailOnGenerate { }
engine := validationTestEngine ( t , adapterPrompt , promptkit . WithLLMClient ( client ) )
loadCalls := 0
suite := WithRequestValidation ( Suite [ struct { } , struct { } ] { } , func ( context . Context , ckconfig . Selection , struct { } , struct { } ) ( * promptkit . Engine , error ) {
loadCalls ++
return engine , nil
} , nil )
app := newPromptTestApp ( t , Command ( suite ) )
var output bytes . Buffer
outcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "empty" , "--profile" , "default" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeSuccess || outcome . Status != 0 {
t . Fatalf ( "native request validation = %#v, output=%q" , outcome , output . String ( ) )
}
var report map [ string ] any
if err := yaml . Unmarshal ( output . Bytes ( ) , & report ) ; err != nil {
t . Fatalf ( "decode native confirmation %q: %v" , output . String ( ) , err )
}
if len ( report ) != 2 || report [ "prompt_id" ] != "empty" || report [ "valid" ] != true {
t . Fatalf ( "native confirmation contains unexpected data: %#v" , report )
}
for _ , test := range [ ] struct {
name string
args [ ] string
want error
} {
{ name : "missing required input" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--profile" , "default" } , want : promptkit . ErrPromptRender } ,
{ name : "invalid profile selection" , args : [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--profile" , "missing" , "--input" , "topic=unused.txt" } , want : promptkit . ErrProfileNotFound } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
output . Reset ( )
outcome := app . Execute ( context . Background ( ) , test . args , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeOperational || outcome . Status != 1 || ! errors . Is ( outcome . Cause , test . want ) || output . Len ( ) != 0 {
t . Fatalf ( "native failure = %#v, output=%q, want %v" , outcome , output . String ( ) , test . want )
}
} )
}
if client . calls != 0 || loadCalls != 3 {
t . Fatalf ( "model calls=%d, engine loads=%d" , client . calls , loadCalls )
}
}
func TestValidationCommandRetainsProviderDiagnosticsAndClassifiesFailures ( t * testing . T ) {
2026-10-26 03:35:39 +00:00
warning := commandkit . MessageDiagnostic ( commandkit . SeverityWarning , "validation warning" )
2026-10-12 22:20:07 +00:00
var diagnostics bytes . Buffer
app := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
return commandkit . Acquired [ struct { } ] { Diagnostics : [ ] commandkit . Diagnostic { warning } } , nil
} } ) )
2026-10-26 03:35:39 +00:00
outcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--verbose" } , commandkit . Streams { Out : io . Discard , Err : & diagnostics } )
if outcome . Kind != commandkit . OutcomeSuccess || outcome . Status != 0 || outcome . Presentation . Diagnostics . State != commandkit . DeliveryComplete || ! strings . Contains ( diagnostics . String ( ) , "validation warning" ) {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "warning-only success = %#v, stderr=%q" , outcome , diagnostics . String ( ) )
}
2026-10-26 03:35:39 +00:00
var quietReport , quietDiagnostics bytes . Buffer
quietOutcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--quiet" } , commandkit . Streams { Out : & quietReport , Err : & quietDiagnostics } )
if quietOutcome . Kind != commandkit . OutcomeSuccess || quietOutcome . Presentation . Output . State != commandkit . DeliverySuppressed || quietOutcome . Presentation . Diagnostics . State != commandkit . DeliverySuppressed || quietReport . Len ( ) != 0 || quietDiagnostics . Len ( ) != 0 {
t . Fatalf ( "quiet validation = %#v, report=%q diagnostics=%q" , quietOutcome , quietReport . String ( ) , quietDiagnostics . String ( ) )
}
2026-10-12 22:20:07 +00:00
providerErr := errors . New ( "validation provider failed" )
failedApp := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
return commandkit . Acquired [ struct { } ] { Diagnostics : [ ] commandkit . Diagnostic { warning } } , providerErr
} } ) )
diagnostics . Reset ( )
2026-10-26 03:35:39 +00:00
outcome = failedApp . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--verbose" } , commandkit . Streams { Out : io . Discard , Err : & diagnostics } )
if outcome . Kind != commandkit . OutcomeOperational || outcome . Status != 1 || ! errors . Is ( outcome . Cause , providerErr ) || outcome . Presentation . Diagnostics . State != commandkit . DeliveryComplete {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "provider failure = %#v, stderr=%q" , outcome , diagnostics . String ( ) )
}
2026-10-26 03:35:39 +00:00
if strings . Count ( diagnostics . String ( ) , "validation warning" ) != 1 || strings . Contains ( diagnostics . String ( ) , providerErr . Error ( ) ) || ! strings . Contains ( diagnostics . String ( ) , "Unable to validate the PromptKit request" ) {
t . Fatalf ( "provider warning or terminal explanation was incorrect: stderr=%q" , diagnostics . String ( ) )
2026-10-12 23:43:16 +00:00
}
2026-10-12 22:20:07 +00:00
builderErr := errors . New ( "request builder failed" )
loaderCalls := 0
builderApp := newPromptTestApp ( t , Command ( WithRequestValidation ( Suite [ struct { } , struct { } ] { } , func ( context . Context , ckconfig . Selection , struct { } , struct { } ) ( * promptkit . Engine , error ) {
loaderCalls ++
return nil , nil
} , func ( context . Context , ValidationRequest , struct { } , struct { } ) ( promptkit . RunRequest , error ) {
return promptkit . RunRequest { } , builderErr
} ) ) )
outcome = builderApp . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" } , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeOperational || outcome . Status != 1 || ! errors . Is ( outcome . Cause , builderErr ) || loaderCalls != 0 {
t . Fatalf ( "builder failure = %#v, loader calls=%d" , outcome , loaderCalls )
}
_ , loadErr := configkit . Load [ map [ string ] any ] ( configkit . Bytes ( "validation command load failure" , [ ] byte ( "broken: [" ) ) )
if loadErr == nil {
t . Fatal ( "invalid configuration unexpectedly loaded" )
}
loadApp := newPromptTestApp ( t , Command ( WithRequestValidation ( Suite [ struct { } , struct { } ] { } , func ( context . Context , ckconfig . Selection , struct { } , struct { } ) ( * promptkit . Engine , error ) {
return nil , loadErr
} , nil ) ) )
outcome = loadApp . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" } , commandkit . Streams { Out : io . Discard , Err : io . Discard } )
var retained * configkit . LoadError
2026-10-26 03:35:39 +00:00
if outcome . Kind != commandkit . OutcomeOperational || outcome . Status != 1 || ! errors . Is ( outcome . Cause , loadErr ) || ! errors . As ( outcome . Cause , & retained ) || retained != loadErr || outcome . Explanation . Message == "" && len ( outcome . Explanation . Details ) == 0 {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "loader failure = %#v, want retained ConfigKit error and diagnostics" , outcome )
}
}
func TestValidationCommandCancellationPreventsAcquisitionAndReport ( t * testing . T ) {
2026-10-26 03:35:39 +00:00
warning := commandkit . MessageDiagnostic ( commandkit . SeverityWarning , "validation warning" )
2026-10-12 22:20:07 +00:00
calls := 0
app := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
calls ++
return commandkit . Acquired [ struct { } ] { } , nil
} } ) )
ctx , cancel := context . WithCancel ( context . Background ( ) )
cancel ( )
var output bytes . Buffer
outcome := app . Execute ( ctx , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" } , commandkit . Streams { Out : & output , Err : io . Discard } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Cause , context . Canceled ) || calls != 0 || output . Len ( ) != 0 {
t . Fatalf ( "pre-acquisition cancellation = %#v, calls=%d, output=%q" , outcome , calls , output . String ( ) )
}
ctx , cancel = context . WithCancel ( context . Background ( ) )
canceledApp := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
calls ++
cancel ( )
return commandkit . Acquired [ struct { } ] { Diagnostics : [ ] commandkit . Diagnostic { warning } } , nil
} } ) )
output . Reset ( )
2026-10-26 03:35:39 +00:00
var diagnostics bytes . Buffer
outcome = canceledApp . Execute ( ctx , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--verbose" } , commandkit . Streams { Out : & output , Err : & diagnostics } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Cause , context . Canceled ) || ! strings . Contains ( diagnostics . String ( ) , "validation warning" ) || output . Len ( ) != 0 {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "post-acquisition cancellation = %#v, output=%q" , outcome , output . String ( ) )
}
providerErr := errors . New ( "provider error before cancellation" )
ctx , cancel = context . WithCancel ( context . Background ( ) )
errorApp := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
cancel ( )
return commandkit . Acquired [ struct { } ] { Diagnostics : [ ] commandkit . Diagnostic { warning } } , providerErr
} } ) )
2026-10-26 03:35:39 +00:00
var errorDiagnostics bytes . Buffer
outcome = errorApp . Execute ( ctx , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--verbose" } , commandkit . Streams { Out : io . Discard , Err : & errorDiagnostics } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Cause , providerErr ) || ! strings . Contains ( errorDiagnostics . String ( ) , "validation warning" ) {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "provider error after cancellation = %#v" , outcome )
}
}
func TestValidationCommandPreservesReportAndDiagnosticDeliveryFailures ( t * testing . T ) {
warning := commandkit . Diagnostic { Severity : commandkit . SeverityWarning , Write : func ( writer io . Writer ) error {
_ , err := io . WriteString ( writer , "validation warning\n" )
return err
} }
app := newPromptTestApp ( t , Command ( Suite [ struct { } , struct { } ] { ValidateRequest : func ( context . Context , ValidationRequest , struct { } , struct { } ) ( commandkit . Acquired [ struct { } ] , error ) {
return commandkit . Acquired [ struct { } ] { Diagnostics : [ ] commandkit . Diagnostic { warning } } , nil
} } ) )
writeErr := errors . New ( "validation report writer failed" )
for _ , test := range [ ] struct {
name string
writer io . Writer
want error
} {
{ name : "writer error" , writer : partialReportWriter { err : writeErr } , want : writeErr } ,
{ name : "short write" , writer : partialReportWriter { } , want : io . ErrShortWrite } ,
} {
t . Run ( test . name , func ( t * testing . T ) {
var diagnosticOutput bytes . Buffer
2026-10-26 03:35:39 +00:00
outcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--verbose" } , commandkit . Streams { Out : test . writer , Err : & diagnosticOutput } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Presentation . Output . WriteError , test . want ) || outcome . Presentation . Output . State != commandkit . DeliveryIncomplete || outcome . Presentation . Diagnostics . State != commandkit . DeliveryComplete || ! strings . Contains ( diagnosticOutput . String ( ) , "validation warning" ) {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "report delivery = %#v, diagnostics=%q" , outcome , diagnosticOutput . String ( ) )
}
} )
}
diagnosticErr := errors . New ( "validation diagnostic writer failed" )
2026-10-26 03:35:39 +00:00
outcome := app . Execute ( context . Background ( ) , [ ] string { "promptkit" , "requirements" , "validate" , "briefing" , "--verbose" } , commandkit . Streams { Out : io . Discard , Err : partialReportWriter { err : diagnosticErr } } )
if outcome . Kind != commandkit . OutcomeOperational || ! errors . Is ( outcome . Presentation . Diagnostics . WriteError , diagnosticErr ) || outcome . Presentation . Diagnostics . State != commandkit . DeliveryIncomplete {
2026-10-12 22:20:07 +00:00
t . Fatalf ( "diagnostic delivery = %#v" , outcome )
}
}