m/pkg/logtree: fix exact backlog fetch, head/tail confusion

This started off as 'hm, the backlog data returned seems wrong'. I
realized we had no test for that, so I added one. It was indeed broken.

This was because we had two simultaneous bugs: we confused head/tail
between docs and different parts of the code, and we forgot to do a
reverse operation when scanning/retrieving journal entries.

With those two fixed, we also implement backlog retrieval in a optimized
fashion, but not scanning/retrieving more entries that is necessary.

Finally, we drive-by fix a massacred ASCII graphic in a comment.

Change-Id: I2ec5dd9b5b58f66fbc015c142feb91bd92038e4f
Reviewed-on: https://review.monogon.dev/c/monogon/+/1430
Tested-by: Jenkins CI
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/metropolis/pkg/logtree/logtree_test.go b/metropolis/pkg/logtree/logtree_test.go
index a7614a4..e37893a 100644
--- a/metropolis/pkg/logtree/logtree_test.go
+++ b/metropolis/pkg/logtree/logtree_test.go
@@ -29,6 +29,7 @@
 	if err != nil {
 		t.Fatalf("Read: %v", err)
 	}
+	defer res.Close()
 	if want, got := len(entries), len(res.Backlog); want != got {
 		t.Fatalf("wanted %v backlog entries, got %v", want, got)
 	}
@@ -49,6 +50,29 @@
 	return ""
 }
 
+func readBacklog(tree *LogTree, t *testing.T, dn DN, backlog int, recursive bool) []string {
+	t.Helper()
+	opts := []LogReadOption{
+		WithBacklog(backlog),
+	}
+	if recursive {
+		opts = append(opts, WithChildren())
+	}
+	res, err := tree.Read(dn, opts...)
+	if err != nil {
+		t.Fatalf("Read: %v", err)
+	}
+	defer res.Close()
+
+	var lines []string
+	for _, e := range res.Backlog {
+		for _, msg := range e.Leveled.Messages() {
+			lines = append(lines, msg)
+		}
+	}
+	return lines
+}
+
 func TestMultiline(t *testing.T) {
 	tree := New()
 	// Two lines in a single message.
@@ -61,7 +85,7 @@
 	}
 }
 
-func TestBacklog(t *testing.T) {
+func TestBacklogAll(t *testing.T) {
 	tree := New()
 	tree.MustLeveledFor("main").Info("hello, main!")
 	tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
@@ -81,6 +105,44 @@
 	}
 }
 
+func TestBacklogExact(t *testing.T) {
+	tree := New()
+	tree.MustLeveledFor("main").Info("hello, main!")
+	tree.MustLeveledFor("main.foo").Info("hello, main.foo!")
+	tree.MustLeveledFor("main.bar").Info("hello, main.bar!")
+	tree.MustLeveledFor("main.bar.chatty").Info("hey there how are you")
+	tree.MustLeveledFor("main.bar.quiet").Info("fine how are you")
+	tree.MustLeveledFor("main.bar.chatty").Info("i've been alright myself")
+	tree.MustLeveledFor("main.bar.chatty").Info("but to tell you honestly...")
+	tree.MustLeveledFor("main.bar.chatty").Info("i feel like i'm stuck?")
+	tree.MustLeveledFor("main.bar.quiet").Info("mhm")
+	tree.MustLeveledFor("main.bar.chatty").Info("like you know what i'm saying, stuck in like")
+	tree.MustLeveledFor("main.bar.chatty").Info("like a go test?")
+	tree.MustLeveledFor("main.bar.quiet").Info("yeah totally")
+	tree.MustLeveledFor("main.bar.chatty").Info("it's hard to put my finger on it")
+	tree.MustLeveledFor("main.bar.chatty").Info("anyway, how's the wife doing?")
+
+	check := func(a []string, b ...string) {
+		t.Helper()
+		if len(a) != len(b) {
+			t.Errorf("Legth mismatch: wanted %d, got %d", len(b), len(a))
+		}
+		count := len(a)
+		if len(b) < count {
+			count = len(b)
+		}
+		for i := 0; i < count; i++ {
+			if want, got := b[i], a[i]; want != got {
+				t.Errorf("Message %d: wanted %q, got %q", i, want, got)
+			}
+		}
+	}
+
+	check(readBacklog(tree, t, "main", 3, true), "yeah totally", "it's hard to put my finger on it", "anyway, how's the wife doing?")
+	check(readBacklog(tree, t, "main.foo", 3, false), "hello, main.foo!")
+	check(readBacklog(tree, t, "main.bar.quiet", 2, true), "mhm", "yeah totally")
+}
+
 func TestStream(t *testing.T) {
 	tree := New()
 	tree.MustLeveledFor("main").Info("hello, backlog")