Verify JSON and YAML resource inspection rendering
This commit is contained in:
@@ -408,6 +408,10 @@ do not replace valid execution-result fields with the same name.
|
||||
|
||||
## 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.
|
||||
|
||||
1. Inspect real configured resources, then encode the returned values directly
|
||||
|
||||
2
go.mod
2
go.mod
@@ -5,12 +5,12 @@ go 1.27.0
|
||||
require (
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
|
||||
go.maximumdirect.net/configkit v1.1.0
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/yuin/goldmark/v2 v2.1.6 // 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/text v0.14.0 // indirect
|
||||
)
|
||||
|
||||
121
resource_serialization_test.go
Normal file
121
resource_serialization_test.go
Normal 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")
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user