]> git.giacomolli.uk - onechat/blobdiff - tree_test.go
Send the replies to Telegram with their formats
[onechat] / tree_test.go
index 2e7ff3a90a91c0c41b77c6ae1f0dcb083a22de8c..ece7c6fbc17e7a4d7e18aa0e320cd705484155c1 100644 (file)
@@ -58,9 +58,7 @@ func TestTreeBuildsEachNodeOnce(t *testing.T) {
        require.NoError(t, tree.Add(testNode(0, 1, "reply: b")))
        require.NoError(t, tree.Add(testNode(1, 0, "a b")))
 
-       node, built := tree.Node(0, 0)
-       require.True(t, built)
-       require.Equal(t, "user: a", node.Text)
+       require.Equal(t, "user: a", readNode(t, tree, 0, 0).Text)
 }
 
 func TestTreeReopenLoadsTheSameNodes(t *testing.T) {
@@ -91,15 +89,93 @@ func TestTreeReopenLoadsTheSameNodes(t *testing.T) {
                require.NoError(t, tree.Close())
        }()
 
-       require.Len(t, tree.nodes, len(nodes))
+       require.Equal(t, len(nodes), tree.Count())
        for _, node := range nodes {
-               loaded, built := tree.Node(node.Level, node.Index)
-               require.True(t, built)
-               require.Equal(t, node, loaded)
+               require.Equal(t, node, readNode(t, tree, node.Level, node.Index))
        }
+}
+
+func TestTreeBuildsAMissingIndexAgain(t *testing.T) {
+       root := t.TempDir()
+       clock := newTestClock()
+       tree, err := openTree(t.Context(), root, 2, clock.Now)
+       require.NoError(t, err)
+
+       nodes := []Node{
+               testNode(0, 1, "reply: b"),
+               testNode(0, 0, "user: a"),
+               testNode(1, 0, "user: a\nreply: b"),
+       }
+       for _, node := range nodes {
+               require.NoError(t, tree.Add(node))
+       }
+
+       require.NoError(t, tree.Close())
+       require.NoError(t, os.RemoveAll(filepath.Join(root, "index")))
 
        _, err = openTree(t.Context(), root, 1, clock.Now)
        require.ErrorContains(t, err, "covers messages after the log")
+       require.NoError(t, os.RemoveAll(filepath.Join(root, "index")))
+
+       tree, err = openTree(t.Context(), root, 2, clock.Now)
+       require.NoError(t, err)
+
+       defer func() {
+               require.NoError(t, tree.Close())
+       }()
+
+       require.Equal(t, len(nodes), tree.Count())
+       for _, node := range nodes {
+               require.Equal(t, node, readNode(t, tree, node.Level, node.Index))
+       }
+}
+
+func TestTreeIndexesTheNodesAfterACrash(t *testing.T) {
+       root := t.TempDir()
+       clock := newTestClock()
+       tree, err := openTree(t.Context(), root, 2, clock.Now)
+       require.NoError(t, err)
+
+       require.NoError(t, tree.Add(testNode(0, 0, "user: a")))
+       require.NoError(t, tree.Add(testNode(0, 1, "reply: b")))
+       require.NoError(t, tree.Close())
+
+       // A crash after the line write and in the middle of the index writes.
+       order := filepath.Join(root, "index", "tree.idx")
+       require.NoError(t, os.Truncate(order, treeRecordBytes+5))
+       require.NoError(t, os.Truncate(filepath.Join(root, "index", "tree-0.idx"), positionBytes))
+
+       tree, err = openTree(t.Context(), root, 2, clock.Now)
+       require.NoError(t, err)
+
+       defer func() {
+               require.NoError(t, tree.Close())
+       }()
+
+       require.Equal(t, 2, tree.Count())
+       require.Equal(t, "reply: b", readNode(t, tree, 0, 1).Text)
+       require.NoError(t, tree.Add(testNode(1, 0, "user: a\nreply: b")))
+}
+
+// isBuilt tells if the node (l, i) of tree is built.
+func isBuilt(t *testing.T, tree *Tree, level, index int) bool {
+       t.Helper()
+
+       built, err := tree.Built(level, index)
+       require.NoError(t, err)
+
+       return built
+}
+
+// readNode reads the node (l, i) of tree, which must be built.
+func readNode(t *testing.T, tree *Tree, level, index int) Node {
+       t.Helper()
+
+       node, built, err := tree.Node(level, index)
+       require.NoError(t, err)
+       require.True(t, built, "node (%d, %d)", level, index)
+
+       return node
 }
 
 func testNode(level, index int, text string) Node {