Verify PromptKit requirements command behavior
This commit is contained in:
465
integrations/ckprompt/requirements_command_test.go
Normal file
465
integrations/ckprompt/requirements_command_test.go
Normal file
@@ -0,0 +1,465 @@
|
||||
package ckprompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
||||
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
|
||||
promptkit "gitea.maximumdirect.net/eric/promptkit"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
const requirementsCommandPrompt = `prompts:
|
||||
briefing:
|
||||
"2":
|
||||
default_profile: editorial
|
||||
session_id: '{{input "session-only"}} {{.session_variable}}'
|
||||
messages:
|
||||
- role: user
|
||||
content: '{{if .mode}}{{input "notes"}}{{end}}'
|
||||
output:
|
||||
format: text
|
||||
validation_mode: none
|
||||
required-variable:
|
||||
"1":
|
||||
default_profile: editorial
|
||||
messages:
|
||||
- role: user
|
||||
content: '{{.required}}'
|
||||
output:
|
||||
format: text
|
||||
validation_mode: none
|
||||
ambiguous:
|
||||
"1":
|
||||
default_profile: editorial
|
||||
messages:
|
||||
- role: user
|
||||
content: "First version"
|
||||
output:
|
||||
format: text
|
||||
validation_mode: none
|
||||
"2":
|
||||
default_profile: editorial
|
||||
messages:
|
||||
- role: user
|
||||
content: "Second version"
|
||||
output:
|
||||
format: text
|
||||
validation_mode: none
|
||||
backends:
|
||||
local:
|
||||
endpoint: "https://example.invalid/v1"
|
||||
profiles:
|
||||
editorial:
|
||||
backend: local
|
||||
model: example
|
||||
api_key_required: true
|
||||
`
|
||||
|
||||
func requirementsCommandEngine(t *testing.T) *promptkit.Engine {
|
||||
t.Helper()
|
||||
return adapterEngineFrom(t, "requirements command fixture", requirementsCommandPrompt)
|
||||
}
|
||||
|
||||
func executeRequirementsAgainstNative(t *testing.T, engine *promptkit.Engine, request promptkit.RequirementInspectionRequest, args []string) (*promptkit.RequirementInspection, commandkit.Outcome, map[string]any, string) {
|
||||
t.Helper()
|
||||
|
||||
native, err := engine.InspectRequirements(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("direct native inspection: %v", err)
|
||||
}
|
||||
expectedBytes, err := encodePromptYAMLBytes(requirementsReportFromInspection(native))
|
||||
if err != nil {
|
||||
t.Fatalf("encode direct native report: %v", err)
|
||||
}
|
||||
var expected map[string]any
|
||||
if err := yaml.Unmarshal(expectedBytes, &expected); err != nil {
|
||||
t.Fatalf("decode direct native report %q: %v", expectedBytes, err)
|
||||
}
|
||||
|
||||
app := newPromptTestApp(t, Command(FromEngine[int, int](func(context.Context, ckconfig.Selection, int, int) (*promptkit.Engine, error) {
|
||||
return engine, nil
|
||||
})))
|
||||
var output, diagnostic bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: &diagnostic})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || outcome.Status != 0 {
|
||||
t.Fatalf("requirements command outcome = %#v, report=%q diagnostics=%q", outcome, output.String(), diagnostic.String())
|
||||
}
|
||||
var actual map[string]any
|
||||
if err := yaml.Unmarshal(output.Bytes(), &actual); err != nil {
|
||||
t.Fatalf("decode command report %q: %v", output.Bytes(), err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("command report differs from native inspection:\n got: %#v\nwant: %#v", actual, expected)
|
||||
}
|
||||
return native, outcome, actual, diagnostic.String()
|
||||
}
|
||||
|
||||
func TestRequirementsCommandMatchesNativePromptKitDiscovery(t *testing.T) {
|
||||
engine := requirementsCommandEngine(t)
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
request promptkit.RequirementInspectionRequest
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
name: "partial conditional discovery",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "briefing", PromptVersion: "2", ProfileID: "editorial"},
|
||||
args: []string{"promptkit", "requirements", "show", "briefing", "--version", "2", "--profile", "editorial"},
|
||||
},
|
||||
{
|
||||
name: "supplied branch variable",
|
||||
request: promptkit.RequirementInspectionRequest{
|
||||
PromptID: "briefing", PromptVersion: "2", ProfileID: "editorial", Vars: map[string]string{"mode": "full"},
|
||||
},
|
||||
args: []string{"promptkit", "requirements", "show", "briefing", "--version", "2", "--profile", "editorial", "--var", "mode=full", "--format", "yaml"},
|
||||
},
|
||||
{
|
||||
name: "complete with an unsupplied required variable",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "required-variable", PromptVersion: "1", ProfileID: "editorial"},
|
||||
args: []string{"promptkit", "requirements", "show", "required-variable", "--version", "1", "--profile", "editorial"},
|
||||
},
|
||||
{
|
||||
name: "native version and default profile selection",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "briefing"},
|
||||
args: []string{"promptkit", "requirements", "show", "briefing"},
|
||||
},
|
||||
{
|
||||
name: "session template discovery",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "briefing", ProfileID: "editorial"},
|
||||
args: []string{"promptkit", "requirements", "show", "briefing", "--profile", "editorial"},
|
||||
},
|
||||
{
|
||||
name: "session ID bypasses the session template",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "briefing", ProfileID: "editorial", SessionID: "session-42"},
|
||||
args: []string{"promptkit", "requirements", "show", "briefing", "--profile", "editorial", "--session-id", "session-42"},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
native, outcome, _, _ := executeRequirementsAgainstNative(t, engine, test.request, test.args)
|
||||
if outcome.Status != 0 {
|
||||
t.Fatalf("partial or complete inspection returned status %d", outcome.Status)
|
||||
}
|
||||
switch test.name {
|
||||
case "partial conditional discovery":
|
||||
if native.Complete || len(native.Issues) == 0 || len(native.Inputs) == 0 {
|
||||
t.Fatalf("partial discovery lost useful evidence: %#v", native)
|
||||
}
|
||||
case "supplied branch variable":
|
||||
if !native.Complete || len(native.Issues) != 0 {
|
||||
t.Fatalf("resolved discovery = %#v", native)
|
||||
}
|
||||
case "complete with an unsupplied required variable":
|
||||
if !native.Complete || len(native.Variables) != 1 || native.Variables[0].Supplied {
|
||||
t.Fatalf("complete discovery with unsupplied variable = %#v", native)
|
||||
}
|
||||
case "native version and default profile selection":
|
||||
if native.PromptVersion != "2" || native.ProfileID != "editorial" {
|
||||
t.Fatalf("native default selection = %#v", native)
|
||||
}
|
||||
case "session template discovery":
|
||||
foundInput, foundVariable := false, false
|
||||
for _, input := range native.Inputs {
|
||||
foundInput = foundInput || input.Name == "session-only"
|
||||
}
|
||||
for _, variable := range native.Variables {
|
||||
foundVariable = foundVariable || variable.Name == "session_variable"
|
||||
}
|
||||
if !foundInput || !foundVariable {
|
||||
t.Fatalf("session template dependencies missing: %#v", native)
|
||||
}
|
||||
case "session ID bypasses the session template":
|
||||
for _, input := range native.Inputs {
|
||||
if input.Name == "session-only" {
|
||||
t.Fatalf("session template input remained with session ID: %#v", native.Inputs)
|
||||
}
|
||||
}
|
||||
for _, variable := range native.Variables {
|
||||
if variable.Name == "session_variable" {
|
||||
t.Fatalf("session template variable remained with session ID: %#v", native.Variables)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequirementsCommandPreservesNativeSelectionErrors(t *testing.T) {
|
||||
engine := requirementsCommandEngine(t)
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
request promptkit.RequirementInspectionRequest
|
||||
args []string
|
||||
cause error
|
||||
}{
|
||||
{
|
||||
name: "missing prompt",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "missing", PromptVersion: "2", ProfileID: "editorial"},
|
||||
args: []string{"promptkit", "requirements", "show", "missing", "--version", "2", "--profile", "editorial"},
|
||||
cause: promptkit.ErrPromptNotFound,
|
||||
},
|
||||
{
|
||||
name: "missing profile",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "briefing", PromptVersion: "2", ProfileID: "missing"},
|
||||
args: []string{"promptkit", "requirements", "show", "briefing", "--version", "2", "--profile", "missing"},
|
||||
cause: promptkit.ErrProfileNotFound,
|
||||
},
|
||||
{
|
||||
name: "ambiguous prompt version",
|
||||
request: promptkit.RequirementInspectionRequest{PromptID: "ambiguous", ProfileID: "editorial"},
|
||||
args: []string{"promptkit", "requirements", "show", "ambiguous", "--profile", "editorial"},
|
||||
cause: promptkit.ErrPromptLoad,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, nativeErr := engine.InspectRequirements(context.Background(), test.request)
|
||||
if !errors.Is(nativeErr, test.cause) {
|
||||
t.Fatalf("native selection error = %v, want %v", nativeErr, test.cause)
|
||||
}
|
||||
app := newPromptTestApp(t, Command(FromEngine[int, int](func(context.Context, ckconfig.Selection, int, int) (*promptkit.Engine, error) {
|
||||
return engine, nil
|
||||
})))
|
||||
var output bytes.Buffer
|
||||
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.cause) || output.Len() != 0 {
|
||||
t.Fatalf("native selection outcome = %#v output=%q", outcome, output.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequirementsCommandKeepsLifecycleAndCapturedDeclaration(t *testing.T) {
|
||||
type input struct{ campaign commandkit.Supplied[string] }
|
||||
type state struct{ calls int }
|
||||
|
||||
newCampaignFlag := func(name string) commandkit.Flag[input] {
|
||||
return commandkit.ValueFlag(commandkit.FlagMeta{Name: name, Required: true}, (*string)(nil), parseDefinitionString,
|
||||
func(target *input, value commandkit.Supplied[string]) { target.campaign = value })
|
||||
}
|
||||
selectors := []commandkit.Flag[input]{newCampaignFlag("campaign")}
|
||||
callbackData := &struct{ value string }{value: "initial"}
|
||||
newInputCalls, checkInputCalls, newStateCalls, providerCalls, replacementCalls := 0, 0, 0, 0, 0
|
||||
var received []input
|
||||
suite := Suite[input, state]{
|
||||
NewInput: func() input {
|
||||
newInputCalls++
|
||||
return input{}
|
||||
},
|
||||
NewState: func() state {
|
||||
newStateCalls++
|
||||
return state{calls: newStateCalls}
|
||||
},
|
||||
Selectors: selectors,
|
||||
CheckInput: func(_ ckconfig.Selection, value input) error {
|
||||
checkInputCalls++
|
||||
if !value.campaign.Set {
|
||||
t.Errorf("selected campaign lost presence: %#v", value)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
InspectRequirements: func(_ context.Context, _ RequirementsRequest, value input, gotState state) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
providerCalls++
|
||||
received = append(received, value)
|
||||
if gotState.calls != providerCalls || callbackData.value != "updated" {
|
||||
t.Errorf("provider state or callback-owned data = %#v/%q", gotState, callbackData.value)
|
||||
}
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{PromptID: "briefing"}}, nil
|
||||
},
|
||||
}
|
||||
declaration := Command(suite)
|
||||
suite.Selectors[0] = newCampaignFlag("replacement")
|
||||
suite.InspectRequirements = func(context.Context, RequirementsRequest, input, state) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
replacementCalls++
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{}}, nil
|
||||
}
|
||||
selectors[0] = newCampaignFlag("also-replacement")
|
||||
callbackData.value = "updated"
|
||||
app := newPromptTestApp(t, declaration)
|
||||
for _, campaign := range []string{"spring", "autumn"} {
|
||||
args := []string{"promptkit", "requirements", "show", "briefing", "--campaign", campaign}
|
||||
outcome := app.Execute(context.Background(), args, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeSuccess {
|
||||
t.Fatalf("captured declaration invocation = %#v", outcome)
|
||||
}
|
||||
}
|
||||
if newInputCalls != 2 || checkInputCalls != 2 || newStateCalls != 2 || providerCalls != 2 || replacementCalls != 0 || len(received) != 2 {
|
||||
t.Fatalf("captured declaration calls = input/check/state/provider/replacement %d/%d/%d/%d/%d", newInputCalls, checkInputCalls, newStateCalls, providerCalls, replacementCalls)
|
||||
}
|
||||
if received[0].campaign.Value != "spring" || received[1].campaign.Value != "autumn" {
|
||||
t.Fatalf("captured selector values = %#v", received)
|
||||
}
|
||||
|
||||
zeroValueApp := newPromptTestApp(t, Command(Suite[*input, *state]{InspectRequirements: func(_ context.Context, _ RequirementsRequest, gotInput *input, gotState *state) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
if gotInput != nil || gotState != nil {
|
||||
t.Errorf("omitted factories allocated pointer values: %#v/%#v", gotInput, gotState)
|
||||
}
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{}}, nil
|
||||
}}))
|
||||
zeroOutcome := zeroValueApp.Execute(context.Background(), []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if zeroOutcome.Kind != commandkit.OutcomeSuccess {
|
||||
t.Fatalf("omitted-factory zero values = %#v", zeroOutcome)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequirementsCommandChecksInputAndCancellationBeforeAcquisition(t *testing.T) {
|
||||
inputErr := errors.New("requirements input rejected")
|
||||
newInputCalls, checkInputCalls, newStateCalls, providerCalls := 0, 0, 0, 0
|
||||
suite := Suite[struct{}, struct{}]{
|
||||
NewInput: func() struct{} {
|
||||
newInputCalls++
|
||||
return struct{}{}
|
||||
},
|
||||
CheckInput: func(ckconfig.Selection, struct{}) error {
|
||||
checkInputCalls++
|
||||
return inputErr
|
||||
},
|
||||
NewState: func() struct{} {
|
||||
newStateCalls++
|
||||
return struct{}{}
|
||||
},
|
||||
InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
providerCalls++
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{}}, nil
|
||||
},
|
||||
}
|
||||
app := newPromptTestApp(t, Command(suite))
|
||||
args := []string{"promptkit", "requirements", "show", "briefing"}
|
||||
inputOutcome := app.Execute(context.Background(), args, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if inputOutcome.Kind != commandkit.OutcomeUsage || !errors.Is(inputOutcome.Cause, inputErr) || newInputCalls != 1 || checkInputCalls != 1 || newStateCalls != 0 || providerCalls != 0 {
|
||||
t.Fatalf("rejected input reached state/provider: outcome=%#v counts=%d/%d/%d/%d", inputOutcome, newInputCalls, checkInputCalls, newStateCalls, providerCalls)
|
||||
}
|
||||
|
||||
suite.CheckInput = nil
|
||||
app = newPromptTestApp(t, Command(suite))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
cancelOutcome := app.Execute(ctx, args, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if cancelOutcome.Kind != commandkit.OutcomeOperational || !errors.Is(cancelOutcome.Cause, context.Canceled) || newStateCalls != 0 || providerCalls != 0 {
|
||||
t.Fatalf("pre-acquisition cancellation reached state/provider: outcome=%#v counts=%d/%d", cancelOutcome, newStateCalls, providerCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequirementsCommandPreservesDiagnosticsCancellationAndDelivery(t *testing.T) {
|
||||
diagnostic := commandkit.MessageDiagnostic(commandkit.SeverityWarning, "provider warning")
|
||||
issue := promptkit.RequirementIssue{Code: "native_issue", VariableName: "mode", Description: "native issue details"}
|
||||
|
||||
t.Run("partial inspection succeeds and keeps issues in YAML", func(t *testing.T) {
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{Issues: []promptkit.RequirementIssue{issue}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
||||
}}))
|
||||
var report, diagnostics bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: &report, Err: &diagnostics})
|
||||
var decoded map[string]any
|
||||
if err := yaml.Unmarshal(report.Bytes(), &decoded); err != nil {
|
||||
t.Fatalf("decode partial report %q: %v", report.String(), err)
|
||||
}
|
||||
if outcome.Kind != commandkit.OutcomeSuccess || outcome.Status != 0 || len(outcome.Diagnostics) != 1 || !strings.Contains(diagnostics.String(), "provider warning") || strings.Contains(diagnostics.String(), "native issue details") {
|
||||
t.Fatalf("partial inspection outcome=%#v report=%q diagnostics=%q", outcome, report.String(), diagnostics.String())
|
||||
}
|
||||
issues, ok := decoded["issues"].([]any)
|
||||
if !ok || len(issues) != 1 || !strings.Contains(report.String(), "native issue details") {
|
||||
t.Fatalf("native issue not retained in report: %q", report.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("provider failure retains diagnostic and cause", func(t *testing.T) {
|
||||
providerErr := errors.New("requirements provider failed")
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Diagnostics: []commandkit.Diagnostic{diagnostic}}, providerErr
|
||||
}}))
|
||||
var report, diagnostics bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: &report, Err: &diagnostics})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Status != 1 || !errors.Is(outcome.Cause, providerErr) || len(outcome.Diagnostics) == 0 || !strings.Contains(diagnostics.String(), "provider warning") || report.Len() != 0 {
|
||||
t.Fatalf("provider failure outcome=%#v report=%q diagnostics=%q", outcome, report.String(), diagnostics.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("cancellation after provider retains diagnostics and omits report", func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
cancel()
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{Complete: true}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
||||
}}))
|
||||
var report bytes.Buffer
|
||||
outcome := app.Execute(ctx, []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: &report, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || len(outcome.Diagnostics) == 0 || report.Len() != 0 {
|
||||
t.Fatalf("post-provider cancellation = %#v report=%q", outcome, report.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil provider result is operational failure", func(t *testing.T) {
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{}, nil
|
||||
}}))
|
||||
outcome := app.Execute(context.Background(), []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Status != 1 || outcome.Cause == nil || !strings.Contains(outcome.Cause.Error(), "returned nil") {
|
||||
t.Fatalf("nil provider result = %#v", outcome)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("report writer failure and short write preserve delivery state", func(t *testing.T) {
|
||||
writeErr := errors.New("requirements report writer failed")
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
want error
|
||||
}{
|
||||
{name: "writer error", want: writeErr},
|
||||
{name: "short write", want: io.ErrShortWrite},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
writer := &requirementsPartialWriter{err: test.want}
|
||||
if test.name == "short write" {
|
||||
writer.err = nil
|
||||
writer.short = true
|
||||
}
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{PromptID: "briefing"}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
||||
}}))
|
||||
outcome := app.Execute(context.Background(), []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: writer, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, test.want) || !errors.Is(outcome.DeliveryError, test.want) || !outcome.ReportIncomplete || writer.output.Len() == 0 {
|
||||
t.Fatalf("report delivery = %#v, partial=%q", outcome, writer.output.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("diagnostic writer failure is retained", func(t *testing.T) {
|
||||
diagnosticErr := errors.New("requirements diagnostic writer failed")
|
||||
badDiagnostic := commandkit.Diagnostic{Severity: commandkit.SeverityWarning, Write: func(io.Writer) error { return diagnosticErr }}
|
||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: &promptkit.RequirementInspection{PromptID: "briefing"}, Diagnostics: []commandkit.Diagnostic{badDiagnostic}}, nil
|
||||
}}))
|
||||
var report bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"promptkit", "requirements", "show", "briefing"}, commandkit.Streams{Out: &report, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.DeliveryError, diagnosticErr) || outcome.DiagnosticDelivery != commandkit.DeliveryIncomplete || len(outcome.Diagnostics) == 0 || report.Len() == 0 {
|
||||
t.Fatalf("diagnostic delivery outcome = %#v report=%q", outcome, report.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type requirementsPartialWriter struct {
|
||||
output bytes.Buffer
|
||||
err error
|
||||
short bool
|
||||
}
|
||||
|
||||
func (writer *requirementsPartialWriter) Write(value []byte) (int, error) {
|
||||
if len(value) == 0 {
|
||||
return 0, writer.err
|
||||
}
|
||||
count := 1
|
||||
if count > len(value) {
|
||||
count = len(value)
|
||||
}
|
||||
_, _ = writer.output.Write(value[:count])
|
||||
if writer.short {
|
||||
return count, nil
|
||||
}
|
||||
return count, writer.err
|
||||
}
|
||||
Reference in New Issue
Block a user