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() }