test(session): add JSONLBackend integration tests
8 tests covering the full SessionStore contract through the JSONL backend: message roundtrip, tool calls, summary, truncation with compaction, history replacement, empty sessions, session isolation, and the complete summarization flow (SetSummary → TruncateHistory → Save). Includes compile-time interface satisfaction checks for both SessionManager and JSONLBackend. Part of #1169
This commit is contained in:
parent
1680e98414
commit
317d998ea5
1 changed files with 179 additions and 0 deletions
179
pkg/session/jsonl_backend_test.go
Normal file
179
pkg/session/jsonl_backend_test.go
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
package session_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/memory"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/providers"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compile-time interface satisfaction checks.
|
||||||
|
var (
|
||||||
|
_ session.SessionStore = (*session.SessionManager)(nil)
|
||||||
|
_ session.SessionStore = (*session.JSONLBackend)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func newBackend(t *testing.T) *session.JSONLBackend {
|
||||||
|
t.Helper()
|
||||||
|
store, err := memory.NewJSONLStore(t.TempDir())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { store.Close() })
|
||||||
|
return session.NewJSONLBackend(store)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_AddAndGetHistory(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
|
||||||
|
b.AddMessage("s1", "user", "hello")
|
||||||
|
b.AddMessage("s1", "assistant", "hi")
|
||||||
|
|
||||||
|
history := b.GetHistory("s1")
|
||||||
|
if len(history) != 2 {
|
||||||
|
t.Fatalf("got %d messages, want 2", len(history))
|
||||||
|
}
|
||||||
|
if history[0].Role != "user" || history[0].Content != "hello" {
|
||||||
|
t.Errorf("msg[0] = %+v", history[0])
|
||||||
|
}
|
||||||
|
if history[1].Role != "assistant" || history[1].Content != "hi" {
|
||||||
|
t.Errorf("msg[1] = %+v", history[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_AddFullMessage(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
|
||||||
|
msg := providers.Message{
|
||||||
|
Role: "assistant",
|
||||||
|
Content: "done",
|
||||||
|
ToolCalls: []providers.ToolCall{
|
||||||
|
{ID: "tc1", Function: &providers.FunctionCall{Name: "read_file", Arguments: `{"path":"x"}`}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b.AddFullMessage("s1", msg)
|
||||||
|
|
||||||
|
history := b.GetHistory("s1")
|
||||||
|
if len(history) != 1 {
|
||||||
|
t.Fatalf("got %d, want 1", len(history))
|
||||||
|
}
|
||||||
|
if len(history[0].ToolCalls) != 1 || history[0].ToolCalls[0].ID != "tc1" {
|
||||||
|
t.Errorf("tool calls = %+v", history[0].ToolCalls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_Summary(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
|
||||||
|
if got := b.GetSummary("s1"); got != "" {
|
||||||
|
t.Errorf("got %q, want empty", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.SetSummary("s1", "test summary")
|
||||||
|
if got := b.GetSummary("s1"); got != "test summary" {
|
||||||
|
t.Errorf("got %q, want %q", got, "test summary")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_TruncateAndSave(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
b.AddMessage("s1", "user", fmt.Sprintf("msg %d", i))
|
||||||
|
}
|
||||||
|
b.TruncateHistory("s1", 3)
|
||||||
|
|
||||||
|
history := b.GetHistory("s1")
|
||||||
|
if len(history) != 3 {
|
||||||
|
t.Fatalf("got %d, want 3", len(history))
|
||||||
|
}
|
||||||
|
if history[0].Content != "msg 7" {
|
||||||
|
t.Errorf("got %q, want %q", history[0].Content, "msg 7")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save triggers compaction.
|
||||||
|
if err := b.Save("s1"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages still accessible after compaction.
|
||||||
|
history = b.GetHistory("s1")
|
||||||
|
if len(history) != 3 {
|
||||||
|
t.Fatalf("after save: got %d, want 3", len(history))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_SetHistory(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
b.AddMessage("s1", "user", "old")
|
||||||
|
|
||||||
|
b.SetHistory("s1", []providers.Message{
|
||||||
|
{Role: "user", Content: "new1"},
|
||||||
|
{Role: "assistant", Content: "new2"},
|
||||||
|
})
|
||||||
|
|
||||||
|
history := b.GetHistory("s1")
|
||||||
|
if len(history) != 2 {
|
||||||
|
t.Fatalf("got %d, want 2", len(history))
|
||||||
|
}
|
||||||
|
if history[0].Content != "new1" {
|
||||||
|
t.Errorf("got %q, want %q", history[0].Content, "new1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_EmptySession(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
|
||||||
|
history := b.GetHistory("nonexistent")
|
||||||
|
if history == nil {
|
||||||
|
t.Fatal("got nil, want empty slice")
|
||||||
|
}
|
||||||
|
if len(history) != 0 {
|
||||||
|
t.Errorf("got %d, want 0", len(history))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_SessionIsolation(t *testing.T) {
|
||||||
|
b := newBackend(t)
|
||||||
|
b.AddMessage("s1", "user", "session1")
|
||||||
|
b.AddMessage("s2", "user", "session2")
|
||||||
|
|
||||||
|
h1 := b.GetHistory("s1")
|
||||||
|
h2 := b.GetHistory("s2")
|
||||||
|
|
||||||
|
if len(h1) != 1 || h1[0].Content != "session1" {
|
||||||
|
t.Errorf("s1: %+v", h1)
|
||||||
|
}
|
||||||
|
if len(h2) != 1 || h2[0].Content != "session2" {
|
||||||
|
t.Errorf("s2: %+v", h2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONLBackend_SummarizeFlow(t *testing.T) {
|
||||||
|
// Simulates the real summarization flow in the agent loop:
|
||||||
|
// SetSummary → TruncateHistory → Save
|
||||||
|
b := newBackend(t)
|
||||||
|
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
b.AddMessage("s1", "user", fmt.Sprintf("msg %d", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.SetSummary("s1", "conversation about testing")
|
||||||
|
b.TruncateHistory("s1", 4)
|
||||||
|
if err := b.Save("s1"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := b.GetSummary("s1"); got != "conversation about testing" {
|
||||||
|
t.Errorf("summary = %q", got)
|
||||||
|
}
|
||||||
|
history := b.GetHistory("s1")
|
||||||
|
if len(history) != 4 {
|
||||||
|
t.Fatalf("got %d messages, want 4", len(history))
|
||||||
|
}
|
||||||
|
if history[0].Content != "msg 16" {
|
||||||
|
t.Errorf("first message = %q, want %q", history[0].Content, "msg 16")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue