231 lines
7.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|