test(reasoning): cover default-mode routing, user pinning and 000022 contract
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package migrations_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUp022AddsReasoningUserSelectedColumn(t *testing.T) {
|
||||
data, err := os.ReadFile("000022_reasoning_default_mode.up.sql")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source := strings.ToUpper(string(data))
|
||||
for _, required := range []string{
|
||||
"ALTER TABLE REASONING_MODE_STATES",
|
||||
"ADD COLUMN USER_SELECTED BOOLEAN NOT NULL DEFAULT FALSE",
|
||||
} {
|
||||
if !strings.Contains(source, required) {
|
||||
t.Errorf("up migration missing %q", required)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDown022DropsUserSelectedColumn(t *testing.T) {
|
||||
data, err := os.ReadFile("000022_reasoning_default_mode.down.sql")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source := strings.ToUpper(string(data))
|
||||
if !strings.Contains(source, "DROP COLUMN USER_SELECTED") {
|
||||
t.Errorf("down migration missing %q", "DROP COLUMN USER_SELECTED")
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ func (f *fakeRepository) ResolveState(context.Context, Scope, time.Time) (State,
|
||||
func (f *fakeRepository) Enable(context.Context, Scope, time.Time, time.Duration) (State, error) {
|
||||
f.enableCalls++
|
||||
f.state.Mode = ModeReasoning
|
||||
f.state.UserSelected = true
|
||||
f.state.RoundsUsed = 0
|
||||
return f.state, nil
|
||||
}
|
||||
@@ -31,6 +32,7 @@ func (f *fakeRepository) Disable(context.Context, Scope, time.Time) (State, bool
|
||||
f.disableCalls++
|
||||
wasReasoning := f.state.Mode == ModeReasoning
|
||||
f.state.Mode = ModeQuick
|
||||
f.state.UserSelected = true
|
||||
f.state.RoundsUsed = 0
|
||||
return f.state, wasReasoning, nil
|
||||
}
|
||||
@@ -177,3 +179,87 @@ func TestPrepareReturnsLocalHelpWithoutStateMutation(t *testing.T) {
|
||||
t.Fatalf("help mutated mode: %+v", repo)
|
||||
}
|
||||
}
|
||||
|
||||
func newTestServiceWithDefault(t *testing.T, repo Repository, mode Mode) *Service {
|
||||
t.Helper()
|
||||
service, err := NewService(repo, "builtin-quick-answer", "builtin-smart-reasoning", WithDefaultMode(mode))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service.now = func() time.Time { return time.Date(2026, 9, 15, 12, 0, 0, 0, time.UTC) }
|
||||
return service
|
||||
}
|
||||
|
||||
func TestNewServiceRejectsInvalidDefaultMode(t *testing.T) {
|
||||
if _, err := NewService(&fakeRepository{}, "q", "r", WithDefaultMode("turbo")); err == nil {
|
||||
t.Fatal("expected invalid default mode to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareDefaultReasoningRoutesUnselectedUserToReasoningSingle(t *testing.T) {
|
||||
repo := &fakeRepository{state: DefaultState()}
|
||||
decision, err := newTestServiceWithDefault(t, repo, ModeReasoning).Prepare(context.Background(), testScope(), "普通问题")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decision.AgentID != "builtin-smart-reasoning" || decision.Execution != ExecutionReasoningSingle {
|
||||
t.Fatalf("decision=%+v", decision)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareDefaultReasoningHonoursUserQuickSelection(t *testing.T) {
|
||||
state := DefaultState()
|
||||
state.UserSelected = true
|
||||
decision, err := newTestServiceWithDefault(t, &fakeRepository{state: state}, ModeReasoning).
|
||||
Prepare(context.Background(), testScope(), "普通问题")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decision.AgentID != "builtin-quick-answer" || decision.Execution != ExecutionQuick {
|
||||
t.Fatalf("decision=%+v", decision)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareDefaultQuickKeepsLegacyBehaviour(t *testing.T) {
|
||||
decision, err := newTestServiceWithDefault(t, &fakeRepository{state: DefaultState()}, ModeQuick).
|
||||
Prepare(context.Background(), testScope(), "普通问题")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decision.AgentID != "builtin-quick-answer" || decision.Execution != ExecutionQuick {
|
||||
t.Fatalf("decision=%+v", decision)
|
||||
}
|
||||
}
|
||||
|
||||
func TestThinkOffPinsQuickModeUnderReasoningDefault(t *testing.T) {
|
||||
repo := &fakeRepository{state: DefaultState()}
|
||||
service := newTestServiceWithDefault(t, repo, ModeReasoning)
|
||||
decision, err := service.Prepare(context.Background(), testScope(), "/think off")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !decision.Handled || !strings.Contains(decision.Reply, "切回") {
|
||||
t.Fatalf("decision=%+v", decision)
|
||||
}
|
||||
if !repo.state.UserSelected || repo.state.Mode != ModeQuick {
|
||||
t.Fatalf("state=%+v", repo.state)
|
||||
}
|
||||
followUp, err := service.Prepare(context.Background(), testScope(), "后续问题")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if followUp.AgentID != "builtin-quick-answer" {
|
||||
t.Fatalf("follow-up escaped user quick selection: %+v", followUp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpReflectsReasoningDefaultMode(t *testing.T) {
|
||||
decision, err := newTestServiceWithDefault(t, &fakeRepository{state: DefaultState()}, ModeReasoning).
|
||||
Prepare(context.Background(), testScope(), "/help")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(decision.Reply, "默认模式:智能推理模式") {
|
||||
t.Fatalf("help=%q", decision.Reply)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user