815 lines
32 KiB
Go
815 lines
32 KiB
Go
package configuration
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.maximumdirect.net/configkit"
|
|
)
|
|
|
|
func mapping(label string, values map[string]any) configkit.Input {
|
|
return configkit.Input{Source: configkit.Mapping(label, values)}
|
|
}
|
|
|
|
func TestLoadIdentifiedResourceLists(t *testing.T) {
|
|
for _, header := range []bool{false, true} {
|
|
values := map[string]any{
|
|
"backends": []any{map[string]any{"id": "local", "endpoint": "https://example.invalid/v1"}},
|
|
"collections": map[string]any{"team": map[string]any{
|
|
"backends": []any{map[string]any{"id": "local", "endpoint": "https://example.invalid/v1"}},
|
|
}},
|
|
}
|
|
if header {
|
|
values["version"] = "2"
|
|
}
|
|
loaded, err := load(mapping("identified resources", values), nil)
|
|
if err != nil {
|
|
t.Fatalf("load identified resources with header %t: %v", header, err)
|
|
}
|
|
if loaded.result.Config.Backends[0].ID != "local" || loaded.result.Config.Collections["team"].Backends[0].ID != "local" {
|
|
t.Fatal("identified root and collection resources were not retained")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIdentifiedResourcesComposeAcrossNamespacesAndKinds(t *testing.T) {
|
|
resources := func() map[string]any {
|
|
return map[string]any{
|
|
"backends": []any{
|
|
map[string]any{"id": "shared", "endpoint": "https://example.invalid/v1"},
|
|
map[string]any{"id": "child", "extends": map[string]any{"id": "shared"}},
|
|
},
|
|
"profiles": []any{
|
|
map[string]any{"id": "shared", "model": "model"},
|
|
map[string]any{"id": "child", "extends": map[string]any{"id": "shared"}},
|
|
},
|
|
"prompts": []any{
|
|
map[string]any{"id": "shared", "messages": []any{map[string]any{"role": "user", "content": "inherited"}},
|
|
"output": map[string]any{"format": "text", "validation_mode": "basic"}},
|
|
map[string]any{"id": "child", "extends": map[string]any{"id": "shared"}},
|
|
},
|
|
}
|
|
}
|
|
values := resources()
|
|
values["collections"] = map[string]any{"*": resources(), "123": resources()}
|
|
state, err := load(mapping("identified namespaces", values), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, prefix := range []string{"", "*/", "123/"} {
|
|
backend, err := state.registry.GetBackend(prefix + "child")
|
|
if err != nil || backend.ID != prefix+"child" || backend.Endpoint != "https://example.invalid/v1" {
|
|
t.Fatalf("inherited backend %q = %+v, %v", prefix, backend, err)
|
|
}
|
|
profile, err := state.GetProfileIdentity(context.Background(), prefix+"child")
|
|
if err != nil || profile != prefix+"child" {
|
|
t.Fatalf("inherited profile %q = %q, %v", prefix, profile, err)
|
|
}
|
|
definition, _, err := state.prompt(prefix + "child")
|
|
if err != nil || definition.ID != prefix+"child" || len(definition.Templates) != 1 || definition.Templates[0].Content != "inherited" {
|
|
t.Fatalf("inherited prompt %q = %+v, %v", prefix, definition, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIdentifiedResourcesRejectInvalidInheritance(t *testing.T) {
|
|
for _, kind := range []string{"backends", "profiles", "prompts"} {
|
|
for name, records := range map[string][]any{
|
|
"missing": {map[string]any{"id": "child", "extends": map[string]any{"id": "missing"}}},
|
|
"self": {map[string]any{"id": "child", "extends": map[string]any{"id": "child"}}},
|
|
"cycle": {
|
|
map[string]any{"id": "first", "extends": map[string]any{"id": "second"}},
|
|
map[string]any{"id": "second", "extends": map[string]any{"id": "first"}},
|
|
},
|
|
} {
|
|
t.Run(kind+"/"+name, func(t *testing.T) {
|
|
_, err := load(mapping("invalid inheritance", map[string]any{kind: records}), nil)
|
|
var loadErr *configkit.LoadError
|
|
if !errors.As(err, &loadErr) {
|
|
t.Fatalf("load error = %v, want an upstream inheritance diagnostic", err)
|
|
}
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
if diagnostic.Code == configkit.CodeInheritanceMissing || diagnostic.Code == configkit.CodeInheritanceCycle {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("missing inheritance diagnostic: %+v", loadErr.Diagnostics())
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIdentifiedResourcesRejectInvalidLocalIDs(t *testing.T) {
|
|
for _, kind := range []string{"backends", "profiles", "prompts"} {
|
|
for _, test := range []struct {
|
|
name string
|
|
record map[string]any
|
|
}{
|
|
{name: "missing", record: map[string]any{}},
|
|
{name: "null", record: map[string]any{"id": nil}},
|
|
{name: "non-string", record: map[string]any{"id": 42}},
|
|
{name: "blank", record: map[string]any{"id": " "}},
|
|
{name: "slash", record: map[string]any{"id": "not/local"}},
|
|
} {
|
|
t.Run(kind+"/"+test.name, func(t *testing.T) {
|
|
_, err := load(mapping("invalid resource ID", map[string]any{kind: []any{test.record}}), nil)
|
|
var loadErr *configkit.LoadError
|
|
if !errors.As(err, &loadErr) || len(loadErr.Diagnostics()) == 0 {
|
|
t.Fatalf("load error = %v, want a path-aware error", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIdentifiedResourcesRejectDuplicateStrictPeers(t *testing.T) {
|
|
for _, kind := range []string{"backends", "profiles", "prompts"} {
|
|
for _, namespace := range []string{"", "*"} {
|
|
t.Run(kind+"/"+namespace, func(t *testing.T) {
|
|
values := func() map[string]any {
|
|
records := []any{map[string]any{"id": "duplicate"}}
|
|
if namespace == "" {
|
|
return map[string]any{kind: records}
|
|
}
|
|
return map[string]any{"collections": map[string]any{namespace: map[string]any{kind: records}}}
|
|
}
|
|
_, err := load(configkit.Input{Source: configkit.NoSource()}, []configkit.Underlay{{Inputs: []configkit.Input{
|
|
mapping("first", values()), mapping("second", values()),
|
|
}}})
|
|
var loadErr *configkit.LoadError
|
|
if !errors.As(err, &loadErr) {
|
|
t.Fatalf("load error = %v, want an upstream duplicate diagnostic", err)
|
|
}
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
if diagnostic.Code == configkit.CodeComposeConflict {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("missing duplicate conflict: %+v", loadErr.Diagnostics())
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFinalIdentitiesAreCheckedAfterApplicationResolution(t *testing.T) {
|
|
for _, kind := range []string{"backends", "profiles", "prompts"} {
|
|
for _, namespace := range []string{"", "*"} {
|
|
t.Run(kind+"/"+namespace, func(t *testing.T) {
|
|
definition := configurationDefinition(&loaded{})
|
|
definition.Resolve = func(config *document, resolution *configkit.Resolution) error {
|
|
path := configkit.Keys(kind)
|
|
if namespace == "" {
|
|
switch kind {
|
|
case "backends":
|
|
config.Backends = []backendConfig{{ID: "same"}, {ID: "same"}}
|
|
case "profiles":
|
|
config.Profiles = []profileConfig{{ID: "same"}, {ID: "same"}}
|
|
case "prompts":
|
|
config.Prompts = []promptConfig{{ID: "same"}, {ID: "same"}}
|
|
}
|
|
} else {
|
|
collection := collectionConfig{}
|
|
switch kind {
|
|
case "backends":
|
|
collection.Backends = []backendConfig{{ID: "same"}, {ID: "same"}}
|
|
case "profiles":
|
|
collection.Profiles = []profileConfig{{ID: "same"}, {ID: "same"}}
|
|
case "prompts":
|
|
collection.Prompts = []promptConfig{{ID: "same"}, {ID: "same"}}
|
|
}
|
|
config.Collections = map[string]collectionConfig{namespace: collection}
|
|
path = configkit.Keys("collections", namespace, kind)
|
|
}
|
|
resolution.RecordNormalization(path, "supply resolved records")
|
|
return nil
|
|
}
|
|
input := configkit.Input{Source: configkit.NoSource()}
|
|
if namespace != "" {
|
|
input = mapping("existing collection", map[string]any{"collections": map[string]any{
|
|
namespace: map[string]any{kind: []any{}},
|
|
}})
|
|
}
|
|
_, err := configkit.LoadLayered(input, configkit.LayeredOptions[document]{Definition: definition})
|
|
var loadErr *configkit.LoadError
|
|
if !errors.As(err, &loadErr) {
|
|
t.Fatalf("load error = %v, want a final identity diagnostic", err)
|
|
}
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
if diagnostic.Code == configkit.CodeSequenceConflict {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("missing final identity conflict: %+v", loadErr.Diagnostics())
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIdentifiedResourceLayerOperations(t *testing.T) {
|
|
base := mapping("base profiles", map[string]any{"profiles": []any{
|
|
map[string]any{"id": "first", "model": "original", "service_tier": "standard"},
|
|
map[string]any{"id": "second", "model": "retained"},
|
|
}})
|
|
for _, test := range []struct {
|
|
name string
|
|
mode string
|
|
records []any
|
|
models map[string]string
|
|
}{
|
|
{name: "whole record upsert", records: []any{map[string]any{"id": "first", "model": "updated"}}, models: map[string]string{"first": "updated", "second": "retained"}},
|
|
{name: "empty upsert", records: []any{}, models: map[string]string{"first": "original", "second": "retained"}},
|
|
{name: "append", mode: "append", records: []any{map[string]any{"id": "third", "model": "added"}}, models: map[string]string{"first": "original", "second": "retained", "third": "added"}},
|
|
{name: "replace", mode: "replace", records: []any{map[string]any{"id": "third", "model": "replacement"}}, models: map[string]string{"third": "replacement"}},
|
|
{name: "clear", mode: "replace", records: []any{}, models: map[string]string{}},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
values := map[string]any{"profiles": test.records}
|
|
if test.mode != "" {
|
|
values["config"] = map[string]any{"sequences": []any{map[string]any{
|
|
"destination": []any{"profiles"}, "mode": test.mode,
|
|
}}}
|
|
}
|
|
state, err := load(mapping("overlay profiles", values), []configkit.Underlay{{Inputs: []configkit.Input{base}}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
profiles := indexedProfiles(&state.result.Config)
|
|
if len(profiles) != len(test.models) {
|
|
t.Fatalf("profiles = %+v, want IDs and models %+v", profiles, test.models)
|
|
}
|
|
for id, model := range test.models {
|
|
profile, ok := profiles[id]
|
|
if !ok || profile.Model == nil || *profile.Model != model {
|
|
t.Fatalf("profile %q = %+v, want model %q", id, profile, model)
|
|
}
|
|
}
|
|
if test.name == "whole record upsert" && profiles["first"].ServiceTier != nil {
|
|
t.Fatalf("upsert retained a displaced field: %+v", profiles["first"])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIdentifiedResourceLayersRejectDisplacedParentAndDuplicateAppend(t *testing.T) {
|
|
base := mapping("base", map[string]any{"profiles": []any{
|
|
map[string]any{"id": "shared", "model": "original"},
|
|
}})
|
|
for _, test := range []struct {
|
|
name string
|
|
values map[string]any
|
|
code configkit.Code
|
|
}{
|
|
{
|
|
name: "self extension cannot recover displaced record",
|
|
values: map[string]any{"profiles": []any{
|
|
map[string]any{"id": "shared", "extends": map[string]any{"id": "shared"}},
|
|
}},
|
|
code: configkit.CodeInheritanceCycle,
|
|
},
|
|
{
|
|
name: "append cannot duplicate an identity",
|
|
values: map[string]any{
|
|
"config": map[string]any{"sequences": []any{map[string]any{"destination": []any{"profiles"}, "mode": "append"}}},
|
|
"profiles": []any{map[string]any{"id": "shared", "model": "other"}},
|
|
},
|
|
code: configkit.CodeSequenceConflict,
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, err := load(mapping("overlay", test.values), []configkit.Underlay{{Inputs: []configkit.Input{base}}})
|
|
var loadErr *configkit.LoadError
|
|
if !errors.As(err, &loadErr) {
|
|
t.Fatalf("load error = %v, want structured diagnostic", err)
|
|
}
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
if diagnostic.Code == test.code {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("missing %s diagnostic: %+v", test.code, loadErr.Diagnostics())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadChecksOptionalConfigurationVersion(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
version any
|
|
include bool
|
|
valid bool
|
|
}{
|
|
{name: "string two", version: "2", include: true, valid: true},
|
|
{name: "integer two", version: int64(2), include: true, valid: true},
|
|
{name: "omitted", valid: true},
|
|
{name: "explicit null", include: true},
|
|
{name: "unsupported string", version: "1", include: true},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
values := map[string]any{}
|
|
if test.include {
|
|
values["version"] = test.version
|
|
}
|
|
loaded, err := load(mapping("main", values), nil)
|
|
if test.valid {
|
|
if err != nil {
|
|
t.Fatalf("load accepted version: %v", err)
|
|
}
|
|
if test.include && (loaded.result.Config.Version == nil || *loaded.result.Config.Version != "2") {
|
|
t.Fatalf("typed version = %v, want 2", loaded.result.Config.Version)
|
|
}
|
|
if !test.include && loaded.result.Config.Version != nil {
|
|
t.Fatalf("omitted version = %q, want nil", *loaded.result.Config.Version)
|
|
}
|
|
return
|
|
}
|
|
var loadErr *configkit.LoadError
|
|
if err == nil || !errors.As(err, &loadErr) {
|
|
t.Fatalf("load error = %v, want ConfigKit load error", err)
|
|
}
|
|
found := false
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
if diagnostic.Path.String() == "/version" && diagnostic.Message == "unsupported configuration specification version" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("load diagnostics = %+v, want safe version diagnostic", loadErr.Diagnostics())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsMalformedHeadersAndLegacyResourceMappings(t *testing.T) {
|
|
for name, values := range map[string]map[string]any{
|
|
"boolean header": {"version": true},
|
|
"fractional header": {"version": 2.5},
|
|
"sequence header": {"version": []any{2}},
|
|
"backend mapping": {"backends": map[string]any{"local": map[string]any{"endpoint": "https://example.invalid/v1"}}},
|
|
"profile mapping": {"profiles": map[string]any{"local": map[string]any{}}},
|
|
"prompt mapping": {"prompts": map[string]any{"local": map[string]any{}}},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
_, err := load(mapping(name, values), nil)
|
|
var loadErr *configkit.LoadError
|
|
if !errors.As(err, &loadErr) || len(loadErr.Diagnostics()) == 0 {
|
|
t.Fatalf("load error = %v, want structured rejection", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNestedConfigRemainsApplicationData(t *testing.T) {
|
|
state, err := load(mapping("nested config", map[string]any{
|
|
"config": map[string]any{},
|
|
"profiles": []any{map[string]any{"id": "example", "extra_params": map[string]any{
|
|
"config": map[string]any{"imports": "provider data"},
|
|
}}},
|
|
}), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
profile, err := state.GetProfile(context.Background(), "example")
|
|
if err != nil || profile.ResolvedTarget.ExtraParams["config"].(map[string]any)["imports"] != "provider data" {
|
|
t.Fatalf("nested application data = %+v, %v", profile, err)
|
|
}
|
|
}
|
|
|
|
func TestLoadConsumesAcceptedUnderlayVersionAndValidatesHeaderlessPayload(t *testing.T) {
|
|
underlay := mapping("declared underlay", map[string]any{
|
|
"version": int64(2),
|
|
})
|
|
loaded, err := load(mapping("headerless main", map[string]any{}), []configkit.Underlay{{Inputs: []configkit.Input{underlay}}})
|
|
if err != nil {
|
|
t.Fatalf("load accepted non-main declaration: %v", err)
|
|
}
|
|
if loaded.result.Config.Version != nil {
|
|
t.Fatalf("consumed non-main declaration remained in effective payload: %q", *loaded.result.Config.Version)
|
|
}
|
|
|
|
invalidFragment := mapping("headerless prompt fragment", map[string]any{
|
|
"prompts": []any{map[string]any{"id": "incomplete", "description": "still validated"}},
|
|
})
|
|
if _, err := load(configkit.Input{Source: configkit.NoSource()}, []configkit.Underlay{{Inputs: []configkit.Input{invalidFragment}}}); err == nil || !strings.Contains(err.Error(), "at least one message is required") {
|
|
t.Fatalf("headerless fragment validation error = %v, want final prompt validation", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadComposesLiteralKeysAndEffectivePresence(t *testing.T) {
|
|
base := mapping("defaults", map[string]any{
|
|
"version": int64(2),
|
|
"backends": []any{map[string]any{"id": "local.v1", "endpoint": "http://localhost:8080/v1", "concurrency_limit": 2}},
|
|
"collections": map[string]any{"base": map[string]any{
|
|
"release_version": "",
|
|
"backends": []any{map[string]any{"id": "local.v1", "endpoint": "http://localhost:8080/v1", "concurrency_limit": 3}},
|
|
"profiles": []any{
|
|
map[string]any{"id": "one", "backend": "local.v1", "temperature": 0.8, "top_p": 0.9, "api_key_required": true,
|
|
"extra_params": map[string]any{"provider": map[string]any{"first": 1, "second": 2}}},
|
|
map[string]any{"id": "child.v1", "extends": map[string]any{"id": "one"}},
|
|
map[string]any{"id": "namespaced", "backend": "base/local.v1", "model": "collection-model"},
|
|
},
|
|
}, "invoice": map[string]any{
|
|
"release_version": "r7",
|
|
"prompts": []any{
|
|
map[string]any{"id": "summary",
|
|
"messages": []any{map[string]any{"role": "user", "content": "Summarize."}},
|
|
"output": map[string]any{"format": "text", "validation_mode": "basic"},
|
|
},
|
|
map[string]any{"id": "version",
|
|
"messages": []any{map[string]any{"role": "user", "content": "Nested ID."}},
|
|
"output": map[string]any{"format": "text", "validation_mode": "basic"},
|
|
},
|
|
},
|
|
}},
|
|
})
|
|
overlay := mapping("operator", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local.v1", "endpoint": "http://localhost:8080/v1", "concurrency_limit": 2, "queue_capacity": 0}},
|
|
"collections": map[string]any{"base": map[string]any{"profiles": []any{map[string]any{"id": "child.v1", "extends": map[string]any{"id": "one"},
|
|
"temperature": 0, "top_p": nil, "api_key_required": false,
|
|
"extra_params": map[string]any{"provider": map[string]any{"second": 3}},
|
|
}}}},
|
|
})
|
|
loaded, err := load(mapping("main", map[string]any{"version": "2"}), []configkit.Underlay{
|
|
{Inputs: []configkit.Input{base}}, {Inputs: []configkit.Input{overlay}},
|
|
})
|
|
if err != nil {
|
|
var loadErr *configkit.LoadError
|
|
if errors.As(err, &loadErr) {
|
|
t.Fatalf("load: %v; diagnostics: %+v", err, loadErr.Diagnostics())
|
|
}
|
|
t.Fatal(err)
|
|
}
|
|
profile := loaded.result.Config.Collections["base"].Profiles[1]
|
|
if profile.Temperature == nil || *profile.Temperature != 0 || profile.TopP != nil || profile.APIKeyRequired == nil || *profile.APIKeyRequired {
|
|
t.Fatalf("effective values were not retained: %+v", profile)
|
|
}
|
|
if loaded.profiles["base/child.v1"]["temperature"] != fieldValue || loaded.profiles["base/child.v1"]["top_p"] != fieldNull || loaded.profiles["base/child.v1"]["api_key_required"] != fieldValue {
|
|
t.Fatalf("effective presence was lost: %+v", loaded.profiles["base/child.v1"])
|
|
}
|
|
provider := profile.ExtraParams["provider"].(map[string]any)
|
|
if provider["first"] != int64(1) || provider["second"] != int64(3) {
|
|
t.Fatalf("recursive overlay failed: %+v", provider)
|
|
}
|
|
backend := loaded.result.Config.Backends[0]
|
|
if backend.QueueCapacity == nil || *backend.QueueCapacity != 0 || loaded.backends["local.v1"]["queue_capacity"] != fieldValue {
|
|
t.Fatalf("explicit zero queue capacity was lost: %+v", backend)
|
|
}
|
|
if _, err := loaded.registry.GetBackend("base/local.v1"); err != nil {
|
|
t.Fatalf("collection backend was not indexed by its full ID: %v", err)
|
|
}
|
|
collectionProfile, exists := loaded.indexedProfiles["base/namespaced"]
|
|
if !exists || collectionProfile.ID != "base/namespaced" || collectionProfile.ResolvedTarget == nil || collectionProfile.ResolvedTarget.BackendID != "base/local.v1" {
|
|
t.Fatalf("collection profile snapshot = %+v, exists %t", collectionProfile, exists)
|
|
}
|
|
if loaded.result.Config.Collections["invoice"].Prompts[0].Messages[0].Content == nil {
|
|
t.Fatal("literal prompt content was not decoded")
|
|
}
|
|
if loaded.result.Config.Version == nil || *loaded.result.Config.Version != "2" ||
|
|
loaded.result.Config.Collections["base"].ReleaseVersion == nil || *loaded.result.Config.Collections["base"].ReleaseVersion != "" ||
|
|
loaded.result.Config.Collections["invoice"].ReleaseVersion == nil || *loaded.result.Config.Collections["invoice"].ReleaseVersion != "r7" {
|
|
t.Fatalf("root or collection version metadata was not retained: %+v", loaded.result.Config)
|
|
}
|
|
if _, _, err := loaded.prompt("invoice/version"); err != nil {
|
|
t.Fatalf("resource named version was not indexed under its qualified identity: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsInvalidCollectionAndResourceKeys(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
values map[string]any
|
|
message string
|
|
}{
|
|
{
|
|
name: "namespace with separator",
|
|
values: map[string]any{"collections": map[string]any{"bad/name": map[string]any{}}},
|
|
message: "collection namespace must be nonblank and contain no slash",
|
|
},
|
|
{
|
|
name: "root resource with separator",
|
|
values: map[string]any{"backends": []any{
|
|
map[string]any{"id": "bad/name", "endpoint": "https://example.test/v1"},
|
|
}},
|
|
message: "backend ID must be a nonblank local key without a slash",
|
|
},
|
|
{
|
|
name: "collection resource with separator",
|
|
values: map[string]any{"collections": map[string]any{"good": map[string]any{
|
|
"prompts": []any{map[string]any{"id": "bad/name",
|
|
"messages": []any{map[string]any{"role": "user", "content": "Hello."}},
|
|
"output": map[string]any{"format": "text", "validation_mode": "basic"},
|
|
}},
|
|
}}},
|
|
message: "prompt ID must be a nonblank local key without a slash",
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, err := load(mapping("invalid names", test.values), nil)
|
|
var loadErr *configkit.LoadError
|
|
if err == nil || !errors.As(err, &loadErr) {
|
|
t.Fatalf("load error = %v, want ConfigKit validation error", err)
|
|
}
|
|
for _, diagnostic := range loadErr.Diagnostics() {
|
|
if diagnostic.Message == test.message {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("load diagnostics = %+v, want message %q", loadErr.Diagnostics(), test.message)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadInterpretsBackendNormalizationAndNullDefault(t *testing.T) {
|
|
loaded, err := load(mapping("backend", map[string]any{"backends": []any{
|
|
map[string]any{"id": "local", "endpoint": " HTTP://localhost:8080/v1 ", "api_key_env": " TOKEN_NAME ",
|
|
"concurrency_limit": 2, "queue_capacity": nil},
|
|
}}), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
backend, err := loaded.GetBackend("local")
|
|
if err != nil || backend.Endpoint != "http://localhost:8080/v1" ||
|
|
backend.APIKeyEnv != "TOKEN_NAME" || backend.QueueCapacity != 1024 {
|
|
t.Fatalf("backend normalization/default was not applied: %+v", backend)
|
|
}
|
|
composed := loaded.result.Config.Backends[0]
|
|
if composed.Endpoint == nil || *composed.Endpoint != " HTTP://localhost:8080/v1 " ||
|
|
composed.APIKeyEnv == nil || *composed.APIKeyEnv != " TOKEN_NAME " || composed.QueueCapacity != nil {
|
|
t.Fatalf("composed backend was changed: %+v", composed)
|
|
}
|
|
if loaded.backends["local"]["queue_capacity"] != fieldNull {
|
|
t.Fatalf("explicit null was not retained: %+v", loaded.backends["local"])
|
|
}
|
|
field, found := loaded.result.Metadata().Lookup(configkit.Keys("backends").Index(0).Key("endpoint"))
|
|
if !found || field.Origin.Operation == configkit.ChangeNormalization || len(field.Origin.Locations) == 0 {
|
|
t.Fatalf("endpoint origin was not preserved: %+v", field)
|
|
}
|
|
}
|
|
|
|
func TestLoadBackendSmokeTestsOmissionAndNull(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
smokeTests any
|
|
include bool
|
|
inherited bool
|
|
}{
|
|
{name: "omitted"},
|
|
{name: "null", smokeTests: nil, include: true, inherited: true},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
backend := map[string]any{"id": "local", "endpoint": "https://backend.example/v1"}
|
|
if test.include {
|
|
backend["smoke_tests"] = test.smokeTests
|
|
}
|
|
main := mapping("backend", map[string]any{
|
|
"backends": []any{backend},
|
|
})
|
|
var underlays []configkit.Underlay
|
|
if test.inherited {
|
|
underlays = []configkit.Underlay{{Inputs: []configkit.Input{mapping("defaults", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local",
|
|
"endpoint": "https://backend.example/v1",
|
|
"smoke_tests": map[string]any{"default_profile": "inherited-profile"},
|
|
}},
|
|
})}}}
|
|
}
|
|
loaded, err := load(main, underlays)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := loaded.result.Config.Backends[0].SmokeTests; got != nil {
|
|
t.Fatalf("smoke_tests = %+v, want nil", got)
|
|
}
|
|
selected, err := loaded.registry.GetBackend("local")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.SmokeTests != nil {
|
|
t.Fatalf("indexed smoke_tests = %+v, want nil", selected.SmokeTests)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadMergesBackendSmokeTestsAndRetainsFallbacks(t *testing.T) {
|
|
base := mapping("defaults", map[string]any{
|
|
"backends": []any{map[string]any{"id": "catalog",
|
|
"endpoint": "https://backend.example/v1",
|
|
"smoke_tests": map[string]any{
|
|
"default_profile": "catalog-profile",
|
|
"reachability": map[string]any{"endpoint": " https://probe.example/ready?check=1 "},
|
|
"authentication": map[string]any{
|
|
"kind": " OPENROUTER_KEY ",
|
|
"endpoint": "https://probe.example/key?scope=current",
|
|
},
|
|
},
|
|
}},
|
|
})
|
|
overlay := mapping("operator", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local", "extends": map[string]any{"id": "catalog"},
|
|
"smoke_tests": map[string]any{
|
|
"default_profile": "not-yet-listed",
|
|
"authentication": map[string]any{"kind": " NONE ", "endpoint": nil},
|
|
},
|
|
}},
|
|
})
|
|
loaded, err := load(configkit.Input{Source: configkit.NoSource()}, []configkit.Underlay{
|
|
{Inputs: []configkit.Input{base}},
|
|
{Inputs: []configkit.Input{overlay}},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("load effective diagnostic metadata: %v", err)
|
|
}
|
|
selected, err := loaded.registry.GetBackend("local")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
metadata := selected.SmokeTests
|
|
if selected.Endpoint != "https://backend.example/v1" {
|
|
t.Fatalf("effective backend endpoint = %q", selected.Endpoint)
|
|
}
|
|
if metadata == nil || metadata.DefaultProfile == nil || *metadata.DefaultProfile != "not-yet-listed" {
|
|
t.Fatalf("default profile overlay was not retained: %+v", metadata)
|
|
}
|
|
if metadata.Reachability == nil || metadata.Reachability.Endpoint == nil ||
|
|
*metadata.Reachability.Endpoint != "https://probe.example/ready?check=1" {
|
|
t.Fatalf("full reachability destination was not normalized: %+v", metadata.Reachability)
|
|
}
|
|
if metadata.Authentication == nil || metadata.Authentication.Kind == nil || *metadata.Authentication.Kind != "none" ||
|
|
metadata.Authentication.Endpoint != nil {
|
|
t.Fatalf("authentication overlay or null fallback was not retained: %+v", metadata.Authentication)
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsInvalidFinalBackendSmokeTests(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
data map[string]any
|
|
path string
|
|
}{
|
|
{
|
|
name: "blank default profile",
|
|
data: map[string]any{"default_profile": " \t "},
|
|
path: "/backends/0/smoke_tests/default_profile",
|
|
},
|
|
{
|
|
name: "authentication kind required",
|
|
data: map[string]any{"authentication": map[string]any{}},
|
|
path: "/backends/0/smoke_tests/authentication/kind",
|
|
},
|
|
{
|
|
name: "unknown authentication kind",
|
|
data: map[string]any{"authentication": map[string]any{"kind": "bearer"}},
|
|
path: "/backends/0/smoke_tests/authentication/kind",
|
|
},
|
|
{
|
|
name: "non-http reachability URL",
|
|
data: map[string]any{"reachability": map[string]any{"endpoint": "ftp://probe.example/ready"}},
|
|
path: "/backends/0/smoke_tests/reachability/endpoint",
|
|
},
|
|
{
|
|
name: "blank authentication URL",
|
|
data: map[string]any{"authentication": map[string]any{"kind": "none", "endpoint": " "}},
|
|
path: "/backends/0/smoke_tests/authentication/endpoint",
|
|
},
|
|
{
|
|
name: "relative authentication URL",
|
|
data: map[string]any{"authentication": map[string]any{"kind": "none", "endpoint": "/key"}},
|
|
path: "/backends/0/smoke_tests/authentication/endpoint",
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, err := load(mapping("backend", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local",
|
|
"endpoint": "https://backend.example/v1",
|
|
"smoke_tests": test.data,
|
|
}},
|
|
}), nil)
|
|
var loadError *configkit.LoadError
|
|
if !errors.As(err, &loadError) {
|
|
t.Fatalf("expected final-value validation error, got %v", err)
|
|
}
|
|
for _, diagnostic := range loadError.Diagnostics() {
|
|
if diagnostic.Path.String() == test.path {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("expected diagnostic at %s, got %+v", test.path, loadError.Diagnostics())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadDefaultsAbsentQueueCapacity(t *testing.T) {
|
|
underlay := mapping("defaults", map[string]any{"backends": []any{
|
|
map[string]any{"id": "local", "endpoint": "http://localhost:8080/v1", "api_key_env": "OLD_KEY"},
|
|
}})
|
|
loaded, err := load(mapping("backend", map[string]any{"backends": []any{
|
|
map[string]any{"id": "local", "endpoint": "http://localhost:8080/v1", "concurrency_limit": 1},
|
|
}}), []configkit.Underlay{{Inputs: []configkit.Input{underlay}}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
backend, err := loaded.GetBackend("local")
|
|
if err != nil || backend.APIKeyEnv != "" || backend.QueueCapacity != 1024 || loaded.result.Config.Backends[0].QueueCapacity != nil || loaded.backends["local"]["queue_capacity"] != fieldAbsent {
|
|
t.Fatalf("absent queue capacity did not receive its default: %+v", backend)
|
|
}
|
|
}
|
|
|
|
func TestLoadCapturesProfilePresence(t *testing.T) {
|
|
loaded, err := load(mapping("profile", map[string]any{"profiles": []any{
|
|
map[string]any{"id": "local", "temperature": 0, "top_p": nil, "api_key_required": false},
|
|
}}), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
profile := loaded.result.Config.Profiles[0]
|
|
if profile.Temperature == nil || *profile.Temperature != 0 || profile.TopP != nil || profile.APIKeyRequired == nil || *profile.APIKeyRequired {
|
|
t.Fatalf("profile values were not preserved: %+v", profile)
|
|
}
|
|
fields := loaded.profiles["local"]
|
|
if fields["temperature"] != fieldValue || fields["top_p"] != fieldNull || fields["api_key_required"] != fieldValue || fields["model"] != fieldAbsent {
|
|
t.Fatalf("profile presence was not preserved: %+v", fields)
|
|
}
|
|
}
|
|
|
|
func TestLoadRejectsStrictGroupConflict(t *testing.T) {
|
|
_, err := load(configkit.Input{Source: configkit.NoSource()}, []configkit.Underlay{{Inputs: []configkit.Input{
|
|
mapping("first", map[string]any{"profiles": []any{map[string]any{"id": "shared", "model": "one"}}}),
|
|
mapping("second", map[string]any{"profiles": []any{map[string]any{"id": "shared", "model": "one"}}}),
|
|
}}})
|
|
var loadError *configkit.LoadError
|
|
if !errors.As(err, &loadError) {
|
|
t.Fatalf("expected ConfigKit load error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadValidatesOnlyFinalDomainValues(t *testing.T) {
|
|
underlay := mapping("defaults", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local", "endpoint": "not a URL"}},
|
|
"profiles": []any{map[string]any{"id": "partial", "backend": "local"}},
|
|
})
|
|
main := mapping("operator", map[string]any{"backends": []any{map[string]any{"id": "local", "endpoint": "http://localhost:8080/v1"}}})
|
|
loaded, err := load(main, []configkit.Underlay{{Inputs: []configkit.Input{underlay}}})
|
|
if err != nil {
|
|
t.Fatalf("replaced lower-layer domain value should not fail: %v", err)
|
|
}
|
|
if loaded.result.Config.Profiles[0].Model != nil {
|
|
t.Fatal("unused partial profile unexpectedly gained a model")
|
|
}
|
|
|
|
_, err = load(configkit.Input{Source: configkit.NoSource()}, []configkit.Underlay{{Inputs: []configkit.Input{
|
|
mapping("invalid", map[string]any{"backends": []any{map[string]any{"id": "unused", "endpoint": "not a URL"}}}),
|
|
}}})
|
|
var loadError *configkit.LoadError
|
|
if !errors.As(err, &loadError) {
|
|
t.Fatalf("expected path-aware effective backend failure, got %v", err)
|
|
}
|
|
if diagnostics := loadError.Diagnostics(); len(diagnostics) != 1 || diagnostics[0].Path.String() != "/backends/0/endpoint" {
|
|
t.Fatalf("expected endpoint diagnostic, got %+v", diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestLoadValidatesOnlyFinalBackendSmokeTestValues(t *testing.T) {
|
|
underlay := mapping("defaults", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local",
|
|
"endpoint": "https://backend.example/v1",
|
|
"smoke_tests": map[string]any{
|
|
"default_profile": " ",
|
|
"reachability": map[string]any{"endpoint": "ftp://invalid.example/"},
|
|
"authentication": map[string]any{"kind": "unsupported"},
|
|
},
|
|
}},
|
|
})
|
|
main := mapping("operator", map[string]any{
|
|
"backends": []any{map[string]any{"id": "local", "endpoint": "https://backend.example/v1",
|
|
"smoke_tests": map[string]any{
|
|
"default_profile": "dangling-profile",
|
|
"reachability": map[string]any{"endpoint": nil},
|
|
"authentication": map[string]any{"kind": "none"},
|
|
},
|
|
}},
|
|
})
|
|
loaded, err := load(main, []configkit.Underlay{{Inputs: []configkit.Input{underlay}}})
|
|
if err != nil {
|
|
t.Fatalf("replaced lower metadata should not fail: %v", err)
|
|
}
|
|
selected, err := loaded.registry.GetBackend("local")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.SmokeTests == nil || selected.SmokeTests.DefaultProfile == nil ||
|
|
*selected.SmokeTests.DefaultProfile != "dangling-profile" {
|
|
t.Fatalf("final dangling default profile was not retained: %+v", selected.SmokeTests)
|
|
}
|
|
}
|
|
|
|
func TestLoadKeepsSyntaxErrorsFromLowerTiers(t *testing.T) {
|
|
_, err := load(mapping("operator", map[string]any{"backends": []any{map[string]any{"id": "local", "endpoint": "http://localhost:8080"}}}),
|
|
[]configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.Bytes("broken", []byte("backends: ["))}}}})
|
|
var loadError *configkit.LoadError
|
|
if !errors.As(err, &loadError) {
|
|
t.Fatalf("expected lower-tier parsing error, got %v", err)
|
|
}
|
|
}
|