package aiprovider import ( "context" "testing" "github.com/descrybe/descrybe-v2/apps/api/internal/platformsettings" "github.com/descrybe/descrybe-v2/apps/api/internal/processing" "github.com/google/uuid" ) type stubRoleSource struct { ep RoleEndpoint ok bool err error } func (s stubRoleSource) LookupRole(_ context.Context, _ uuid.UUID, _ string) (RoleEndpoint, bool, error) { return s.ep, s.ok, s.err } func TestResolveCompleterForRole_unsetFallsBackToEnv(t *testing.T) { svc := &Service{ Env: EnvConfig{ OpenAIAPIKey: "sk-env-fallback", OpenAIBaseURL: "https://api.openai.com/v1", OpenAIModel: "gpt-4o-mini", }, } c, label, byok, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleProcessing) if err != nil { t.Fatal(err) } oc, ok := c.(*processing.OpenAIClient) if !ok || oc == nil || !oc.Enabled() { t.Fatalf("expected enabled OpenAIClient, got %T", c) } if label != ModeInternalLabel { t.Fatalf("label=%q", label) } if byok { t.Fatal("env fallback must not be BYOK") } } func TestResolveCompleterForRole_usesRoleBindingWhenSet(t *testing.T) { svc := &Service{ Env: EnvConfig{ OpenAIAPIKey: "sk-should-not-win", OpenAIModel: "env-model", }, Roles: stubRoleSource{ ok: true, ep: RoleEndpoint{ APIKey: "sk-role-processing", BaseURL: "https://role.example/v1", Model: "role-model", UsingBYOK: false, ModeLabel: ModeInternalLabel, }, }, } c, label, byok, err := svc.ResolveCompleterForRole(context.Background(), uuid.New(), RoleProcessing) if err != nil { t.Fatal(err) } oc, ok := c.(*processing.OpenAIClient) if !ok || oc == nil { t.Fatalf("type=%T", c) } if oc.APIKey != "sk-role-processing" || oc.Model != "role-model" { t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model) } if label != ModeInternalLabel || byok { t.Fatalf("label=%q byok=%v", label, byok) } } func TestResolveCompleterForRole_platformProcessingRole(t *testing.T) { plat := platformsettings.NewService(nil, platformsettings.EnvConfig{ OpenAIAPIKey: "sk-plat-processing", OpenAIBaseURL: "http://127.0.0.1:8767/v1", OpenAIModel: "plat-model", }) svc := &Service{ Platform: plat, Env: EnvConfig{OpenAIAPIKey: "sk-should-not-win", OpenAIModel: "env-model"}, } c, label, byok, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleProcessing) if err != nil { t.Fatal(err) } oc, ok := c.(*processing.OpenAIClient) if !ok || oc == nil { t.Fatalf("type=%T", c) } if oc.APIKey != "sk-plat-processing" || oc.Model != "plat-model" { t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model) } if label != ModeInternalLabel || byok { t.Fatalf("label=%q byok=%v", label, byok) } } func TestResolveCompleterForRole_vectorizationUnsetNoChatFallback(t *testing.T) { svc := &Service{ Env: EnvConfig{OpenAIAPIKey: "sk-env", OpenAIModel: "m"}, } c, _, _, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleVectorization) if err != nil { t.Fatal(err) } if c != nil { t.Fatal("vectorization must not fall back to chat completer") } } func TestResolveEmbedderForRole_usesPlatformVectorization(t *testing.T) { plat := platformsettings.NewService(nil, platformsettings.EnvConfig{ OpenAIAPIKey: "sk-embed-env", OpenAIBaseURL: "http://127.0.0.1:8767/v1", OpenAIEmbeddingModel: "text-embedding-3-small", }) svc := &Service{Platform: plat} emb, err := svc.ResolveEmbedderForRole(context.Background(), uuid.Nil, RoleVectorization) if err != nil { t.Fatal(err) } oc, ok := emb.(*processing.OpenAIClient) if !ok || oc == nil || !oc.Enabled() { t.Fatalf("expected OpenAIClient embedder, got %T", emb) } if oc.APIKey != "sk-embed-env" || oc.Model != "text-embedding-3-small" { t.Fatalf("key=%q model=%q", oc.APIKey, oc.Model) } } func TestResolveCompleterForRole_supportUnsetNoEnvFallback(t *testing.T) { svc := &Service{ Env: EnvConfig{OpenAIAPIKey: "sk-env", OpenAIModel: "m"}, } c, _, _, err := svc.ResolveCompleterForRole(context.Background(), uuid.Nil, RoleSupport) if err != nil { t.Fatal(err) } if c != nil { t.Fatal("unset support must not fall back to processing/env completer") } }