Add prototype testing overlay configuration
This commit is contained in:
@@ -74,7 +74,9 @@ historical v0.3.1 evidence. Do not claim overlay integration yet.
|
|||||||
|
|
||||||
## Stage 2: Add Overlay Loading and Configuration Fixtures
|
## Stage 2: Add Overlay Loading and Configuration Fixtures
|
||||||
|
|
||||||
**Status:** Pending. **Depends on:** Stage 1.
|
**Status:** Complete (2026-09-24). **Depends on:** Stage 1. On-disk overlay and
|
||||||
|
profile fixtures, precedence, inheritance, rejection, and runtime assembly tests
|
||||||
|
pass in the nested module (`go test ./...`).
|
||||||
|
|
||||||
Start with `config.go`, `config_test.go`, `config.yaml`, and `profiles/`. Extend
|
Start with `config.go`, `config_test.go`, `config.yaml`, and `profiles/`. Extend
|
||||||
`loadConfig` with an explicit overlay-selection argument or a small invocation
|
`loadConfig` with an explicit overlay-selection argument or a small invocation
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ type LLMConfig struct {
|
|||||||
OperationTimeout time.Duration `yaml:"operation_timeout"`
|
OperationTimeout time.Duration `yaml:"operation_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig(source ck.Source, overrides ...ck.Override) (*ck.Result[Config], error) {
|
func loadConfig(source ck.Source, overlay string, overrides ...ck.Override) (*ck.Result[Config], error) {
|
||||||
return ck.Load(source, ck.Options[Config]{
|
return ck.Load(source, ck.Options[Config]{
|
||||||
|
Overlay: overlay,
|
||||||
Overrides: overrides,
|
Overrides: overrides,
|
||||||
Definition: ck.Definition[Config]{
|
Definition: ck.Definition[Config]{
|
||||||
Defaults: func() Config {
|
Defaults: func() Config {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ func TestConfigurationContract(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Setenv("AUDITA_PROTOTYPE_KEY", tc.key)
|
t.Setenv("AUDITA_PROTOTYPE_KEY", tc.key)
|
||||||
loaded, err := loadConfig(ck.Bytes("example", []byte(tc.edit(string(original)))))
|
loaded, err := loadConfig(ck.Bytes("example", []byte(tc.edit(string(original)))), "")
|
||||||
if tc.bad {
|
if tc.bad {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected rejection")
|
t.Fatal("expected rejection")
|
||||||
@@ -90,11 +91,11 @@ func TestConfigurationContract(t *testing.T) {
|
|||||||
if err := os.Unsetenv("AUDITA_PROTOTYPE_KEY"); err != nil {
|
if err := os.Unsetenv("AUDITA_PROTOTYPE_KEY"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err := loadConfig(ck.Bytes("example", original)); err == nil {
|
if _, err := loadConfig(ck.Bytes("example", original), ""); err == nil {
|
||||||
t.Fatal("missing secret accepted")
|
t.Fatal("missing secret accepted")
|
||||||
}
|
}
|
||||||
t.Setenv("AUDITA_PROTOTYPE_KEY", "key")
|
t.Setenv("AUDITA_PROTOTYPE_KEY", "key")
|
||||||
loaded, err := loadConfig(ck.Bytes("example", original), intOverride("transient_retries", 0), ck.Override{Path: ck.Keys("llm", "validation_profile"), Value: "proposal", Flag: "--validation-profile"})
|
loaded, err := loadConfig(ck.Bytes("example", original), "", intOverride("transient_retries", 0), ck.Override{Path: ck.Keys("llm", "validation_profile"), Value: "proposal", Flag: "--validation-profile"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -103,9 +104,90 @@ func TestConfigurationContract(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelectedOverlayConfiguration(t *testing.T) {
|
||||||
|
t.Setenv("AUDITA_PROTOTYPE_KEY", "synthetic-test-secret")
|
||||||
|
base, err := os.ReadFile("config.yaml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
overlay, err := os.ReadFile("overlays/testing.yml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name, base, overlay, selection, proposal, validation string
|
||||||
|
overrides []ck.Override
|
||||||
|
invalid bool
|
||||||
|
}{
|
||||||
|
{name: "omitted", base: string(base), overlay: string(overlay), proposal: "proposal", validation: "assessment"},
|
||||||
|
{name: "empty", base: string(base), overlay: string(overlay), selection: "", proposal: "proposal", validation: "assessment"},
|
||||||
|
{name: "default", base: string(base), overlay: string(overlay), selection: "default", proposal: "proposal", validation: "assessment"},
|
||||||
|
{name: "testing", base: string(base), overlay: string(overlay), selection: "testing", proposal: "proposal-testing", validation: "assessment-testing"},
|
||||||
|
{name: "proposal override", base: string(base), overlay: string(overlay), selection: "testing", proposal: "proposal", validation: "assessment-testing", overrides: []ck.Override{{Path: ck.Keys("llm", "proposal_profile"), Value: "proposal", Flag: "--proposal-profile"}}},
|
||||||
|
{name: "validation override", base: string(base), overlay: string(overlay), selection: "testing", proposal: "proposal-testing", validation: "assessment", overrides: []ck.Override{{Path: ck.Keys("llm", "validation_profile"), Value: "assessment", Flag: "--validation-profile"}}},
|
||||||
|
{name: "inherited overlay proposal", base: strings.Replace(string(base), " validation_profile: assessment\n", "", 1), overlay: "llm:\n proposal_profile: proposal-testing\n", selection: "testing", proposal: "proposal-testing", validation: "proposal-testing"},
|
||||||
|
{name: "inherited override", base: strings.Replace(string(base), " validation_profile: assessment\n", "", 1), overlay: "llm:\n proposal_profile: proposal-testing\n", selection: "testing", proposal: "proposal", validation: "proposal", overrides: []ck.Override{{Path: ck.Keys("llm", "proposal_profile"), Value: "proposal", Flag: "--proposal-profile"}}},
|
||||||
|
{name: "explicit base assessment", base: string(base), overlay: "llm:\n proposal_profile: proposal-testing\n", selection: "testing", proposal: "proposal-testing", validation: "assessment"},
|
||||||
|
{name: "empty overlay assessment", base: string(base), overlay: "llm:\n validation_profile: ''\n", selection: "testing", invalid: true},
|
||||||
|
{name: "null overlay assessment", base: string(base), overlay: "llm:\n validation_profile: null\n", selection: "testing", invalid: true},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
if err := os.MkdirAll(filepath.Join(dir, "overlays"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "config.yaml"), []byte(tc.base), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "overlays", "testing.yml"), []byte(tc.overlay), 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
loaded, err := loadConfig(ck.File(filepath.Join(dir, "config.yaml")), tc.selection, tc.overrides...)
|
||||||
|
if tc.invalid {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected invalid validation selection")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := loaded.Config.LLM; got.ProposalProfile != tc.proposal || got.ValidationProfile != tc.validation {
|
||||||
|
t.Fatalf("roles = %q, %q; want %q, %q", got.ProposalProfile, got.ValidationProfile, tc.proposal, tc.validation)
|
||||||
|
}
|
||||||
|
for _, name := range []string{"proposal.yaml", "assessment.yaml", "proposal-testing.yaml", "assessment-testing.yaml"} {
|
||||||
|
data, err := os.ReadFile(filepath.Join("profiles", name))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(dir, "profiles"), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "profiles", name), data, 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r, err := newRuntime(loaded.Config, dir, demonstrationClient())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for role, selected := range map[string]string{"proposal": tc.proposal, "assessment": tc.validation} {
|
||||||
|
profile := r.Profiles[role]
|
||||||
|
if profile.ProfileID != selected || profile.EffectiveModelParams.BackendID != "offline" {
|
||||||
|
t.Fatalf("%s resolved to %+v", role, profile)
|
||||||
|
}
|
||||||
|
models := map[string]string{"proposal": "synthetic-grammar", "assessment": "synthetic-review", "proposal-testing": "synthetic-grammar-testing", "assessment-testing": "synthetic-review-testing"}
|
||||||
|
if profile.EffectiveModelParams.Model != models[selected] {
|
||||||
|
t.Fatalf("%s model = %q", role, profile.EffectiveModelParams.Model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProfilesRespectSharedBackendBoundary(t *testing.T) {
|
func TestProfilesRespectSharedBackendBoundary(t *testing.T) {
|
||||||
t.Setenv("AUDITA_PROTOTYPE_KEY", "synthetic-test-secret")
|
t.Setenv("AUDITA_PROTOTYPE_KEY", "synthetic-test-secret")
|
||||||
loaded, err := loadConfig(ck.File("config.yaml"))
|
loaded, err := loadConfig(ck.File("config.yaml"), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func main() {
|
|||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
loaded, err := loadConfig(ck.File("config.yaml"))
|
loaded, err := loadConfig(ck.File("config.yaml"), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
3
examples/library-adoption/overlays/testing.yml
Normal file
3
examples/library-adoption/overlays/testing.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
llm:
|
||||||
|
proposal_profile: proposal-testing
|
||||||
|
validation_profile: assessment-testing
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
id: assessment-testing
|
||||||
|
backend: offline
|
||||||
|
model: synthetic-review-testing
|
||||||
|
timeout_seconds: 600
|
||||||
4
examples/library-adoption/profiles/proposal-testing.yaml
Normal file
4
examples/library-adoption/profiles/proposal-testing.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
id: proposal-testing
|
||||||
|
backend: offline
|
||||||
|
model: synthetic-grammar-testing
|
||||||
|
timeout_seconds: 600
|
||||||
@@ -22,7 +22,7 @@ type step struct {
|
|||||||
func testRuntime(t *testing.T, transport http.RoundTripper, overrides ...ck.Override) *Runtime {
|
func testRuntime(t *testing.T, transport http.RoundTripper, overrides ...ck.Override) *Runtime {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
t.Setenv("AUDITA_PROTOTYPE_KEY", "synthetic-test-secret")
|
t.Setenv("AUDITA_PROTOTYPE_KEY", "synthetic-test-secret")
|
||||||
loaded, err := loadConfig(ck.File("config.yaml"), overrides...)
|
loaded, err := loadConfig(ck.File("config.yaml"), "", overrides...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user