Files
commandkit/integrations/ckprompt/composition_test.go

111 lines
5.0 KiB
Go

package ckprompt_test
import (
"bytes"
"context"
"io"
"testing"
"testing/fstest"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckprompt"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
promptkit "gitea.maximumdirect.net/eric/promptkit"
)
type compositionInput struct{ Campaign commandkit.Supplied[string] }
type compositionState struct{ token int }
func TestPublicConfigurationAndDefinitionFamiliesRemainIndependent(t *testing.T) {
inputs, err := promptkit.FilesystemInputs("composition fixture", fstest.MapFS{
"config/prompts.yml": &fstest.MapFile{Data: []byte(`prompts:
briefing:
"1":
messages:
- role: user
content: "Hello"
output:
format: text
validation_mode: none
backends:
local:
endpoint: "https://example.invalid/v1"
profiles:
default:
backend: local
model: example
`)}}, "config")
if err != nil {
t.Fatal(err)
}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{{Inputs: inputs}}}})
if err != nil {
t.Fatalf("construct engine: %v", err)
}
flag := commandkit.ValueFlag(commandkit.FlagMeta{Name: "campaign"}, (*string)(nil), func(value string) (string, error) { return value, nil },
func(input **compositionInput, value commandkit.Supplied[string]) { (*input).Campaign = value })
newInputCalls, newStateCalls := 0, 0
newInput := func() *compositionInput { newInputCalls++; return &compositionInput{} }
newState := func() *compositionState { newStateCalls++; return &compositionState{token: newStateCalls} }
var appInput, engineInput *compositionInput
var appState, configEngineState *compositionState
appAcquisitions, engineScopeAcquisitions, loaderCalls := 0, 0, 0
loadEngine := func(_ context.Context, _ ckconfig.Selection, input *compositionInput, state *compositionState) (*promptkit.Engine, error) {
loaderCalls++
if !input.Campaign.Set || input.Campaign.Value != "spring" {
t.Errorf("engine loader input = %#v", input)
}
if loaderCalls == 1 {
engineInput, configEngineState = input, state
}
return engine, nil
}
appScope := ckconfig.Scope[*compositionInput, *compositionState]{
Name: "app", Operations: []ckconfig.Operation{ckconfig.Validate},
Acquire: func(_ context.Context, _ ckconfig.Request, input *compositionInput, state *compositionState) (commandkit.Acquired[ckconfig.View], error) {
appAcquisitions++
appInput, appState = input, state
return commandkit.Acquired[ckconfig.View]{}, nil
},
}
engineScope := ckprompt.ConfigScope(loadEngine)
acquireEngineScope := engineScope.Acquire
engineScope.Acquire = func(ctx context.Context, request ckconfig.Request, input *compositionInput, state *compositionState) (commandkit.Acquired[ckconfig.View], error) {
engineScopeAcquisitions++
return acquireEngineScope(ctx, request, input, state)
}
configCommand := ckconfig.Command(ckconfig.Suite[*compositionInput, *compositionState]{
NewInput: newInput, NewState: newState,
Selectors: []ckconfig.Selector[*compositionInput]{{Binding: flag, Scopes: []string{"app", "promptkit"}}},
Scopes: []ckconfig.Scope[*compositionInput, *compositionState]{appScope, engineScope},
})
definitions := ckprompt.FromEngine(loadEngine)
definitions.NewInput = newInput
definitions.NewState = newState
definitions.Selectors = []commandkit.Flag[*compositionInput]{flag}
command, err := commandkit.New(commandkit.Identity{Name: "composition"}, configCommand, ckprompt.Command(definitions))
if err != nil {
t.Fatal(err)
}
var output bytes.Buffer
outcome := command.Execute(context.Background(), []string{"config", "validate", "--scope", "all", "--campaign", "spring"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || appAcquisitions != 1 || engineScopeAcquisitions != 1 || loaderCalls != 1 {
t.Fatalf("configuration run = (%#v, output=%q, counts=%d/%d/%d)", outcome, output.String(), appAcquisitions, engineScopeAcquisitions, loaderCalls)
}
if appInput == nil || engineInput == nil || appInput == engineInput || appState == nil || appState != configEngineState || appState.token != 1 {
t.Fatalf("scope values were not isolated/shared as selected: inputs=%p/%p states=%p/%p", appInput, engineInput, appState, configEngineState)
}
output.Reset()
outcome = command.Execute(context.Background(), []string{"promptkit", "show", "profile", "default", "--campaign", "spring", "--format", "yaml"}, commandkit.Streams{Out: &output, Err: io.Discard})
if outcome.Kind != commandkit.OutcomeSuccess || appAcquisitions != 1 || engineScopeAcquisitions != 1 || loaderCalls != 2 || !bytes.Contains(output.Bytes(), []byte("profile_id: default")) {
t.Fatalf("independent profile run = (%#v, output=%q, counts=%d/%d/%d)", outcome, output.String(), appAcquisitions, engineScopeAcquisitions, loaderCalls)
}
if newInputCalls != 3 || newStateCalls != 2 {
t.Fatalf("invocation factory calls input/state=%d/%d", newInputCalls, newStateCalls)
}
}