9 "github.com/stretchr/testify/require"
12 func TestLogReopenLoadsTheSameMessages(t *testing.T) {
14 clock := newTestClock()
15 log, err := openLog(t.Context(), root, clock.Now)
16 require.NoError(t, err)
19 for _, kind := range []string{"user", "reply", "tool", "echo", "work", "note"} {
20 message, err := log.Append(kind, kind+" text ✓")
21 require.NoError(t, err)
23 written = append(written, message)
26 require.Equal(t, 5, written[5].ID)
27 require.Equal(t, len("note text ✓"), written[5].Size)
28 require.NoError(t, log.Close())
30 log, err = openLog(t.Context(), root, clock.Now)
31 require.NoError(t, err)
34 require.NoError(t, log.Close())
37 require.Equal(t, written, log.messages)
39 message, err := log.Append("user", "after the reopen")
40 require.NoError(t, err)
41 require.Equal(t, 6, message.ID)
44 func TestLogWritesOneFilePerDay(t *testing.T) {
46 clock := newTestClock()
47 log, err := openLog(t.Context(), root, clock.Now)
48 require.NoError(t, err)
51 require.NoError(t, log.Close())
54 _, err = log.Append("user", "first day")
55 require.NoError(t, err)
57 clock.now = clock.now.Add(24 * time.Hour)
58 _, err = log.Append("user", "second day")
59 require.NoError(t, err)
61 first, err := os.ReadFile(filepath.Join(root, "main", "2026-01-01.jsonl"))
62 require.NoError(t, err)
63 require.Equal(t, `{"i":0,"kind":"user","text":"first day","size":9,"date":"2026-01-01T12:00:00Z"}`+"\n", string(first))
65 second, err := os.ReadFile(filepath.Join(root, "main", "2026-01-02.jsonl"))
66 require.NoError(t, err)
67 require.Equal(t, `{"i":1,"kind":"user","text":"second day","size":10,"date":"2026-01-02T12:00:00Z"}`+"\n", string(second))
70 func TestLogAllowsOneWriter(t *testing.T) {
72 clock := newTestClock()
73 log, err := openLog(t.Context(), root, clock.Now)
74 require.NoError(t, err)
76 _, err = openLog(t.Context(), root, clock.Now)
77 require.ErrorIs(t, err, errLocked)
78 require.NoError(t, log.Close())
80 log, err = openLog(t.Context(), root, clock.Now)
81 require.NoError(t, err)
82 require.NoError(t, log.Close())
85 func TestLogRejectsAnUnknownKind(t *testing.T) {
86 log, err := openLog(t.Context(), t.TempDir(), newTestClock().Now)
87 require.NoError(t, err)
90 require.NoError(t, log.Close())
93 _, err = log.Append("talk", "text")
94 require.ErrorContains(t, err, `unknown message kind "talk"`)
95 require.Equal(t, 0, log.Len())
98 func TestLogRejectsAGapInTheIDs(t *testing.T) {
100 require.NoError(t, os.MkdirAll(filepath.Join(root, "main"), 0o700))
102 line := `{"i":1,"kind":"user","text":"x","size":1,"date":"2026-01-01T12:00:00Z"}` + "\n"
103 require.NoError(t, os.WriteFile(filepath.Join(root, "main", "2026-01-01.jsonl"), []byte(line), 0o600))
105 _, err := openLog(t.Context(), root, newTestClock().Now)
106 require.ErrorContains(t, err, "expected message 0, found 1")
109 // testClock is a clock that tests set by hand.
110 type testClock struct {
114 func newTestClock() *testClock {
116 now: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC),
120 func (c *testClock) Now() time.Time {