9 "github.com/stretchr/testify/require"
12 func TestFreeLeafHoldsAShortMessageWordForWord(t *testing.T) {
16 Text: strings.Repeat("a", nodeBytes-len("user: ")),
19 node, free := freeLeaf(message)
21 require.Equal(t, Node{
24 Text: "user: " + message.Text,
29 _, free = freeLeaf(message)
30 require.False(t, free)
33 func TestFreeMergeJoinsShortSiblings(t *testing.T) {
34 left := treeTestNode(1, 2, "left")
35 right := treeTestNode(1, 3, "right")
37 node, free := freeMerge(left, right)
39 require.Equal(t, treeTestNode(2, 1, "left\nright"), node)
41 right = treeTestNode(1, 3, strings.Repeat("r", nodeBytes-len("left")))
42 _, free = freeMerge(left, right)
43 require.False(t, free)
46 func TestTreeBuildsEachNodeOnce(t *testing.T) {
47 tree, err := openTree(t.Context(), t.TempDir(), 2, newTestClock().Now)
48 require.NoError(t, err)
51 require.NoError(t, tree.Close())
54 require.NoError(t, tree.Add(treeTestNode(0, 0, "user: a")))
55 require.ErrorContains(t, tree.Add(treeTestNode(0, 0, "user: b")), "already built")
56 require.ErrorContains(t, tree.Add(treeTestNode(1, 0, "a b")), "child that is not built")
58 require.NoError(t, tree.Add(treeTestNode(0, 1, "reply: b")))
59 require.NoError(t, tree.Add(treeTestNode(1, 0, "a b")))
61 require.Equal(t, "user: a", treeReadNode(t, tree, 0, 0).Text)
64 func TestTreeMissingNodeHasNoErrorAfterWrapping(t *testing.T) {
65 tree, err := openTree(t.Context(), t.TempDir(), 0, newTestClock().Now)
66 require.NoError(t, err)
69 require.NoError(t, tree.Close())
72 _, built, err := tree.Node(0, 0)
73 require.NoError(t, err)
74 require.False(t, built)
77 func TestTreeReadFailureKeepsItsCause(t *testing.T) {
78 tree, err := openTree(t.Context(), t.TempDir(), 1, newTestClock().Now)
79 require.NoError(t, err)
81 require.NoError(t, tree.Close())
84 require.NoError(t, tree.Add(treeTestNode(0, 0, "user: a")))
85 require.NoError(t, tree.levels[0].Close())
87 _, built, err := tree.Node(0, 0)
88 delete(tree.levels, 0)
89 require.ErrorIs(t, err, os.ErrClosed)
90 require.False(t, built)
91 require.ErrorContains(t, err, "find node position")
94 func TestTreeReopenLoadsTheSameNodes(t *testing.T) {
96 clock := newTestClock()
97 tree, err := openTree(t.Context(), root, 2, clock.Now)
98 require.NoError(t, err)
101 treeTestNode(0, 1, "reply: b"),
102 treeTestNode(0, 0, "user: a"),
103 treeTestNode(1, 0, "user: a\nreply: b"),
106 for _, node := range nodes {
107 require.NoError(t, tree.Add(node))
110 require.NoError(t, tree.Close())
112 data, err := os.ReadFile(filepath.Join(root, "tree", "2026-01-01.jsonl"))
113 require.NoError(t, err)
114 require.JSONEq(t, `{"l":0,"i":1,"text":"reply: b","size":8}`+"\n", strings.SplitAfter(string(data), "\n")[0])
116 tree, err = openTree(t.Context(), root, 2, clock.Now)
117 require.NoError(t, err)
120 require.NoError(t, tree.Close())
123 require.Equal(t, len(nodes), tree.Count())
125 for _, node := range nodes {
126 require.Equal(t, node, treeReadNode(t, tree, node.Level, node.Index))
130 func TestTreeBuildsAMissingIndexAgain(t *testing.T) {
132 clock := newTestClock()
133 tree, err := openTree(t.Context(), root, 2, clock.Now)
134 require.NoError(t, err)
137 treeTestNode(0, 1, "reply: b"),
138 treeTestNode(0, 0, "user: a"),
139 treeTestNode(1, 0, "user: a\nreply: b"),
142 for _, node := range nodes {
143 require.NoError(t, tree.Add(node))
146 require.NoError(t, tree.Close())
147 require.NoError(t, os.RemoveAll(filepath.Join(root, "index")))
149 _, err = openTree(t.Context(), root, 1, clock.Now)
150 require.ErrorContains(t, err, "covers messages after the log")
151 require.NoError(t, os.RemoveAll(filepath.Join(root, "index")))
153 tree, err = openTree(t.Context(), root, 2, clock.Now)
154 require.NoError(t, err)
157 require.NoError(t, tree.Close())
160 require.Equal(t, len(nodes), tree.Count())
162 for _, node := range nodes {
163 require.Equal(t, node, treeReadNode(t, tree, node.Level, node.Index))
167 func TestTreeIndexesTheNodesAfterACrash(t *testing.T) {
169 clock := newTestClock()
170 tree, err := openTree(t.Context(), root, 2, clock.Now)
171 require.NoError(t, err)
173 require.NoError(t, tree.Add(treeTestNode(0, 0, "user: a")))
174 require.NoError(t, tree.Add(treeTestNode(0, 1, "reply: b")))
175 require.NoError(t, tree.Close())
177 // A crash after the line write and in the middle of the index writes.
178 order := filepath.Join(root, "index", "tree.idx")
179 require.NoError(t, os.Truncate(order, treeRecordBytes+5))
180 require.NoError(t, os.Truncate(filepath.Join(root, "index", "tree-0.idx"), positionBytes))
182 tree, err = openTree(t.Context(), root, 2, clock.Now)
183 require.NoError(t, err)
186 require.NoError(t, tree.Close())
189 require.Equal(t, 2, tree.Count())
190 require.Equal(t, "reply: b", treeReadNode(t, tree, 0, 1).Text)
191 require.NoError(t, tree.Add(treeTestNode(1, 0, "user: a\nreply: b")))