Add ConfigKit application definition and attribution
This commit is contained in:
163
internal/config/document_test.go
Normal file
163
internal/config/document_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
)
|
||||
|
||||
func loadDocumentBytes(content string) (*configkit.Result[Document], error) {
|
||||
return configkit.Load(configkit.Bytes("application.yml", []byte(content)), configkit.Options[Document]{
|
||||
Definition: documentDefinition(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestDocumentDefaultsAndExplicitValues(t *testing.T) {
|
||||
defaultsOnly, err := configkit.Load(configkit.NoSource(), configkit.Options[Document]{Definition: documentDefinition()})
|
||||
if err != nil {
|
||||
t.Fatalf("defaults-only load: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(defaultsOnly.Config, documentDefaults()) || len(defaultsOnly.Sources()) != 0 {
|
||||
t.Fatalf("unexpected defaults-only result: %+v sources=%+v", defaultsOnly.Config, defaultsOnly.Sources())
|
||||
}
|
||||
|
||||
result, err := loadDocumentBytes(`prompt_dir: ""
|
||||
profile_dir: ""
|
||||
schema_dir: ""
|
||||
transient_retries: 0
|
||||
server:
|
||||
addr: ""
|
||||
artifact_root: ""
|
||||
max_request_bytes: 0
|
||||
max_artifact_bytes: 0
|
||||
max_response_bytes: 0
|
||||
defaults:
|
||||
render_format: " JSON "
|
||||
backends:
|
||||
local:
|
||||
endpoint: "http://fixture.invalid/v1"
|
||||
queue_capacity: 0
|
||||
extra_params:
|
||||
sample_count: 23
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("explicit-value load: %v", err)
|
||||
}
|
||||
config := result.Config
|
||||
if config.SchemaDir != "" || config.Server.Addr != "" || config.Server.MaxRequestBytes != 0 || config.Server.MaxArtifactBytes != 0 || config.Server.MaxResponseBytes != 0 {
|
||||
t.Fatalf("explicit empty and zero values did not replace defaults: %+v", config)
|
||||
}
|
||||
if config.TransientRetries == nil || *config.TransientRetries != 0 {
|
||||
t.Fatalf("explicit zero retry budget lost: %+v", config.TransientRetries)
|
||||
}
|
||||
if backend := config.Backends["local"]; backend.QueueCapacity == nil || *backend.QueueCapacity != 0 || backend.ExtraParams["sample_count"] != int64(23) {
|
||||
t.Fatalf("backend presence or dynamic integer changed: %+v", backend)
|
||||
}
|
||||
if config.Defaults.RenderFormat != "json" {
|
||||
t.Fatalf("render format normalization failed: %q", config.Defaults.RenderFormat)
|
||||
}
|
||||
|
||||
nullable, err := loadDocumentBytes("transient_retries: null\nbackends:\n local:\n queue_capacity: null\n")
|
||||
if err != nil {
|
||||
t.Fatalf("nullable-value load: %v", err)
|
||||
}
|
||||
if nullable.Config.TransientRetries != nil || nullable.Config.Backends["local"].QueueCapacity != nil {
|
||||
t.Fatalf("nullable fields did not remain nil: %+v", nullable.Config)
|
||||
}
|
||||
|
||||
nullBackends, err := loadDocumentBytes("backends: null\n")
|
||||
if err != nil || nullBackends.Config.Backends != nil {
|
||||
t.Fatalf("nullable backend map did not remain nil: %+v error=%v", nullBackends, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentRecordsEffectiveNormalization(t *testing.T) {
|
||||
result, err := loadDocumentBytes(`prompt_dir: " ./one/../two "
|
||||
profile_dir: " "
|
||||
server:
|
||||
artifact_root: " ./data/../artifacts "
|
||||
addr: " :9000 "
|
||||
defaults:
|
||||
render_format: " JSON "
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("load normalized document: %v", err)
|
||||
}
|
||||
if result.Config.PromptDir != "two" || result.Config.ProfileDir != "" || result.Config.Server.ArtifactRoot != "artifacts" || result.Config.Server.Addr != ":9000" {
|
||||
t.Fatalf("unexpected normalized values: %+v", result.Config)
|
||||
}
|
||||
var effective bytes.Buffer
|
||||
if err := result.WriteEffective(&effective); err != nil {
|
||||
t.Fatalf("write effective configuration: %v", err)
|
||||
}
|
||||
for _, fragment := range []string{"prompt_dir: two", "profile_dir: ''", "artifact_root: artifacts", "addr: :9000", "render_format: json"} {
|
||||
if !strings.Contains(effective.String(), fragment) {
|
||||
t.Fatalf("effective output omitted %q: %s", fragment, effective.String())
|
||||
}
|
||||
}
|
||||
explanation, known := result.Explain(configkit.Keys("prompt_dir"))
|
||||
if !known || explanation.Effective.YAML != "two\n" {
|
||||
t.Fatalf("explanation does not agree with effective value: %+v known=%t", explanation, known)
|
||||
}
|
||||
found := false
|
||||
for _, change := range explanation.History {
|
||||
if change.Origin.Operation == configkit.ChangeNormalization {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("normalization was not recorded: %+v", explanation.History)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentValidationIsSafeAndPathSpecific(t *testing.T) {
|
||||
for _, tc := range []struct{ name, content, path string }{
|
||||
{"negative retry", "transient_retries: -9173\n", "/transient_retries"},
|
||||
{"negative request limit", "server:\n max_request_bytes: -9173\n", "/server/max_request_bytes"},
|
||||
{"negative artifact limit", "server:\n max_artifact_bytes: -9173\n", "/server/max_artifact_bytes"},
|
||||
{"negative response limit", "server:\n max_response_bytes: -9173\n", "/server/max_response_bytes"},
|
||||
{"empty format", "defaults:\n render_format: \"\"\n", "/defaults/render_format"},
|
||||
{"unknown format", "defaults:\n render_format: private-invalid-value\n", "/defaults/render_format"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := loadDocumentBytes(tc.content)
|
||||
var loadError *configkit.LoadError
|
||||
if !errors.As(err, &loadError) {
|
||||
t.Fatalf("expected typed validation error, got %v", err)
|
||||
}
|
||||
found := false
|
||||
for _, diagnostic := range loadError.Diagnostics() {
|
||||
if diagnostic.Code == configkit.CodeValidateError && diagnostic.Path.String() == tc.path {
|
||||
found = true
|
||||
}
|
||||
if strings.Contains(diagnostic.Message, "9173") || strings.Contains(diagnostic.Message, "private-invalid-value") {
|
||||
t.Fatalf("diagnostic disclosed rejected value: %+v", diagnostic)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("expected validation at %s, got %+v", tc.path, loadError.Diagnostics())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentRejectsNativeShapeErrors(t *testing.T) {
|
||||
for _, content := range []string{
|
||||
"server: null\n",
|
||||
"defaults: null\n",
|
||||
"server:\n addr: null\n",
|
||||
"server:\n max_request_bytes: null\n",
|
||||
"defaults:\n render_format: null\n",
|
||||
"server:\n unknown_field: 1\n",
|
||||
"backends:\n local:\n unknown_field: 1\n",
|
||||
} {
|
||||
_, err := loadDocumentBytes(content)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected acceptance of %q", content)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user