v0.0.4: add sessions and refine tui
This commit is contained in:
@@ -2,6 +2,7 @@ package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -19,8 +20,8 @@ func (p *recordingProvider) ChatStream(ctx context.Context, req llm.ChatRequest,
|
||||
return emit(llm.StreamEvent{Content: "ok"})
|
||||
}
|
||||
|
||||
func TestManagerSwitchesModelAndThinking(t *testing.T) {
|
||||
cfg := &config.Config{
|
||||
func testConfig() *config.Config {
|
||||
return &config.Config{
|
||||
Providers: map[string]config.ProviderConfig{
|
||||
"one": {
|
||||
Name: "one",
|
||||
@@ -34,6 +35,11 @@ func TestManagerSwitchesModelAndThinking(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testManager(t *testing.T) (*Manager, *agent.Agent, *recordingProvider) {
|
||||
t.Helper()
|
||||
cfg := testConfig()
|
||||
provider := &recordingProvider{}
|
||||
assistant := agent.New(agent.Options{
|
||||
Provider: provider,
|
||||
@@ -43,10 +49,19 @@ func TestManagerSwitchesModelAndThinking(t *testing.T) {
|
||||
ThinkingParam: "thinking",
|
||||
ThinkingEnabled: true,
|
||||
})
|
||||
manager, err := NewManager(cfg, assistant, nil)
|
||||
store, err := NewStore(filepath.Join(t.TempDir(), "sessions"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
manager, err := NewManager(cfg, assistant, nil, store)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return manager, assistant, provider
|
||||
}
|
||||
|
||||
func TestManagerSwitchesModelAndThinking(t *testing.T) {
|
||||
manager, assistant, provider := testManager(t)
|
||||
|
||||
if _, err := manager.SetModel("model-b"); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -80,7 +95,8 @@ func TestManagerRejectsUnknownModelAndThinking(t *testing.T) {
|
||||
},
|
||||
}
|
||||
assistant := agent.New(agent.Options{Provider: &recordingProvider{}, Model: "model-a"})
|
||||
manager, err := NewManager(cfg, assistant, nil)
|
||||
store, _ := NewStore(filepath.Join(t.TempDir(), "sessions"))
|
||||
manager, err := NewManager(cfg, assistant, nil, store)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -92,3 +108,159 @@ func TestManagerRejectsUnknownModelAndThinking(t *testing.T) {
|
||||
t.Fatal("expected invalid thinking error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerSaveAndResume(t *testing.T) {
|
||||
manager, assistant, _ := testManager(t)
|
||||
|
||||
// Start new session and have a conversation
|
||||
manager.newSession()
|
||||
var out strings.Builder
|
||||
assistant.RunTurn(context.Background(), "hello world", &out, nil)
|
||||
|
||||
// Save
|
||||
if err := manager.Save(); err != nil {
|
||||
t.Fatalf("save: %v", err)
|
||||
}
|
||||
savedID := manager.CurrentSessionID()
|
||||
if savedID == "" {
|
||||
t.Fatal("expected session ID after save")
|
||||
}
|
||||
// Name should be auto-derived from first user message
|
||||
if manager.CurrentSessionName() != "hello world" {
|
||||
t.Fatalf("session name = %q, want %q", manager.CurrentSessionName(), "hello world")
|
||||
}
|
||||
|
||||
// Start a new session
|
||||
manager.newSession()
|
||||
if manager.CurrentSessionID() == savedID {
|
||||
t.Fatal("new session should have different ID")
|
||||
}
|
||||
|
||||
// Resume
|
||||
if err := manager.Resume(savedID); err != nil {
|
||||
t.Fatalf("resume: %v", err)
|
||||
}
|
||||
if manager.CurrentSessionID() != savedID {
|
||||
t.Fatalf("after resume, ID = %q, want %q", manager.CurrentSessionID(), savedID)
|
||||
}
|
||||
msgs := assistant.Messages()
|
||||
hasUser := false
|
||||
for _, m := range msgs {
|
||||
if m.Role == llm.RoleUser && m.Content == "hello world" {
|
||||
hasUser = true
|
||||
}
|
||||
}
|
||||
if !hasUser {
|
||||
t.Fatal("resumed session should contain original user message")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerRename(t *testing.T) {
|
||||
manager, _, _ := testManager(t)
|
||||
manager.newSession()
|
||||
|
||||
msg, err := manager.Rename("my cool session")
|
||||
if err != nil {
|
||||
t.Fatalf("rename: %v", err)
|
||||
}
|
||||
if !strings.Contains(msg, "my cool session") {
|
||||
t.Fatalf("rename message = %q", msg)
|
||||
}
|
||||
if manager.CurrentSessionName() != "my cool session" {
|
||||
t.Fatalf("name = %q", manager.CurrentSessionName())
|
||||
}
|
||||
|
||||
// Verify persisted
|
||||
_, err = manager.store.Load(manager.CurrentSessionID())
|
||||
if err != nil {
|
||||
t.Fatalf("load after rename: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerRenameEmpty(t *testing.T) {
|
||||
manager, _, _ := testManager(t)
|
||||
manager.newSession()
|
||||
_, err := manager.Rename(" ")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for empty name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerSessions(t *testing.T) {
|
||||
manager, _, _ := testManager(t)
|
||||
|
||||
// No sessions yet
|
||||
out, err := manager.Sessions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(out, "No saved sessions") {
|
||||
t.Fatalf("expected 'no saved sessions', got: %s", out)
|
||||
}
|
||||
|
||||
// Create and save a session
|
||||
manager.newSession()
|
||||
manager.Save()
|
||||
|
||||
out, err = manager.Sessions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(out, "No saved sessions") {
|
||||
t.Fatal("expected session list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerNewSession(t *testing.T) {
|
||||
manager, assistant, _ := testManager(t)
|
||||
manager.newSession()
|
||||
var out strings.Builder
|
||||
assistant.RunTurn(context.Background(), "first message", &out, nil)
|
||||
|
||||
msg, err := manager.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("new session: %v", err)
|
||||
}
|
||||
if !strings.Contains(msg, "Starting new session") {
|
||||
t.Fatalf("message = %q", msg)
|
||||
}
|
||||
// Old session should be saved
|
||||
metas, _ := manager.store.List()
|
||||
if len(metas) != 1 {
|
||||
t.Fatalf("expected 1 saved session, got %d", len(metas))
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerResumeLatest(t *testing.T) {
|
||||
manager, assistant, _ := testManager(t)
|
||||
|
||||
// Create two sessions
|
||||
manager.newSession()
|
||||
var out strings.Builder
|
||||
assistant.RunTurn(context.Background(), "session one", &out, nil)
|
||||
manager.Save()
|
||||
id1 := manager.CurrentSessionID()
|
||||
|
||||
manager.NewSession()
|
||||
assistant.RunTurn(context.Background(), "session two", &out, nil)
|
||||
manager.Save()
|
||||
id2 := manager.CurrentSessionID()
|
||||
|
||||
// Clear and resume latest
|
||||
manager.newSession()
|
||||
if err := manager.ResumeLatest(); err != nil {
|
||||
t.Fatalf("resume latest: %v", err)
|
||||
}
|
||||
// Should resume the most recently updated (id2)
|
||||
if manager.CurrentSessionID() != id2 {
|
||||
t.Fatalf("expected to resume %s, got %s", id2, manager.CurrentSessionID())
|
||||
}
|
||||
|
||||
// Also verify id1 is resumable
|
||||
if err := manager.Resume(id1); err != nil {
|
||||
t.Fatalf("resume id1: %v", err)
|
||||
}
|
||||
if manager.CurrentSessionID() != id1 {
|
||||
t.Fatalf("expected %s, got %s", id1, manager.CurrentSessionID())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user