package kbblock import ( "os" "path/filepath" "strings" "testing" ) func TestSessionDir(t *testing.T) { got := SessionDir("/root/.plexum", "alice", "s_1") want := "/root/.plexum/agents/alice/sessions/s_1/plugins/harbor-forge" if got != want { t.Errorf("SessionDir = %q want %q", got, want) } } func TestOpenMissingFile(t *testing.T) { b, err := Open(t.TempDir(), "alice", "s_1") if err != nil { t.Fatal(err) } if b.Len() != 0 || b.NextSeq != 1 { t.Errorf("empty open: Len=%d NextSeq=%d", b.Len(), b.NextSeq) } } func TestOpenRequiresAllArgs(t *testing.T) { if _, err := Open("", "a", "s"); err == nil { t.Error("want error on empty profileRoot") } if _, err := Open("/x", "", "s"); err == nil { t.Error("want error on empty agentID") } if _, err := Open("/x", "a", ""); err == nil { t.Error("want error on empty sessionID") } } func TestAddAndDuplicate(t *testing.T) { b, _ := Open(t.TempDir(), "a", "s") e := b.Add(42, "KB-PAYROT", "debugging", "OOM fix", 3) if e == nil || e.ID != 42 || e.KBCode != "KB-PAYROT" || e.SourceTopic != "debugging" || e.InsertSeq != 1 || e.AddedAtTurn != 3 { t.Errorf("entry wrong: %+v", e) } if b.Add(42, "X", "y", "z", 7) != nil { t.Error("dup should return nil") } if b.Len() != 1 { t.Errorf("Len=%d", b.Len()) } } func TestRoundtrip(t *testing.T) { dir := t.TempDir() b, _ := Open(dir, "a", "s") b.Add(101, "KB-A", "topic-a", "a", 1) b.Add(202, "KB-A", "topic-b", "b", 2) if err := b.Save(); err != nil { t.Fatal(err) } b2, _ := Open(dir, "a", "s") if b2.Len() != 2 || b2.NextSeq != 3 { t.Errorf("reopen Len=%d NextSeq=%d", b2.Len(), b2.NextSeq) } if !b2.Has(101) || !b2.Has(202) { t.Error("entries missing after reopen") } } func TestRenderInsertOrder(t *testing.T) { b, _ := Open(t.TempDir(), "a", "s") b.Add(999, "K", "t", "first", 0) b.Add(1, "K", "t", "second", 0) out := b.Render() idx999 := strings.Index(out, "id=999") idx1 := strings.Index(out, "id=1 ") if !(idx999 >= 0 && idx1 > idx999) { t.Errorf("order broken: %q", out) } } func TestRenderAttributes(t *testing.T) { b, _ := Open(t.TempDir(), "a", "s") b.Add(42, "KB-PAYROT", "debugging", "OOM fix: bump heap", 0) out := b.Render() if !strings.HasPrefix(out, "\n") { t.Errorf("head wrong: %q", out) } if !strings.HasSuffix(out, "\n") { t.Errorf("tail wrong: %q", out) } } func TestRenderOmitsSourceWhenEmpty(t *testing.T) { b, _ := Open(t.TempDir(), "a", "s") b.Add(7, "KB-X", "", "raw", 0) out := b.Render() if strings.Contains(out, "source=topic:") { t.Errorf("source attr leaked: %q", out) } if !strings.Contains(out, "") { t.Errorf("tight tag missing: %q", out) } } func TestRemove(t *testing.T) { b, _ := Open(t.TempDir(), "a", "s") b.Add(1, "K", "t", "a", 0) b.Add(2, "K", "t", "b", 0) b.Add(3, "K", "t", "c", 0) removed := b.Remove(2, 99, 3) if len(removed) != 2 { t.Errorf("removed=%v", removed) } if b.Len() != 1 || !b.Has(1) { t.Errorf("post-state wrong: Len=%d", b.Len()) } } func TestRenderEmpty(t *testing.T) { b, _ := Open(t.TempDir(), "a", "s") if b.Render() != "" { t.Error("empty Render should be \"\"") } } func TestSaveEmptyNoFile(t *testing.T) { dir := t.TempDir() b, _ := Open(dir, "a", "s") if err := b.Save(); err != nil { t.Fatal(err) } path := filepath.Join(SessionDir(dir, "a", "s"), FileName) if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("file should not exist: %v", err) } }