71 lines
2.6 KiB
Go
71 lines
2.6 KiB
Go
package config
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestCheckRunnableRejectsPhase2Mechanics(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
p Pipeline
|
||
want string // substring the error must mention
|
||
}{
|
||
{"c1 ok", Pipeline{Core: "C1", Stages: []Stage{{Name: "draft", Role: "translator"}}}, ""},
|
||
{"c0 ok", Pipeline{Core: "C0", Stages: []Stage{{Name: "draft", Role: "translator"}}}, ""},
|
||
{"c2 rejected", Pipeline{Core: "C2", Stages: []Stage{{Name: "draft", Role: "translator"}}}, "C2"},
|
||
{"fanout rejected", Pipeline{Core: "C1", Fanout: Fanout{Candidates: 3}, Stages: []Stage{{Name: "draft", Role: "translator"}}}, "fanout"},
|
||
{"judge rejected", Pipeline{Core: "C1", Stages: []Stage{{Name: "select", Role: "judge"}}}, "judge"},
|
||
}
|
||
for _, c := range cases {
|
||
err := c.p.CheckRunnable()
|
||
if c.want == "" {
|
||
if err != nil {
|
||
t.Errorf("%s: expected runnable, got %v", c.name, err)
|
||
}
|
||
continue
|
||
}
|
||
if err == nil || !strings.Contains(err.Error(), c.want) {
|
||
t.Errorf("%s: expected error mentioning %q, got %v", c.name, c.want, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestCheckKeysOnlyUsedNonLocalModels(t *testing.T) {
|
||
m := &Models{
|
||
Providers: map[string]Provider{
|
||
"cloud": {Kind: "openai", BaseURL: "http://x", APIKeyEnv: "TEST_CLOUD_KEY"},
|
||
"local": {Kind: "local", BaseURL: "http://127.0.0.1", Model: "q"},
|
||
},
|
||
Models: map[string]Model{
|
||
"cloud-model": {Provider: "cloud"},
|
||
"local-model": {Provider: "local"},
|
||
"unused-cloud": {Provider: "cloud"},
|
||
},
|
||
}
|
||
// local-only pipeline не должен падать на облачном ключе.
|
||
localPipe := &Pipeline{Stages: []Stage{{Model: "local-model"}}}
|
||
if err := m.CheckKeys(localPipe); err != nil {
|
||
t.Fatalf("local-only pipeline must not require cloud keys: %v", err)
|
||
}
|
||
// Пайплайн с облачной моделью без ключа — fail-fast.
|
||
t.Setenv("TEST_CLOUD_KEY", "")
|
||
cloudPipe := &Pipeline{Stages: []Stage{{Model: "cloud-model"}}}
|
||
if err := m.CheckKeys(cloudPipe); err == nil || !strings.Contains(err.Error(), "TEST_CLOUD_KEY") {
|
||
t.Fatalf("missing cloud key must fail-fast mentioning the env var, got %v", err)
|
||
}
|
||
// Ключ задан — проходит.
|
||
t.Setenv("TEST_CLOUD_KEY", "sk-xxx")
|
||
if err := m.CheckKeys(cloudPipe); err != nil {
|
||
t.Fatalf("present key must pass: %v", err)
|
||
}
|
||
// Escalation-цепочки тоже учитываются.
|
||
escPipe := &Pipeline{
|
||
Stages: []Stage{{Model: "local-model"}},
|
||
Escal: Escalation{Chains: map[string][]string{"default": {"cloud-model"}}},
|
||
}
|
||
t.Setenv("TEST_CLOUD_KEY", "")
|
||
if err := m.CheckKeys(escPipe); err == nil {
|
||
t.Fatal("escalation chain model without a key must fail-fast")
|
||
}
|
||
}
|