Files
promptkit/resource_isolation_test.go

102 lines
5.2 KiB
Go
Raw Permalink Normal View History

package promptkit_test
import (
"context"
"strings"
"sync"
"testing"
"go.maximumdirect.net/configkit"
"go.maximumdirect.net/promptkit"
)
func TestResourceInspectionIsolationAndNullInheritance(t *testing.T) {
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "unexpected"}}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{Source: configkit.Mapping("resources", map[string]any{
"backends": []any{map[string]any{"id": "local", "endpoint": "https://backend.test/v1", "api_key_env": "UNSET_INSPECTION_KEY", "extra_params": map[string]any{"nested": map[string]any{"array": []any{"captured"}}}, "smoke_tests": map[string]any{"default_profile": "missing", "reachability": map[string]any{}}}},
"profiles": []any{map[string]any{"id": "parent", "backend": "local", "endpoint": "https://parent.test/v1", "extra_params": map[string]any{"parent": true}, "model": "model"}, map[string]any{"id": "child", "extends": map[string]any{"id": "parent"}, "endpoint": nil, "extra_params": nil, "api_key_env": nil}, map[string]any{"id": "partial"}},
"prompts": []any{map[string]any{"id": "reference", "default_profile": "missing", "messages": []any{map[string]any{"role": "user", "content": "{{.missing}}"}}, "output": map[string]any{"format": "json", "validation_mode": "json_schema", "schema_path": "missing.json"}}, map[string]any{"id": "ready", "inputs": []any{map[string]any{"name": "x", "required": false}}, "messages": []any{map[string]any{"role": "user", "content": "hello", "cache_control": map[string]any{"type": "ephemeral", "ttl": ""}}}, "output": map[string]any{"format": "text", "validation_mode": "none"}}},
})}}}, promptkit.WithLLMClient(client), promptkit.WithBundledOpenRouter(false))
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
if got, err := engine.InspectPrompt(ctx, "reference"); err != nil || got.DefaultProfileID == nil || *got.DefaultProfileID != "missing" || got.OutputContract.SchemaPath == nil || *got.OutputContract.SchemaPath != "missing.json" || got.Messages[0].Content != "{{.missing}}" {
t.Fatalf("reference = %+v, %v", got, err)
}
if got, err := engine.InspectProfile(ctx, "partial"); err != nil || got.Endpoint != nil || got.Model != nil {
t.Fatalf("partial = %+v, %v", got, err)
}
profile, err := engine.InspectProfile(ctx, "child")
if err != nil || profile.Endpoint != nil || profile.ExtraParams != nil || profile.APIKeyEnv != nil {
t.Fatalf("child = %+v, %v", profile, err)
}
view, err := engine.InspectConfiguration()
if err != nil {
t.Fatal(err)
}
for _, field := range []string{"endpoint", "extra_params", "api_key_env"} {
path := configkit.Keys("profiles").Index(1).Key(field)
explanation, ok := view.Explain(path)
if !ok || !strings.Contains(explanation.Effective.YAML, "null") || len(explanation.History) == 0 {
t.Errorf("%s provenance = %+v, %t", field, explanation, ok)
}
}
backend, err := engine.InspectBackend(ctx, "local")
if err != nil {
t.Fatal(err)
}
if backend.SmokeTests == nil || backend.SmokeTests.DefaultProfile == nil || *backend.SmokeTests.DefaultProfile != "missing" {
t.Fatalf("diagnostics = %+v", backend)
}
*backend.APIKeyEnv = "MUTATED"
*backend.SmokeTests.DefaultProfile = "mutated"
(*backend.ExtraParams)["nested"].(map[string]any)["array"].([]any)[0] = "mutated"
*profile.Model = "mutated"
prompt, err := engine.InspectPrompt(ctx, "ready")
if err != nil {
t.Fatal(err)
}
(*prompt.Inputs)[0].Name = "changed"
*(*prompt.Inputs)[0].Required = true
prompt.Messages[0].Content = "changed"
*prompt.Messages[0].CacheControl.TTL = "changed"
again, err := engine.InspectBackend(ctx, "local")
if err != nil || *again.APIKeyEnv != "UNSET_INSPECTION_KEY" || *again.SmokeTests.DefaultProfile != "missing" || (*again.ExtraParams)["nested"].(map[string]any)["array"].([]any)[0] != "captured" {
t.Fatalf("backend mutation leaked: %+v, %v", again, err)
}
againPrompt, err := engine.InspectPrompt(ctx, "ready")
if err != nil || (*againPrompt.Inputs)[0].Name != "x" || *(*againPrompt.Inputs)[0].Required || againPrompt.Messages[0].Content != "hello" || *againPrompt.Messages[0].CacheControl.TTL != "" {
t.Fatalf("prompt mutation leaked: %+v, %v", againPrompt, err)
}
prepared, err := engine.Prepare(ctx, promptkit.RunRequest{PromptID: "ready", ProfileID: "child"})
if err != nil || prepared.EffectiveModelParams.Endpoint != "https://backend.test/v1" || prepared.EffectiveModelParams.APIKeyEnv != "UNSET_INSPECTION_KEY" || prepared.EffectiveModelParams.Model != "model" || prepared.EffectiveModelParams.ExtraParams["nested"].(map[string]any)["array"].([]any)[0] != "captured" {
t.Fatalf("prepared = %+v, %v", prepared, err)
}
if len(client.requests) != 0 {
t.Fatal("inspection invoked provider")
}
var wg sync.WaitGroup
for i := 0; i < 12; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 20; j++ {
b, err := engine.InspectBackend(ctx, "local")
if err != nil {
t.Error(err)
return
}
(*b.ExtraParams)["nested"].(map[string]any)["array"].([]any)[0] = "local"
p, err := engine.InspectPrompt(ctx, "ready")
if err != nil {
t.Error(err)
return
}
p.Messages[0].Content = "local"
}
}()
}
wg.Wait()
}