Verify JSON and YAML resource inspection rendering

This commit is contained in:
2026-11-06 15:24:55 +00:00
parent 129959b24c
commit b5b9b1f209
3 changed files with 126 additions and 1 deletions

View File

@@ -408,6 +408,10 @@ do not replace valid execution-result fields with the same name.
## Stage 7: Verify Generic JSON And YAML Rendering ## Stage 7: Verify Generic JSON And YAML Rendering
**Completed:** Real resource results pass the same semantic presence, nested
provider-data, diagnostics, and omission assertions with standard JSON and YAML
marshalling. The existing YAML dependency is now a direct test dependency.
**Scope:** One focused public serialization contract suite for resource results. **Scope:** One focused public serialization contract suite for resource results.
1. Inspect real configured resources, then encode the returned values directly 1. Inspect real configured resources, then encode the returned values directly

2
go.mod
View File

@@ -5,12 +5,12 @@ go 1.27.0
require ( require (
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
go.maximumdirect.net/configkit v1.1.0 go.maximumdirect.net/configkit v1.1.0
go.yaml.in/yaml/v4 v4.0.0-rc.6
) )
require ( require (
github.com/yuin/goldmark/v2 v2.1.6 // indirect github.com/yuin/goldmark/v2 v2.1.6 // indirect
go.maximumdirect.net/releasekit v0.9.0 // indirect go.maximumdirect.net/releasekit v0.9.0 // indirect
go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect
golang.org/x/mod v0.40.0 // indirect golang.org/x/mod v0.40.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
) )

View File

@@ -0,0 +1,121 @@
package promptkit_test
import (
"context"
"encoding/json"
"fmt"
"testing"
"go.maximumdirect.net/configkit"
"go.maximumdirect.net/promptkit"
"go.yaml.in/yaml/v4"
)
func TestResourceInspectionGenericEncoding(t *testing.T) {
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://example.test/v1", "concurrency_limit": 2, "extra_params": map[string]any{"null": nil, "array": []any{nil}}, "smoke_tests": map[string]any{"reachability": map[string]any{}}}, map[string]any{"id": "plain", "endpoint": "https://plain.test/v1"}},
"profiles": []any{map[string]any{"id": "parent", "backend": "local", "model": "model"}, map[string]any{"id": "profile", "extends": map[string]any{"id": "parent"}, "temperature": 0, "api_key_required": false, "endpoint": "", "extra_params": map[string]any{}}, map[string]any{"id": "unset", "extra_params": nil}},
"prompts": []any{map[string]any{"id": "prompt", "inputs": []any{map[string]any{"name": "a", "required": false, "description": ""}, map[string]any{"name": "b"}}, "messages": []any{map[string]any{"role": "user", "content": "", "cache_control": map[string]any{"type": "ephemeral", "ttl": ""}}}, "output": map[string]any{"format": "text", "validation_mode": "basic", "repair_attempts": 0}}, map[string]any{"id": "empty-inputs", "inputs": []any{}, "messages": []any{map[string]any{"role": "user", "content": "x"}}, "output": map[string]any{"format": "text", "validation_mode": "none"}}, map[string]any{"id": "omitted-inputs", "messages": []any{map[string]any{"role": "user", "content": "x"}}, "output": map[string]any{"format": "text", "validation_mode": "none"}}},
})}}})
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
backend, err := engine.InspectBackend(ctx, "local")
if err != nil {
t.Fatal(err)
}
plain, err := engine.InspectBackend(ctx, "plain")
if err != nil {
t.Fatal(err)
}
profile, err := engine.InspectProfile(ctx, "profile")
if err != nil {
t.Fatal(err)
}
unset, err := engine.InspectProfile(ctx, "unset")
if err != nil {
t.Fatal(err)
}
prompt, err := engine.InspectPrompt(ctx, "prompt")
if err != nil {
t.Fatal(err)
}
empty, err := engine.InspectPrompt(ctx, "empty-inputs")
if err != nil {
t.Fatal(err)
}
omitted, err := engine.InspectPrompt(ctx, "omitted-inputs")
if err != nil {
t.Fatal(err)
}
for name, codec := range map[string]struct {
marshal func(any) ([]byte, error)
unmarshal func([]byte, any) error
}{
"json": {json.Marshal, json.Unmarshal}, "yaml": {yaml.Marshal, yaml.Unmarshal},
} {
t.Run(name, func(t *testing.T) {
decode := func(v any) map[string]any {
t.Helper()
data, err := codec.marshal(v)
if err != nil {
t.Fatal(err)
}
var m map[string]any
if err := codec.unmarshal(data, &m); err != nil {
t.Fatal(err)
}
return m
}
absent := func(m map[string]any, keys ...string) {
t.Helper()
for _, key := range keys {
if _, ok := m[key]; ok {
t.Errorf("unexpected %s in %#v", key, m)
}
}
}
b := decode(backend)
if b["id"] != "local" || fmt.Sprint(b["concurrency_limit"]) != "2" || fmt.Sprint(b["queue_capacity"]) != "1024" {
t.Errorf("backend = %#v", b)
}
params := b["extra_params"].(map[string]any)
if v, ok := params["null"]; !ok || v != nil || params["array"].([]any)[0] != nil {
t.Errorf("provider nulls = %#v", params)
}
smoke := b["smoke_tests"].(map[string]any)
if reach := smoke["reachability"].(map[string]any); len(reach) != 0 {
t.Errorf("reachability = %#v", reach)
}
absent(b, "extends")
absent(decode(plain), "smoke_tests", "queue_capacity", "concurrency_limit")
p := decode(profile)
if p["id"] != "profile" || p["backend"] != "local" || p["endpoint"] != "" || p["api_key_required"] != false || fmt.Sprint(p["temperature"]) != "0" || len(p["extra_params"].(map[string]any)) != 0 {
t.Errorf("profile = %#v", p)
}
absent(p, "extends", "effective_model_params", "timeout_seconds", "api_key_env")
absent(decode(unset), "extra_params", "endpoint")
q := decode(prompt)
inputs := q["inputs"].([]any)
a := inputs[0].(map[string]any)
bInput := inputs[1].(map[string]any)
if a["required"] != false || a["description"] != "" {
t.Errorf("input = %#v", a)
}
absent(bInput, "required", "description")
message := q["messages"].([]any)[0].(map[string]any)
if message["content"] != "" || message["cache_control"].(map[string]any)["ttl"] != "" {
t.Errorf("message = %#v", message)
}
absent(message, "content_file", "template")
if fmt.Sprint(q["output"].(map[string]any)["repair_attempts"]) != "0" {
t.Errorf("output = %#v", q["output"])
}
if inputs := decode(empty)["inputs"].([]any); len(inputs) != 0 {
t.Errorf("empty inputs = %#v", inputs)
}
absent(decode(omitted), "inputs")
})
}
}