5759c1862e
M0-M7 已完成:核心网关(身份/RBAC/TOTP/OIDC/SAML/Provider/配额/路由/内容策略/审计/定价)+ 资源市场(MCP/Skills/数字员工)。 含 22 个 PostgreSQL 迁移、管理端/门户端前端源码、OpenAPI 契约、部署 compose。 Co-Authored-By: Claude <noreply@anthropic.com>
80 lines
3.4 KiB
Go
80 lines
3.4 KiB
Go
package runtime
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"io"
|
|
"log/slog"
|
|
"testing"
|
|
"time"
|
|
|
|
"aigateway.local/core/internal/gateway"
|
|
"aigateway.local/core/internal/provider"
|
|
provideropenai "aigateway.local/core/internal/provider/openai"
|
|
)
|
|
|
|
func TestBuildRuntimeAdapter(t *testing.T) {
|
|
key := base64.StdEncoding.EncodeToString(make([]byte, 32))
|
|
cipher, err := provider.NewCredentialCipher(key, 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
encrypted, version, err := cipher.Encrypt([]byte(`{"api_key":"upstream-secret"}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fallback, _ := provideropenai.New("https://fallback.example", "")
|
|
resolver := NewResolver(nil, cipher, fallback, time.Second, slog.New(slog.NewTextHandler(io.Discard, nil)))
|
|
resolved, defaultProvider, err := resolver.build(provider.Record{
|
|
Code: "primary", Adapter: "openai-compatible", BaseURL: "https://upstream.example/v1",
|
|
EncryptedCredentials: encrypted, CredentialKEKVersion: version,
|
|
Capabilities: []string{"chat", "models"}, Config: json.RawMessage(`{"default":true}`), Revision: 3,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resolved.Code != "primary" || resolved.Revision != 3 || !resolved.Capabilities[provider.CapabilityChat] || !defaultProvider {
|
|
t.Fatalf("unexpected resolved adapter: %#v, default=%v", resolved, defaultProvider)
|
|
}
|
|
}
|
|
|
|
func TestModelRouteUsesConditionsPriorityAndExplicitProvider(t *testing.T) {
|
|
first, _ := provideropenai.New("https://first.example", "")
|
|
second, _ := provideropenai.New("https://second.example", "")
|
|
resolver := &Resolver{}
|
|
resolver.snapshot.Store(&snapshot{
|
|
adapters: map[string]gateway.ResolvedAdapter{
|
|
"first": {Code: "first", Adapter: first}, "second": {Code: "second", Adapter: second},
|
|
},
|
|
routes: map[string][]compiledRoute{"public-chat": {
|
|
{providerCode: "first", targetModel: "model-a", weight: 100, priority: 10},
|
|
{providerCode: "second", targetModel: "model-b", weight: 100, priority: 20, conditions: routeConditions{Endpoints: []string{"/v1/chat/completions"}}},
|
|
}},
|
|
knownModels: map[string]bool{"public-chat": true},
|
|
})
|
|
result, err := resolver.ResolveModelRoute(gateway.ModelRouteQuery{Model: "public-chat", Endpoint: "/v1/chat/completions", Seed: "request-1"})
|
|
if err != nil || !result.Matched || result.Code != "second" || result.TargetModel != "model-b" {
|
|
t.Fatalf("unexpected priority route: %#v %v", result, err)
|
|
}
|
|
result, err = resolver.ResolveModelRoute(gateway.ModelRouteQuery{ProviderCode: "first", Model: "public-chat", Endpoint: "/v1/chat/completions", Seed: "request-1"})
|
|
if err != nil || !result.Matched || result.Code != "first" {
|
|
t.Fatalf("explicit provider was not respected: %#v %v", result, err)
|
|
}
|
|
result, err = resolver.ResolveModelRoute(gateway.ModelRouteQuery{ProviderCode: "second", Model: "public-chat", Endpoint: "/v1/embeddings", Seed: "request-1"})
|
|
if err != nil || result.Matched || !result.Known {
|
|
t.Fatalf("known alias with unmatched conditions must fail closed: %#v %v", result, err)
|
|
}
|
|
}
|
|
|
|
func TestResolverStartsWithEnvironmentFallback(t *testing.T) {
|
|
fallback, _ := provideropenai.New("https://fallback.example", "")
|
|
resolver := NewResolver(nil, nil, fallback, time.Second, slog.New(slog.NewTextHandler(io.Discard, nil)))
|
|
resolved, err := resolver.Resolve("")
|
|
if err != nil || resolved.Code != "environment" {
|
|
t.Fatalf("fallback resolve: %#v %v", resolved, err)
|
|
}
|
|
if _, err := resolver.Resolve("missing"); err == nil {
|
|
t.Fatal("unknown provider must be rejected")
|
|
}
|
|
}
|