ai-agent/internal/integration/integration_test.go
admin 8dc496b626
Some checks failed
CI / test (push) Has been cancelled
Release / release (push) Failing after 4m36s
first commit
2026-03-08 15:40:34 +07:00

231 lines
7.5 KiB
Go

//go:build integration
// +build integration
package integration
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
"ai-agent/internal/agent"
"ai-agent/internal/command"
"ai-agent/internal/config"
"ai-agent/internal/llm"
"ai-agent/internal/mcp"
"ai-agent/internal/tui"
tea "charm.land/bubbletea/v2"
)
func skipIfNoOllama(t *testing.T) {
if os.Getenv("OLLAMA_HOST") == "" {
os.Setenv("OLLAMA_HOST", "http://localhost:11434")
}
client := llm.NewClient(llm.Config{
BaseURL: os.Getenv("OLLAMA_HOST"),
Model: "qwen3.5:2b",
NumCtx: 262144,
})
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := client.Ping(ctx); err != nil {
t.Skip("Ollama not available: skipping integration test")
}
}
func TestTUI_Initialization(t *testing.T) {
skipIfNoOllama(t)
reg := command.NewRegistry()
command.RegisterBuiltins(reg)
cfg := config.DefaultModelConfig()
router := config.NewRouter(&cfg)
modelManager := llm.NewModelManager("http://localhost:11434", 262144)
modelManager.SetCurrentModel("qwen3.5:2b")
ag := agent.New(modelManager, mcp.NewRegistry(), cfg.Ollama.NumCtx)
ag.SetRouter(router)
completer := tui.NewCompleter(reg, []string{"qwen3.5:2b"}, nil, nil, nil)
m := tui.New(ag, reg, nil, completer, modelManager, router, nil)
updated, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
m = updated.(*tui.Model)
if !m.Ready() {
t.Error("TUI should be ready after WindowSizeMsg")
}
}
func TestTUI_ScrollAnchorDuringStreaming(t *testing.T) {
skipIfNoOllama(t)
reg := command.NewRegistry()
command.RegisterBuiltins(reg)
cfg := config.DefaultModelConfig()
router := config.NewRouter(&cfg)
modelManager := llm.NewModelManager("http://localhost:11434", 262144)
ag := agent.New(modelManager, mcp.NewRegistry(), 262144)
ag.SetRouter(router)
completer := tui.NewCompleter(reg, []string{"qwen3.5:2b"}, nil, nil, nil)
m := tui.New(ag, reg, nil, completer, modelManager, router, nil)
updated, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
m = updated.(*tui.Model)
if !m.AnchorActive() {
t.Error("anchorActive should be true after initialization")
}
updated, _ = m.Update(tui.StreamTextMsg{Text: "Hello"})
m = updated.(*tui.Model)
if !m.AnchorActive() {
t.Error("anchorActive should remain true during streaming")
}
}
func TestTUI_OverlayRendering(t *testing.T) {
reg := command.NewRegistry()
command.RegisterBuiltins(reg)
cfg := config.DefaultModelConfig()
router := config.NewRouter(&cfg)
modelManager := llm.NewModelManager("http://localhost:11434", 262144)
ag := agent.New(modelManager, mcp.NewRegistry(), 262144)
ag.SetRouter(router)
completer := tui.NewCompleter(reg, []string{"qwen3.5:2b"}, nil, nil, nil)
m := tui.New(ag, reg, nil, completer, modelManager, router, nil)
updated, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
m = updated.(*tui.Model)
updated, _ = m.Update(tui.KeyPressMsg{Code: '?'})
m = updated.(*tui.Model)
view := m.View()
if view == nil {
t.Error("View should not be nil")
}
updated, _ = m.Update(tui.KeyPressMsg{Code: tea.KeyEscape})
m = updated.(*tui.Model)
}
func TestTUI_ToolCardRendering(t *testing.T) {
reg := command.NewRegistry()
command.RegisterBuiltins(reg)
cfg := config.DefaultModelConfig()
router := config.NewRouter(&cfg)
modelManager := llm.NewModelManager("http://localhost:11434", 262144)
ag := agent.New(modelManager, mcp.NewRegistry(), 262144)
ag.SetRouter(router)
completer := tui.NewCompleter(reg, []string{"qwen3.5:2b"}, nil, nil, nil)
m := tui.New(ag, reg, nil, completer, modelManager, router, nil)
updated, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
m = updated.(*tui.Model)
startTime := time.Now()
updated, _ = m.Update(tui.ToolCallStartMsg{
Name: "read_file",
Args: map[string]any{"path": "test.go"},
StartTime: startTime,
})
m = updated.(*tui.Model)
updated, _ = m.Update(tui.ToolCallResultMsg{
Name: "read_file",
Result: "file content",
IsError: false,
Duration: 100 * time.Millisecond,
})
m = updated.(*tui.Model)
view := m.View()
if view == nil {
t.Error("View should not be nil after tool execution")
}
}
func TestQwenRouter_Integration(t *testing.T) {
cfg := config.DefaultModelConfig()
router := config.NewQwenModelRouter(&cfg)
tests := []struct {
query string
mode config.ModeContext
expectSmaller string
expectLarger string
}{
{"what is go?", config.ModeAskContext, "qwen3.5:2b", ""},
{"design architecture", config.ModeBuildContext, "", "qwen3.5:4b"},
{"plan the system", config.ModePlanContext, "", "qwen3.5:4b"},
}
for _, tt := range tests {
t.Run(tt.query, func(t *testing.T) {
model := router.SelectModelForMode(tt.query, tt.mode)
if tt.expectSmaller != "" {
if modelRank(model) > modelRank(tt.expectSmaller) {
t.Errorf("model %s is larger than expected %s", model, tt.expectSmaller)
}
}
if tt.expectLarger != "" {
if modelRank(model) < modelRank(tt.expectLarger) {
t.Errorf("model %s is smaller than expected %s", model, tt.expectLarger)
}
}
})
}
}
func modelRank(model string) int {
switch {
case strings.Contains(model, "0.8b"):
return 1
case strings.Contains(model, "2b"):
return 2
case strings.Contains(model, "4b"):
return 3
case strings.Contains(model, "9b"):
return 4
default:
return 0
}
}
func TestFileOperations_Integration(t *testing.T) {
skipIfNoOllama(t)
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.txt")
if err := os.WriteFile(testFile, []byte("hello world"), 0644); err != nil {
t.Fatalf("failed to create test file: %v", err)
}
content, err := os.ReadFile(testFile)
if err != nil {
t.Fatalf("failed to read test file: %v", err)
}
if string(content) != "hello world" {
t.Errorf("unexpected file content: %q", string(content))
}
}
func BenchmarkTUI_Render(b *testing.B) {
reg := command.NewRegistry()
command.RegisterBuiltins(reg)
cfg := config.DefaultModelConfig()
router := config.NewRouter(&cfg)
modelManager := llm.NewModelManager("http://localhost:11434", 262144)
ag := agent.New(modelManager, mcp.NewRegistry(), 262144)
ag.SetRouter(router)
completer := tui.NewCompleter(reg, []string{"qwen3.5:2b"}, nil, nil, nil)
m := tui.New(ag, reg, nil, completer, modelManager, router, nil)
updated, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
m = updated.(*tui.Model)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = m.View()
}
}
func BenchmarkQwenRouter_Classification(b *testing.B) {
cfg := config.DefaultModelConfig()
router := config.NewQwenModelRouter(&cfg)
queries := []string{
"what is go",
"how do i create a file",
"debug this nil pointer error",
"design microservice architecture",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, q := range queries {
_ = router.SelectModelForMode(q, config.ModeAskContext)
}
}
}