Ctrl K
ComunidadGuías…

internal/memory/carry_forward_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package memory_test

import (
	"context"
	"os"
	"path/filepath"
	"testing"

	"github.com/yeomyeonggeori/blueclaw/internal/memory"
	"github.com/yeomyeonggeori/blueclaw/internal/memory/memorytest"
	"github.com/yeomyeonggeori/bluememo"
	"github.com/yeomyeonggeori/bluememo/bluememotest"
)

func oldPathOf(t *testing.T, root string, parts ...string) string {
	t.Helper()
	directory := filepath.Join(append([]string{root, ".blueclaw", "memory"}, parts[:len(parts)-1]...)...)
	if errorValue := os.MkdirAll(directory, 0o700); errorValue != nil {
		t.Fatalf("make the old directory: %v", errorValue)
	}
	return filepath.Join(directory, parts[len(parts)-1])
}

// writeOldStore leaves a store at the old path holding one memory, closed, so
// its log is folded into the one file a cleanly stopped host leaves behind.
func writeOldStore(t *testing.T, path string, content string) {
	t.Helper()
	store, errorValue := bluememo.Open(context.Background(), path, bluememo.Configuration{Embedder: bluememotest.HashEmbedder{}, EmbeddingModel: "carry-test"})
	if errorValue != nil {
		t.Fatalf("open the old store: %v", errorValue)
	}
	if _, errorValue := store.Adopt(context.Background(), []bluememo.AdoptedMemory{{
		Memory: bluememo.Memory{MemoryID: bluememo.NewIdentifier(), Content: content},
	}}); errorValue != nil {
		t.Fatalf("write the old store: %v", errorValue)
	}
	if errorValue := store.Close(); errorValue != nil {
		t.Fatalf("close the old store: %v", errorValue)
	}
}

func recalledContent(t *testing.T, stores *memory.Stores, scope memory.Scope, query string) []string {
	t.Helper()
	path, errorValue := stores.Path(scope)
	if errorValue != nil {
		t.Fatalf("where the memory belongs: %v", errorValue)
	}
	store, errorValue := bluememo.Open(context.Background(), path, bluememo.Configuration{Embedder: bluememotest.HashEmbedder{}, EmbeddingModel: "carry-test"})
	if errorValue != nil {
		t.Fatalf("open the carried store: %v", errorValue)
	}
	defer store.Close()
	recalled, errorValue := store.Recall(context.Background(), query, 10)
	if errorValue != nil {
		t.Fatalf("recall from the carried store: %v", errorValue)
	}
	contents := []string{}
	for _, held := range recalled.Memories {
		contents = append(contents, held.Memory.Content)
	}
	return contents
}

func TestMemoryWrittenAtTheOldPathIsReadableAtTheNewOne(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	writeOldStore(t, oldPathOf(t, root, "persons", "person-1.db"), "the quarterly ledger lives in Numbers")
	writeOldStore(t, oldPathOf(t, root, "circles", "member.db"), "the all-hands is on Thursday")
	writeOldStore(t, oldPathOf(t, root, "workspace.db"), "the office closes at seven")

	report, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil {
		t.Fatalf("carry the memory forward: %v", errorValue)
	}
	if report.Carried != 3 {
		t.Fatalf("carried %d of the three stores, report %+v", report.Carried, report)
	}
	for _, carried := range []struct {
		scope   memory.Scope
		query   string
		content string
	}{
		{memory.PersonScope("person-1"), "where is the ledger", "the quarterly ledger lives in Numbers"},
		{memory.CircleScope("member"), "when is the all-hands", "the all-hands is on Thursday"},
		{memory.WorkspaceScope(), "when does the office close", "the office closes at seven"},
	} {
		contents := recalledContent(t, stores, carried.scope, carried.query)
		if len(contents) != 1 || contents[0] != carried.content {
			t.Fatalf("%s holds %q, wanted %q", carried.scope.Kind, contents, carried.content)
		}
	}
}

func TestWhatOnlyTheLogHeldIsCarriedRatherThanTruncatedAway(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	staging := filepath.Join(t.TempDir(), "memory.db")
	store, errorValue := bluememo.Open(context.Background(), staging, bluememo.Configuration{Embedder: bluememotest.HashEmbedder{}, EmbeddingModel: "carry-test"})
	if errorValue != nil {
		t.Fatalf("open the staging store: %v", errorValue)
	}
	if _, errorValue := store.Adopt(context.Background(), []bluememo.AdoptedMemory{{
		Memory: bluememo.Memory{MemoryID: bluememo.NewIdentifier(), Content: "the audit was filed on a Friday"},
	}}); errorValue != nil {
		t.Fatalf("write the staging store: %v", errorValue)
	}

	// Copy while it is still open, so the old path holds what a host killed
	// mid-write leaves: a database whose recent writes live only in its log.
	oldPath := oldPathOf(t, root, "persons", "person-1.db")
	for _, suffix := range []string{"", "-wal", "-shm"} {
		bytes, errorValue := os.ReadFile(staging + suffix)
		if errorValue != nil {
			t.Fatalf("read the staging %s: %v", suffix, errorValue)
		}
		if errorValue := os.WriteFile(oldPath+suffix, bytes, 0o600); errorValue != nil {
			t.Fatalf("stage the old %s: %v", suffix, errorValue)
		}
	}
	store.Close()

	if _, errorValue := stores.CarryForward(context.Background()); errorValue != nil {
		t.Fatalf("carry the memory forward: %v", errorValue)
	}
	contents := recalledContent(t, stores, memory.PersonScope("person-1"), "when was the audit filed")
	if len(contents) != 1 || contents[0] != "the audit was filed on a Friday" {
		t.Fatalf("the carried store holds %q, so what only the log held was lost", contents)
	}
	for _, suffix := range []string{"", "-wal", "-shm"} {
		if _, errorValue := os.Stat(oldPath + suffix); !os.IsNotExist(errorValue) {
			t.Fatalf("%s was left at the old path", filepath.Base(oldPath+suffix))
		}
	}
}

func TestAnOldFileIsFoldedIntoTheFileAlreadyThere(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	oldPath := oldPathOf(t, root, "persons", "person-1.db")
	writeOldStore(t, oldPath, "what the old path holds")
	memorytest.Remember(t, stores, memory.PersonScope("person-1"), "what the new path holds")

	report, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil {
		t.Fatalf("carry the memory forward: %v", errorValue)
	}
	if report.Carried != 0 || report.Adopted != 1 {
		t.Fatalf("report %+v, wanted one file folded in", report)
	}
	held := heldContent(t, stores, memory.PersonScope("person-1"))
	if !held["what the old path holds"] || !held["what the new path holds"] {
		t.Fatalf("the store holds %v, wanted what both paths held", held)
	}
	if _, errorValue := os.Stat(oldPath); !os.IsNotExist(errorValue) {
		t.Fatal("the old file was left behind once its memory was folded in")
	}
}

func heldContent(t *testing.T, stores *memory.Stores, scope memory.Scope) map[string]bool {
	t.Helper()
	store, errorValue := stores.Store(context.Background(), scope)
	if errorValue != nil {
		t.Fatalf("open the store: %v", errorValue)
	}
	memories, errorValue := store.Memories(context.Background())
	if errorValue != nil {
		t.Fatalf("read the store: %v", errorValue)
	}
	held := map[string]bool{}
	for _, memory := range memories {
		held[memory.Content] = true
	}
	return held
}

func TestCarryingForwardTwiceCarriesNothingTheSecondTime(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	writeOldStore(t, oldPathOf(t, root, "persons", "person-1.db"), "the ledger lives in Numbers")

	first, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil || first.Carried != 1 {
		t.Fatalf("first carry %+v: %v", first, errorValue)
	}
	second, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil {
		t.Fatalf("second carry: %v", errorValue)
	}
	if second.Carried != 0 || second.Adopted != 0 {
		t.Fatalf("second carry %+v, wanted nothing to do", second)
	}
}

func TestAHostThatNeverWroteAtTheOldPathCarriesNothing(t *testing.T) {
	stores, _ := memorytest.OpenWithRoot(t)
	report, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil {
		t.Fatalf("a fresh host is not a failure: %v", errorValue)
	}
	if report.Carried != 0 || report.Adopted != 0 {
		t.Fatalf("report %+v, wanted nothing to do", report)
	}
}

func TestTheAdminCirclesOldFileIsLeftWhereItIs(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	oldPath := oldPathOf(t, root, "circles", "admin.db")
	writeOldStore(t, oldPath, "the board meets on the first Monday")

	report, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil {
		t.Fatalf("carry the memory forward: %v", errorValue)
	}
	if report.Carried != 0 || len(report.Left) != 1 || report.Left[0] != oldPath {
		t.Fatalf("report %+v; the admin circle has no directory to carry into", report)
	}
	if _, errorValue := os.Stat(filepath.Join(root, "circles", "admin")); !os.IsNotExist(errorValue) {
		t.Fatalf("circles/admin was made (%v); nothing sets up its group, so nobody could read it", errorValue)
	}
}

func emptyCircleStore(t *testing.T, root string, circleID string) string {
	t.Helper()
	protected := filepath.Join(root, "circles", circleID, ".protected")
	if errorValue := os.MkdirAll(protected, 0o700); errorValue != nil {
		t.Fatal(errorValue)
	}
	store, errorValue := bluememo.Open(context.Background(), filepath.Join(protected, "memory.db"), bluememo.Configuration{Embedder: bluememotest.HashEmbedder{}, EmbeddingModel: "carry-test"})
	if errorValue != nil {
		t.Fatal(errorValue)
	}
	if errorValue := store.Close(); errorValue != nil {
		t.Fatal(errorValue)
	}
	return filepath.Join(root, "circles", circleID)
}

func TestTheAdminCirclesFolderIsRemovedWhenItsStoreKeepsNothing(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	folder := emptyCircleStore(t, root, "admin")

	report, errorValue := stores.CarryForward(context.Background())
	if errorValue != nil {
		t.Fatalf("carry the memory forward: %v", errorValue)
	}
	if _, errorValue := os.Stat(folder); !os.IsNotExist(errorValue) {
		t.Fatalf("circles/admin is still there (%v); report %+v", errorValue, report)
	}
	if len(report.Removed) != 1 || report.Removed[0] != folder {
		t.Fatalf("report %+v names no removed folder", report)
	}
}

func TestACircleStoreHoldingAMemoryIsKept(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	protected := filepath.Join(root, "circles", "c-level", ".protected")
	if errorValue := os.MkdirAll(protected, 0o700); errorValue != nil {
		t.Fatal(errorValue)
	}
	writeOldStore(t, filepath.Join(protected, "memory.db"), "the board meets on the first Monday")

	report, errorValue := stores.RemoveRetiredCircleFolders(context.Background(), []string{"c-level"})
	if errorValue != nil {
		t.Fatalf("remove retired circle folders: %v", errorValue)
	}
	if _, errorValue := os.Stat(filepath.Join(protected, "memory.db")); errorValue != nil {
		t.Fatalf("a store that kept a memory was removed: %v", errorValue)
	}
	if len(report.Left) != 1 || len(report.Removed) != 0 {
		t.Fatalf("report %+v; a kept folder must be named and none removed", report)
	}
}

func TestACircleFolderStaysWhenSomebodyPutAFileThere(t *testing.T) {
	stores, root := memorytest.OpenWithRoot(t)
	folder := emptyCircleStore(t, root, "hr")
	if errorValue := os.WriteFile(filepath.Join(folder, "agenda.md"), []byte("이샘플 presents"), 0o600); errorValue != nil {
		t.Fatal(errorValue)
	}

	report, errorValue := stores.RemoveRetiredCircleFolders(context.Background(), []string{"hr"})
	if errorValue != nil {
		t.Fatalf("remove retired circle folders: %v", errorValue)
	}
	if _, errorValue := os.Stat(filepath.Join(folder, "agenda.md")); errorValue != nil {
		t.Fatalf("a file somebody put in circles/hr was touched: %v", errorValue)
	}
	if len(report.Left) != 1 || report.Left[0] != folder {
		t.Fatalf("report %+v; the folder kept for its file must be named", report)
	}
}