+package main
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestLogReopenLoadsTheSameMessages(t *testing.T) {
+ root := t.TempDir()
+ clock := newTestClock()
+ log, err := openLog(t.Context(), root, clock.Now)
+ require.NoError(t, err)
+
+ var written []Message
+ for _, kind := range []string{"user", "unii", "tool", "echo", "work", "note"} {
+ message, err := log.Append(kind, kind+" text ✓")
+ require.NoError(t, err)
+
+ written = append(written, message)
+ }
+
+ require.Equal(t, 5, written[5].ID)
+ require.Equal(t, len("note text ✓"), written[5].Size)
+ require.NoError(t, log.Close())
+
+ log, err = openLog(t.Context(), root, clock.Now)
+ require.NoError(t, err)
+
+ defer func() {
+ require.NoError(t, log.Close())
+ }()
+
+ require.Equal(t, written, log.messages)
+
+ message, err := log.Append("user", "after the reopen")
+ require.NoError(t, err)
+ require.Equal(t, 6, message.ID)
+}
+
+func TestLogWritesOneFilePerDay(t *testing.T) {
+ root := t.TempDir()
+ clock := newTestClock()
+ log, err := openLog(t.Context(), root, clock.Now)
+ require.NoError(t, err)
+
+ defer func() {
+ require.NoError(t, log.Close())
+ }()
+
+ _, err = log.Append("user", "first day")
+ require.NoError(t, err)
+
+ clock.now = clock.now.Add(24 * time.Hour)
+ _, err = log.Append("user", "second day")
+ require.NoError(t, err)
+
+ first, err := os.ReadFile(filepath.Join(root, "main", "2026-01-01.jsonl"))
+ require.NoError(t, err)
+ require.Equal(t, `{"i":0,"kind":"user","text":"first day","size":9,"date":"2026-01-01T12:00:00Z"}`+"\n", string(first))
+
+ second, err := os.ReadFile(filepath.Join(root, "main", "2026-01-02.jsonl"))
+ require.NoError(t, err)
+ require.Equal(t, `{"i":1,"kind":"user","text":"second day","size":10,"date":"2026-01-02T12:00:00Z"}`+"\n", string(second))
+}
+
+func TestLogAllowsOneWriter(t *testing.T) {
+ root := t.TempDir()
+ clock := newTestClock()
+ log, err := openLog(t.Context(), root, clock.Now)
+ require.NoError(t, err)
+
+ _, err = openLog(t.Context(), root, clock.Now)
+ require.ErrorIs(t, err, errLocked)
+ require.NoError(t, log.Close())
+
+ log, err = openLog(t.Context(), root, clock.Now)
+ require.NoError(t, err)
+ require.NoError(t, log.Close())
+}
+
+func TestLogRejectsAnUnknownKind(t *testing.T) {
+ log, err := openLog(t.Context(), t.TempDir(), newTestClock().Now)
+ require.NoError(t, err)
+
+ defer func() {
+ require.NoError(t, log.Close())
+ }()
+
+ _, err = log.Append("talk", "text")
+ require.ErrorContains(t, err, `unknown message kind "talk"`)
+ require.Equal(t, 0, log.Len())
+}
+
+func TestLogRejectsAGapInTheIDs(t *testing.T) {
+ root := t.TempDir()
+ require.NoError(t, os.MkdirAll(filepath.Join(root, "main"), 0o700))
+
+ line := `{"i":1,"kind":"user","text":"x","size":1,"date":"2026-01-01T12:00:00Z"}` + "\n"
+ require.NoError(t, os.WriteFile(filepath.Join(root, "main", "2026-01-01.jsonl"), []byte(line), 0o600))
+
+ _, err := openLog(t.Context(), root, newTestClock().Now)
+ require.ErrorContains(t, err, "expected message 0, found 1")
+}
+
+// testClock is a clock that tests set by hand.
+type testClock struct {
+ now time.Time
+}
+
+func newTestClock() *testClock {
+ return &testClock{
+ now: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC),
+ }
+}
+
+func (c *testClock) Now() time.Time {
+ return c.now
+}