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 }