Verify identified resource composition and inheritance
This commit is contained in:
@@ -647,6 +647,13 @@ configuration fixtures not yet converted.
|
||||
Validation: real-loader tests assert effective behavior and structured diagnostic
|
||||
identity/paths as appropriate, not complete upstream error wording or ordering.
|
||||
|
||||
Completion: loader fixtures now exercise identified lists, inherited records,
|
||||
strict duplicates, invalid identities and parent references, all six registered
|
||||
destinations, final identity enforcement, layered upsert/append/replace, and
|
||||
request completion of partial profiles. The focused real-loader composition,
|
||||
target, validation, and syntax tests passed on Go 1.27.0. Header and remaining
|
||||
adapter fixtures are handled in the next assignment.
|
||||
|
||||
## Stage 14: Complete Header, Origin, And Adapter Fixture Coverage
|
||||
|
||||
Scope: remaining `internal/configuration` prompt, inspection, listing, and
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -33,6 +34,265 @@ func TestLoadIdentifiedResourceLists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -97,7 +357,7 @@ func TestLoadConsumesAcceptedUnderlayVersionAndValidatesHeaderlessPayload(t *tes
|
||||
}
|
||||
|
||||
invalidFragment := mapping("headerless prompt fragment", map[string]any{
|
||||
"prompts": map[string]any{"incomplete": map[string]any{"description": "still validated"}},
|
||||
"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)
|
||||
@@ -106,30 +366,25 @@ func TestLoadConsumesAcceptedUnderlayVersionAndValidatesHeaderlessPayload(t *tes
|
||||
|
||||
func TestLoadComposesLiteralKeysAndEffectivePresence(t *testing.T) {
|
||||
base := mapping("defaults", map[string]any{
|
||||
"version": int64(1),
|
||||
"backends": map[string]any{
|
||||
"local.v1": map[string]any{"endpoint": "http://localhost:8080/v1", "concurrency_limit": 2},
|
||||
},
|
||||
"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": map[string]any{
|
||||
"local.v1": map[string]any{"endpoint": "http://localhost:8080/v1", "concurrency_limit": 3},
|
||||
},
|
||||
"profiles": map[string]any{
|
||||
"one": map[string]any{"backend": "local.v1", "temperature": 0.8, "top_p": 0.9, "api_key_required": true,
|
||||
"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}}},
|
||||
"child.v1": map[string]any{"extends": "one"},
|
||||
"namespaced": map[string]any{"backend": "base/local.v1", "model": "collection-model"},
|
||||
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": map[string]any{
|
||||
"summary": map[string]any{
|
||||
"version": "v1.0",
|
||||
"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"},
|
||||
},
|
||||
"version": map[string]any{
|
||||
map[string]any{"id": "version",
|
||||
"messages": []any{map[string]any{"role": "user", "content": "Nested ID."}},
|
||||
"output": map[string]any{"format": "text", "validation_mode": "basic"},
|
||||
},
|
||||
@@ -137,19 +392,23 @@ func TestLoadComposesLiteralKeysAndEffectivePresence(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
overlay := mapping("operator", map[string]any{
|
||||
"backends": map[string]any{"local.v1": map[string]any{"queue_capacity": 0}},
|
||||
"collections": map[string]any{"base": map[string]any{"profiles": map[string]any{"child.v1": 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": "1"}), []configkit.Underlay{
|
||||
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["child.v1"]
|
||||
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)
|
||||
}
|
||||
@@ -160,7 +419,7 @@ func TestLoadComposesLiteralKeysAndEffectivePresence(t *testing.T) {
|
||||
if provider["first"] != int64(1) || provider["second"] != int64(3) {
|
||||
t.Fatalf("recursive overlay failed: %+v", provider)
|
||||
}
|
||||
backend := loaded.result.Config.Backends["local.v1"]
|
||||
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)
|
||||
}
|
||||
@@ -171,10 +430,10 @@ func TestLoadComposesLiteralKeysAndEffectivePresence(t *testing.T) {
|
||||
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["summary"].Messages[0].Content == nil {
|
||||
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 != "1" ||
|
||||
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)
|
||||
@@ -197,20 +456,20 @@ func TestLoadRejectsInvalidCollectionAndResourceKeys(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "root resource with separator",
|
||||
values: map[string]any{"backends": map[string]any{
|
||||
"bad/name": map[string]any{"endpoint": "https://example.test/v1"},
|
||||
values: map[string]any{"backends": []any{
|
||||
map[string]any{"id": "bad/name", "endpoint": "https://example.test/v1"},
|
||||
}},
|
||||
message: "backend key must be a nonblank local key without a slash",
|
||||
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": map[string]any{"bad/name": 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 key must be a nonblank local key without a slash",
|
||||
message: "prompt ID must be a nonblank local key without a slash",
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
@@ -267,17 +526,17 @@ func TestLoadBackendSmokeTestsOmissionAndNull(t *testing.T) {
|
||||
{name: "null", smokeTests: nil, include: true, inherited: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
backend := map[string]any{"endpoint": "https://backend.example/v1"}
|
||||
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": map[string]any{"local": backend},
|
||||
"backends": []any{backend},
|
||||
})
|
||||
var underlays []configkit.Underlay
|
||||
if test.inherited {
|
||||
underlays = []configkit.Underlay{{Inputs: []configkit.Input{mapping("defaults", map[string]any{
|
||||
"backends": map[string]any{"local": map[string]any{
|
||||
"backends": []any{map[string]any{"id": "local",
|
||||
"endpoint": "https://backend.example/v1",
|
||||
"smoke_tests": map[string]any{"default_profile": "inherited-profile"},
|
||||
}},
|
||||
@@ -287,7 +546,7 @@ func TestLoadBackendSmokeTestsOmissionAndNull(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := loaded.result.Config.Backends["local"].SmokeTests; got != nil {
|
||||
if got := loaded.result.Config.Backends[0].SmokeTests; got != nil {
|
||||
t.Fatalf("smoke_tests = %+v, want nil", got)
|
||||
}
|
||||
selected, err := loaded.registry.GetBackend("local")
|
||||
@@ -303,7 +562,7 @@ func TestLoadBackendSmokeTestsOmissionAndNull(t *testing.T) {
|
||||
|
||||
func TestLoadMergesBackendSmokeTestsAndRetainsFallbacks(t *testing.T) {
|
||||
base := mapping("defaults", map[string]any{
|
||||
"backends": map[string]any{"local": map[string]any{
|
||||
"backends": []any{map[string]any{"id": "catalog",
|
||||
"endpoint": "https://backend.example/v1",
|
||||
"smoke_tests": map[string]any{
|
||||
"default_profile": "catalog-profile",
|
||||
@@ -316,7 +575,7 @@ func TestLoadMergesBackendSmokeTestsAndRetainsFallbacks(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
overlay := mapping("operator", map[string]any{
|
||||
"backends": map[string]any{"local": 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},
|
||||
@@ -360,37 +619,37 @@ func TestLoadRejectsInvalidFinalBackendSmokeTests(t *testing.T) {
|
||||
{
|
||||
name: "blank default profile",
|
||||
data: map[string]any{"default_profile": " \t "},
|
||||
path: "/backends/local/smoke_tests/default_profile",
|
||||
path: "/backends/0/smoke_tests/default_profile",
|
||||
},
|
||||
{
|
||||
name: "authentication kind required",
|
||||
data: map[string]any{"authentication": map[string]any{}},
|
||||
path: "/backends/local/smoke_tests/authentication/kind",
|
||||
path: "/backends/0/smoke_tests/authentication/kind",
|
||||
},
|
||||
{
|
||||
name: "unknown authentication kind",
|
||||
data: map[string]any{"authentication": map[string]any{"kind": "bearer"}},
|
||||
path: "/backends/local/smoke_tests/authentication/kind",
|
||||
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/local/smoke_tests/reachability/endpoint",
|
||||
path: "/backends/0/smoke_tests/reachability/endpoint",
|
||||
},
|
||||
{
|
||||
name: "blank authentication URL",
|
||||
data: map[string]any{"authentication": map[string]any{"kind": "none", "endpoint": " "}},
|
||||
path: "/backends/local/smoke_tests/authentication/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/local/smoke_tests/authentication/endpoint",
|
||||
path: "/backends/0/smoke_tests/authentication/endpoint",
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, err := load(mapping("backend", map[string]any{
|
||||
"backends": map[string]any{"local": map[string]any{
|
||||
"backends": []any{map[string]any{"id": "local",
|
||||
"endpoint": "https://backend.example/v1",
|
||||
"smoke_tests": test.data,
|
||||
}},
|
||||
@@ -455,33 +714,33 @@ func TestLoadRejectsStrictGroupConflict(t *testing.T) {
|
||||
|
||||
func TestLoadValidatesOnlyFinalDomainValues(t *testing.T) {
|
||||
underlay := mapping("defaults", map[string]any{
|
||||
"backends": map[string]any{"local": map[string]any{"endpoint": "not a URL"}},
|
||||
"profiles": map[string]any{"partial": map[string]any{"backend": "local"}},
|
||||
"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": map[string]any{"local": map[string]any{"endpoint": "http://localhost:8080/v1"}}})
|
||||
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["partial"].Model != nil {
|
||||
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": map[string]any{"unused": map[string]any{"endpoint": "not a URL"}}}),
|
||||
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/unused/endpoint" {
|
||||
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": map[string]any{"local": map[string]any{
|
||||
"backends": []any{map[string]any{"id": "local",
|
||||
"endpoint": "https://backend.example/v1",
|
||||
"smoke_tests": map[string]any{
|
||||
"default_profile": " ",
|
||||
@@ -491,7 +750,7 @@ func TestLoadValidatesOnlyFinalBackendSmokeTestValues(t *testing.T) {
|
||||
}},
|
||||
})
|
||||
main := mapping("operator", map[string]any{
|
||||
"backends": map[string]any{"local": 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},
|
||||
@@ -514,7 +773,7 @@ func TestLoadValidatesOnlyFinalBackendSmokeTestValues(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadKeepsSyntaxErrorsFromLowerTiers(t *testing.T) {
|
||||
_, err := load(mapping("operator", map[string]any{"backends": map[string]any{"local": map[string]any{"endpoint": "http://localhost:8080"}}}),
|
||||
_, 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) {
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/llm"
|
||||
profilepkg "gitea.maximumdirect.net/eric/promptkit/internal/profile"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/prompt"
|
||||
"gitea.maximumdirect.net/eric/promptkit/internal/usecase"
|
||||
)
|
||||
|
||||
func TestLoadIndexesIdentifiedTargets(t *testing.T) {
|
||||
@@ -40,6 +42,31 @@ func TestLoadIndexesIdentifiedTargets(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestCompletesLoadedPartialProfile(t *testing.T) {
|
||||
state, err := load(mapping("partial profile", map[string]any{
|
||||
"profiles": []any{map[string]any{"id": "partial"}},
|
||||
"prompts": []any{map[string]any{
|
||||
"id": "example", "messages": []any{map[string]any{"role": "user", "content": "hello"}},
|
||||
"output": map[string]any{"format": "text", "validation_mode": "basic"},
|
||||
}},
|
||||
}), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := usecase.NewRunner(state, state, nil, prompt.NewGoRenderer(), nil, nil, nil)
|
||||
prepared, err := runner.Prepare(context.Background(), domain.RunRequest{
|
||||
PromptID: "example", ProfileID: "partial",
|
||||
Execution: &domain.ExecutionTargetOverride{Endpoint: "https://example.invalid/v1", Model: "request-model"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if prepared.SelectedProfileID != "partial" || prepared.EffectiveModelParams.Endpoint != "https://example.invalid/v1" ||
|
||||
prepared.EffectiveModelParams.Model != "request-model" {
|
||||
t.Fatalf("request did not complete the loaded profile: %+v", prepared)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadBuildsEffectiveTargetsAfterInheritance(t *testing.T) {
|
||||
state, err := load(mapping("effective-targets", map[string]any{
|
||||
"backends": []any{
|
||||
|
||||
Reference in New Issue
Block a user