1package filetracker
2
3import (
4 "context"
5 "testing"
6 "testing/synctest"
7 "time"
8
9 "github.com/charmbracelet/crush/internal/db"
10 "github.com/stretchr/testify/require"
11)
12
13type testEnv struct {
14 ctx context.Context
15 q *db.Queries
16 svc Service
17}
18
19func setupTest(t *testing.T) *testEnv {
20 t.Helper()
21
22 conn, err := db.Connect(t.Context(), t.TempDir())
23 require.NoError(t, err)
24 t.Cleanup(func() { conn.Close() })
25
26 q := db.New(conn)
27 return &testEnv{
28 ctx: t.Context(),
29 q: q,
30 svc: NewService(q),
31 }
32}
33
34func (e *testEnv) createSession(t *testing.T, sessionID string) {
35 t.Helper()
36 _, err := e.q.CreateSession(e.ctx, db.CreateSessionParams{
37 ID: sessionID,
38 Title: "Test Session",
39 })
40 require.NoError(t, err)
41}
42
43func TestService_RecordRead(t *testing.T) {
44 env := setupTest(t)
45
46 sessionID := "test-session-1"
47 path := "/path/to/file.go"
48 env.createSession(t, sessionID)
49
50 env.svc.RecordRead(env.ctx, sessionID, path)
51
52 lastRead := env.svc.LastReadTime(env.ctx, sessionID, path)
53 require.False(t, lastRead.IsZero(), "expected non-zero time after recording read")
54 require.WithinDuration(t, time.Now(), lastRead, 2*time.Second)
55}
56
57func TestService_LastReadTime_NotFound(t *testing.T) {
58 env := setupTest(t)
59
60 lastRead := env.svc.LastReadTime(env.ctx, "nonexistent-session", "/nonexistent/path")
61 require.True(t, lastRead.IsZero(), "expected zero time for unread file")
62}
63
64func TestService_RecordRead_UpdatesTimestamp(t *testing.T) {
65 env := setupTest(t)
66
67 sessionID := "test-session-2"
68 path := "/path/to/file.go"
69 env.createSession(t, sessionID)
70
71 env.svc.RecordRead(env.ctx, sessionID, path)
72 firstRead := env.svc.LastReadTime(env.ctx, sessionID, path)
73 require.False(t, firstRead.IsZero())
74
75 synctest.Test(t, func(t *testing.T) {
76 time.Sleep(100 * time.Millisecond)
77 synctest.Wait()
78 env.svc.RecordRead(env.ctx, sessionID, path)
79 secondRead := env.svc.LastReadTime(env.ctx, sessionID, path)
80
81 require.False(t, secondRead.Before(firstRead), "second read time should not be before first")
82 })
83}
84
85func TestService_RecordRead_DifferentSessions(t *testing.T) {
86 env := setupTest(t)
87
88 path := "/shared/file.go"
89 session1, session2 := "session-1", "session-2"
90 env.createSession(t, session1)
91 env.createSession(t, session2)
92
93 env.svc.RecordRead(env.ctx, session1, path)
94
95 lastRead1 := env.svc.LastReadTime(env.ctx, session1, path)
96 require.False(t, lastRead1.IsZero())
97
98 lastRead2 := env.svc.LastReadTime(env.ctx, session2, path)
99 require.True(t, lastRead2.IsZero(), "session 2 should not see session 1's read")
100}
101
102func TestService_RecordRead_DifferentPaths(t *testing.T) {
103 env := setupTest(t)
104
105 sessionID := "test-session-3"
106 path1, path2 := "/path/to/file1.go", "/path/to/file2.go"
107 env.createSession(t, sessionID)
108
109 env.svc.RecordRead(env.ctx, sessionID, path1)
110
111 lastRead1 := env.svc.LastReadTime(env.ctx, sessionID, path1)
112 require.False(t, lastRead1.IsZero())
113
114 lastRead2 := env.svc.LastReadTime(env.ctx, sessionID, path2)
115 require.True(t, lastRead2.IsZero(), "path2 should not be recorded")
116}