package ckprompt import ( "bytes" "context" "io" "reflect" "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" ) 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", "--version", "v2", "--profile", "editorial", "--session-id", "session-1", "--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) } if received.Selection != wantSelection || received.Request.PromptID != "briefing" || received.Request.PromptVersion != "v2" || received.Request.ProfileID != "editorial" || received.Request.SessionID != "session-1" { 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) } }