mirror of
https://github.com/lukaszraczylo/compaction-mcp.git
synced 2026-06-05 23:14:02 +00:00
dded4ec04c
- Dockerfile: distroless container for MCP server - GoReleaser: multi-platform binary and Docker builds with cosign signing - GitHub Actions: release workflow using shared actions - Semver config for automatic version calculation - Persistence layer, content indexing, and improved tool handlers
179 lines
4.1 KiB
Go
179 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestPersistSaveLoad(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store1 := NewStore(10000)
|
|
|
|
if _, err := store1.Add("first item content", "first summary", []string{"tag1"}, 5); err != nil {
|
|
t.Fatalf("Add: %v", err)
|
|
}
|
|
if _, err := store1.Add("second item content", "", []string{"tag2", "tag3"}, 8); err != nil {
|
|
t.Fatalf("Add: %v", err)
|
|
}
|
|
|
|
p1, err := NewPersister(dir, store1)
|
|
if err != nil {
|
|
t.Fatalf("NewPersister: %v", err)
|
|
}
|
|
|
|
errSave := p1.Save()
|
|
if errSave != nil {
|
|
t.Fatalf("Save: %v", errSave)
|
|
}
|
|
|
|
// Load into a fresh store and verify roundtrip.
|
|
store2 := NewStore(10000)
|
|
p2, errP2 := NewPersister(dir, store2)
|
|
if errP2 != nil {
|
|
t.Fatalf("NewPersister (store2): %v", errP2)
|
|
}
|
|
|
|
errLoad := p2.Load()
|
|
if errLoad != nil {
|
|
t.Fatalf("Load: %v", errLoad)
|
|
}
|
|
|
|
_, _, count1, _ := store1.Status()
|
|
_, _, count2, _ := store2.Status()
|
|
|
|
if count2 != count1 {
|
|
t.Fatalf("item count mismatch: got %d, want %d", count2, count1)
|
|
}
|
|
|
|
// Verify individual items survived the roundtrip.
|
|
items := store2.Query("", nil, 100)
|
|
if len(items) != 2 {
|
|
t.Fatalf("expected 2 items from query, got %d", len(items))
|
|
}
|
|
|
|
found := make(map[string]bool)
|
|
for _, item := range items {
|
|
found[item.Content] = true
|
|
if item.Tokens <= 0 {
|
|
t.Errorf("item %s has non-positive token count: %d", item.ID, item.Tokens)
|
|
}
|
|
}
|
|
|
|
if !found["first item content"] {
|
|
t.Error("missing 'first item content' after load")
|
|
}
|
|
if !found["second item content"] {
|
|
t.Error("missing 'second item content' after load")
|
|
}
|
|
}
|
|
|
|
func TestPersistAtomicWrite(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store := NewStore(10000)
|
|
if _, err := store.Add("test content", "", nil, 5); err != nil {
|
|
t.Fatalf("Add: %v", err)
|
|
}
|
|
|
|
p, err := NewPersister(dir, store)
|
|
if err != nil {
|
|
t.Fatalf("NewPersister: %v", err)
|
|
}
|
|
|
|
errSave := p.Save()
|
|
if errSave != nil {
|
|
t.Fatalf("Save: %v", errSave)
|
|
}
|
|
|
|
// state.json should exist.
|
|
_, errStat := os.Stat(filepath.Join(dir, "state.json"))
|
|
if errStat != nil {
|
|
t.Fatalf("state.json missing: %v", errStat)
|
|
}
|
|
|
|
// Temp file should not linger.
|
|
_, errTmp := os.Stat(filepath.Join(dir, "state.json.tmp"))
|
|
if !os.IsNotExist(errTmp) {
|
|
t.Fatal("state.json.tmp should not exist after successful save")
|
|
}
|
|
}
|
|
|
|
func TestPersistLoadMissing(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store := NewStore(10000)
|
|
|
|
p, err := NewPersister(dir, store)
|
|
if err != nil {
|
|
t.Fatalf("NewPersister: %v", err)
|
|
}
|
|
|
|
// Loading from a directory with no state.json should succeed (fresh start).
|
|
errLoad := p.Load()
|
|
if errLoad != nil {
|
|
t.Fatalf("Load on missing file should return nil, got: %v", errLoad)
|
|
}
|
|
|
|
_, _, count, _ := store.Status()
|
|
if count != 0 {
|
|
t.Fatalf("expected 0 items after loading missing file, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestPersistLoadCorrupted(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
// Write garbage to state.json.
|
|
errWrite := os.WriteFile(filepath.Join(dir, "state.json"), []byte("{{{garbage"), 0o600)
|
|
if errWrite != nil {
|
|
t.Fatalf("writing corrupt file: %v", errWrite)
|
|
}
|
|
|
|
store := NewStore(10000)
|
|
p, err := NewPersister(dir, store)
|
|
if err != nil {
|
|
t.Fatalf("NewPersister: %v", err)
|
|
}
|
|
|
|
if p.Load() == nil {
|
|
t.Fatal("Load should return error for corrupted file")
|
|
}
|
|
}
|
|
|
|
func TestPersistMarkDirty(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store := NewStore(10000)
|
|
|
|
p, err := NewPersister(dir, store)
|
|
if err != nil {
|
|
t.Fatalf("NewPersister: %v", err)
|
|
}
|
|
|
|
// Initially not dirty -- Start + Stop should not create state.json.
|
|
p.Start(time.Hour) // long interval so tick won't fire
|
|
p.Stop()
|
|
|
|
_, errStat := os.Stat(filepath.Join(dir, "state.json"))
|
|
if !os.IsNotExist(errStat) {
|
|
t.Fatal("state.json should not exist when not dirty")
|
|
}
|
|
|
|
// Mark dirty, then Stop should trigger a save.
|
|
if _, err := store.Add("dirty item", "", nil, 5); err != nil {
|
|
t.Fatalf("Add: %v", err)
|
|
}
|
|
p2, errP2 := NewPersister(dir, store)
|
|
if errP2 != nil {
|
|
t.Fatalf("NewPersister: %v", errP2)
|
|
}
|
|
|
|
p2.Start(time.Hour)
|
|
p2.MarkDirty()
|
|
p2.Stop()
|
|
|
|
_, errFinal := os.Stat(filepath.Join(dir, "state.json"))
|
|
if errFinal != nil {
|
|
t.Fatalf("state.json should exist after dirty stop: %v", errFinal)
|
|
}
|
|
}
|