First Commit
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Autumn-27/artex/db"
|
||||
)
|
||||
|
||||
// TestChatUnavailableReasonDistinguishesStates pins the operator-facing message
|
||||
// to the actual configuration state: nothing configured vs. configured-but-not-
|
||||
// activated must read differently so the user knows what to do.
|
||||
func TestChatUnavailableReasonDistinguishesStates(t *testing.T) {
|
||||
m, err := NewManager(t.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Skipf("postgres unavailable (%v) — skipping", err)
|
||||
}
|
||||
defer m.Close()
|
||||
td := t.TempDir()
|
||||
s := New(context.Background(), m, td, td, td)
|
||||
|
||||
// Start from a clean profile table; other tests in the shared DB may have left rows.
|
||||
existing, _ := m.pg.ListProfiles()
|
||||
for _, p := range existing {
|
||||
if p.IsDefault {
|
||||
// The active profile cannot be deleted directly; clear the flag first.
|
||||
_, _ = m.pg.Exec(`UPDATE llm_profiles SET is_default=false WHERE id=$1`, p.ID)
|
||||
}
|
||||
_ = m.pg.DeleteProfile(p.ID)
|
||||
}
|
||||
|
||||
if reason := s.chatUnavailableReason(); reason != errChatNoLLMProfile {
|
||||
t.Fatalf("no-profile reason=%q, want %q", reason, errChatNoLLMProfile)
|
||||
}
|
||||
|
||||
id, err := m.pg.SaveProfile(&db.LLMProfile{Name: "p1", Format: "anthropic", Model: "claude-x", APIKey: "sk-test"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _ = m.pg.Exec(`UPDATE llm_profiles SET is_default=false WHERE id=$1`, id)
|
||||
_ = m.pg.DeleteProfile(id)
|
||||
})
|
||||
|
||||
// Profile exists but is not activated.
|
||||
if reason := s.chatUnavailableReason(); reason != errChatNoActiveLLMProfile {
|
||||
t.Fatalf("inactive reason=%q, want %q", reason, errChatNoActiveLLMProfile)
|
||||
}
|
||||
|
||||
// Once activated, the reason no longer claims a missing/inactive config.
|
||||
if err := m.pg.SetActiveProfile(id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if reason := s.chatUnavailableReason(); reason == errChatNoLLMProfile || reason == errChatNoActiveLLMProfile {
|
||||
t.Fatalf("active reason=%q should not report missing/inactive", reason)
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveChatAgentHonoursConversationProfile is the regression guard for the
|
||||
// reported bug: a conversation that picked a valid profile must resolve a chat
|
||||
// agent even when NO global profile is active, so the send precheck stops
|
||||
// rejecting it with "LLM 未配置".
|
||||
func TestResolveChatAgentHonoursConversationProfile(t *testing.T) {
|
||||
m, err := NewManager(t.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Skipf("postgres unavailable (%v) — skipping", err)
|
||||
}
|
||||
defer m.Close()
|
||||
td := t.TempDir()
|
||||
s := New(context.Background(), m, td, td, td)
|
||||
|
||||
// Force the global fallback to nil so a non-nil result can ONLY come from the
|
||||
// conversation's own profile — this is exactly the situation the user hit
|
||||
// (no usable global config) and isolates the fix from the test host's env.
|
||||
s.cfgMu.Lock()
|
||||
s.chatAgent = nil
|
||||
s.cfgMu.Unlock()
|
||||
|
||||
id, err := m.pg.SaveProfile(&db.LLMProfile{Name: "conv-pick", Format: "anthropic", Model: "claude-x", APIKey: "sk-test"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = m.pg.DeleteProfile(id) })
|
||||
|
||||
// A conversation that pinned this profile resolves a chat agent despite the
|
||||
// nil global fallback; without the fix the send precheck rejected it outright.
|
||||
conv := &db.Conversation{AgentKey: "mainagent", LLMProfileID: &id}
|
||||
if s.resolveChatAgent(conv) == nil {
|
||||
t.Fatalf("resolveChatAgent(conversation with valid profile) = nil; global fallback wrongly gates the pick")
|
||||
}
|
||||
|
||||
// With neither a pick nor a global fallback there is genuinely nothing to run.
|
||||
if s.resolveChatAgent(&db.Conversation{AgentKey: "mainagent"}) != nil {
|
||||
t.Fatalf("resolveChatAgent with neither pick nor global config should be nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user