textmachine/backend/internal/config/internal_call_test.go

96 lines
4.4 KiB
Go

package config
import (
"reflect"
"testing"
)
// TestInternalCallDecidesEveryStageField is the second half of the "a fourth synthetic stage cannot be
// forgotten" guarantee. The seam test in package pipeline forces every CALL SITE through InternalCall; this
// one forces every stage FIELD through a decision.
//
// The defect it prevents is the one that produced this pack: a field exists on Stage, an engine-internal
// call needs it, and nobody notices it is missing because a zero value is indistinguishable from a choice.
// Adding a field to Stage now fails HERE, at the one place that has to say what internal calls do with it —
// carry it, or deliberately leave it zero with the reason written next to it in internal_call.go.
func TestInternalCallDecidesEveryStageField(t *testing.T) {
carried := map[string]bool{
"Name": true,
"Role": true,
"Model": true,
"Reasoning": true,
"ResolvedModel": true,
}
// Every entry here is argued in InternalCall.Stage()'s "deliberately zero" block; the value is the
// one-line reason, kept next to the name so this list cannot silently become a dumping ground.
deliberatelyZero := map[string]string{
"Temperature": "structured replies the engine re-parses; sampling variance is pure risk",
"ReasoningMaxTokens": "gates carry no such key, and the loader refuses additive-billing models for them",
"PromptOverride": "internal calls arrive with messages already rendered",
"PromptPath": "same",
"PromptVersion": "same — each gate versions its own prompt",
"EscalateTo": "no hop; these calls degrade by leaving their work unchanged",
"ResolvedHop": "same",
"LabelModels": "no label routing; clientFor refuses an ineligible model loudly",
"FewShot": "few-shot is a prose-translation device",
"LegacyPrompt": "retired key, declared only to be rejected at load",
"LegacyPrompts": "same",
"LegacyChannel": "same",
}
st := reflect.TypeOf(Stage{})
for i := 0; i < st.NumField(); i++ {
name := st.Field(i).Name
if carried[name] == (deliberatelyZero[name] != "") {
t.Errorf("Stage field %q is not decided for engine-internal calls: name it in InternalCall.Stage() "+
"(carrying it) or in deliberatelyZero here (leaving it, with the reason) — exactly one of the two", name)
}
}
// And the reverse direction: a field REMOVED from Stage must not leave a stale decision behind.
for _, m := range []map[string]bool{carried, boolKeys(deliberatelyZero)} {
for name := range m {
if _, ok := st.FieldByName(name); !ok {
t.Errorf("%q is decided here but no longer exists on Stage — drop the stale entry", name)
}
}
}
}
func boolKeys(m map[string]string) map[string]bool {
out := make(map[string]bool, len(m))
for k := range m {
out[k] = true
}
return out
}
// TestInternalCallCarriesTheKnobAndPinsTheRest is the value-level companion: the fields the seam DOES set
// arrive intact, and the ones it pins stay pinned. Cheap, but it is what makes the completeness test above
// mean something — a seam could satisfy "every field is listed" while carrying the wrong value.
func TestInternalCallCarriesTheKnobAndPinsTheRest(t *testing.T) {
got := InternalCall{Name: "terminology", Role: "terminologist", Model: "m1", Reasoning: "low"}.Stage()
if got.Name != "terminology" || got.Role != "terminologist" || got.Model != "m1" || got.Reasoning != "low" {
t.Fatalf("the call's own four facts must arrive intact: %+v", got)
}
if got.ResolvedModel != "m1" {
t.Fatalf("an internal call is not label-routed, so the resolved model IS the model: %q", got.ResolvedModel)
}
if got.Temperature != 0 || got.ReasoningMaxTokens != 0 || got.FewShot != nil || got.EscalateTo != "" {
t.Fatalf("the pinned fields must stay zero: %+v", got)
}
}
func TestValidReasoningEffortIsOneVocabulary(t *testing.T) {
for _, ok := range []string{"", "off", "low", "medium", "high"} {
if !ValidReasoningEffort(ok) {
t.Errorf("%q must be accepted — it is the engine's neutral effort vocabulary", ok)
}
}
// The vendor's own words are NOT ours: they are mapped by the capability layer, and accepting them here
// would let a config name a level the wire has no meaning for.
for _, bad := range []string{"none", "xhigh", "max", "minimal", "LOW"} {
if ValidReasoningEffort(bad) {
t.Errorf("%q must be refused — the engine's vocabulary is provider-neutral", bad)
}
}
}