integration_test.go

  1package loop
  2
  3import (
  4	"context"
  5	"testing"
  6	"time"
  7
  8	"shelley.exe.dev/llm"
  9)
 10
 11func TestLoopWithClaudeTools(t *testing.T) {
 12	var recordedMessages []llm.Message
 13
 14	recordFunc := func(ctx context.Context, message llm.Message, usage llm.Usage) error {
 15		recordedMessages = append(recordedMessages, message)
 16		return nil
 17	}
 18
 19	// Use some actual claudetools
 20	tools := []*llm.Tool{
 21		// TODO: Add actual tools when needed
 22	}
 23
 24	service := NewPredictableService()
 25
 26	// Create loop with the configured service
 27	loop := NewLoop(Config{
 28		LLM:           service,
 29		History:       []llm.Message{},
 30		Tools:         tools,
 31		RecordMessage: recordFunc,
 32	})
 33
 34	// Queue a user message that will trigger a specific predictable response
 35	userMessage := llm.Message{
 36		Role:    llm.MessageRoleUser,
 37		Content: []llm.Content{{Type: llm.ContentTypeText, Text: "hello"}},
 38	}
 39	loop.QueueUserMessage(userMessage)
 40
 41	// Run the loop with a short timeout
 42	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
 43	defer cancel()
 44
 45	err := loop.Go(ctx)
 46	if err != context.DeadlineExceeded {
 47		t.Errorf("expected context deadline exceeded, got %v", err)
 48	}
 49
 50	// Verify that messages were recorded
 51	// Note: User messages are recorded by ConversationManager, not by Loop,
 52	// so we only expect assistant messages to be recorded here
 53	if len(recordedMessages) < 1 {
 54		t.Errorf("expected at least 1 recorded message (assistant), got %d", len(recordedMessages))
 55	}
 56
 57	// Check that usage was accumulated
 58	usage := loop.GetUsage()
 59	if usage.IsZero() {
 60		t.Error("expected non-zero usage")
 61	}
 62
 63	// Verify conversation history includes user and assistant messages
 64	history := loop.GetHistory()
 65	if len(history) < 2 {
 66		t.Errorf("expected at least 2 history messages, got %d", len(history))
 67	}
 68
 69	// Check for expected response
 70	found := false
 71	for _, msg := range history {
 72		if msg.Role == llm.MessageRoleAssistant {
 73			for _, content := range msg.Content {
 74				if content.Type == llm.ContentTypeText && content.Text == "Well, hi there!" {
 75					found = true
 76					break
 77				}
 78			}
 79		}
 80	}
 81	if !found {
 82		t.Error("expected to find 'Well, hi there!' response")
 83	}
 84}
 85
 86func TestLoopContextCancellation(t *testing.T) {
 87	service := NewPredictableService()
 88	loop := NewLoop(Config{
 89		LLM:     service,
 90		History: []llm.Message{},
 91		Tools:   []*llm.Tool{},
 92		RecordMessage: func(ctx context.Context, message llm.Message, usage llm.Usage) error {
 93			return nil
 94		},
 95	})
 96
 97	// Cancel context immediately
 98	ctx, cancel := context.WithCancel(context.Background())
 99	cancel()
100
101	err := loop.Go(ctx)
102	if err != context.Canceled {
103		t.Errorf("expected context canceled, got %v", err)
104	}
105}
106
107func TestLoopSystemMessages(t *testing.T) {
108	// Set system messages
109	system := []llm.SystemContent{
110		{Text: "You are a helpful assistant.", Type: "text"},
111	}
112
113	loop := NewLoop(Config{
114		LLM:     NewPredictableService(),
115		History: []llm.Message{},
116		Tools:   []*llm.Tool{},
117		System:  system,
118		RecordMessage: func(ctx context.Context, message llm.Message, usage llm.Usage) error {
119			return nil
120		},
121	})
122
123	// The system messages are stored and would be passed to LLM
124	loop.mu.Lock()
125	if len(loop.system) != 1 {
126		t.Errorf("expected 1 system message, got %d", len(loop.system))
127	}
128	if loop.system[0].Text != "You are a helpful assistant." {
129		t.Errorf("unexpected system message text: %s", loop.system[0].Text)
130	}
131	loop.mu.Unlock()
132}