55 "os"
66 "path/filepath"
77 "testing"
8+ "time"
89
910 "github.com/illegalstudio/lazyagent/internal/model"
1011)
@@ -59,11 +60,13 @@ func TestCWDIndex_HeadHitAvoidsReread(t *testing.T) {
5960 }
6061}
6162
62- // TestCWDIndex_InvalidatedOnChange confirms a changed file (different mtime
63- // and size) is never trusted from a stale entry -- the brief's "use the
63+ // TestCWDIndex_InvalidatedOnSizeChange confirms a changed file with a new
64+ // size is never trusted from a stale entry -- the brief's "use the
6465// same invalidate-on-change rule for uniformity and safety" requirement for
6566// claude, even though first-cwd-wins is technically stable across appends.
66- func TestCWDIndex_InvalidatedOnChange (t * testing.T ) {
67+ func TestCWDIndex_InvalidatedOnSizeChange (t * testing.T ) {
68+ const replacementCWD = "/tmp/project-b-longer"
69+
6770 dir := t .TempDir ()
6871 path := filepath .Join (dir , "session.jsonl" )
6972 if err := os .WriteFile (path , []byte (cwdLine ("/tmp/project-a" )+ "\n " ), 0o644 ); err != nil {
@@ -82,14 +85,63 @@ func TestCWDIndex_InvalidatedOnChange(t *testing.T) {
8285 }
8386
8487 // Replace the file with new (different-length) content under a new cwd.
88+ if err := os .WriteFile (path , []byte (cwdLine (replacementCWD )+ "\n " ), 0o644 ); err != nil {
89+ t .Fatal (err )
90+ }
91+ info2 , err := os .Stat (path )
92+ if err != nil {
93+ t .Fatal (err )
94+ }
95+ mtime2 , size2 := info2 .ModTime (), info2 .Size ()
96+ if size2 == size1 {
97+ t .Fatalf ("replacement size = %d, want a different size from original %d" , size2 , size1 )
98+ }
99+
100+ cwd , ok = idx .headCWDIndexed (path , mtime2 , size2 )
101+ if ! ok || cwd != replacementCWD {
102+ t .Fatalf ("after replace, headCWDIndexed = (%q, %v), want (%s, true) -- the stale project-a entry must not be trusted" , cwd , ok , replacementCWD )
103+ }
104+ }
105+
106+ // TestCWDIndex_InvalidatedOnMtimeChange covers the other half of the cache
107+ // key: a same-size rewrite must be detected when only mtime changes. Set the
108+ // timestamp explicitly so the test is reliable on coarse-mtime filesystems.
109+ func TestCWDIndex_InvalidatedOnMtimeChange (t * testing.T ) {
110+ dir := t .TempDir ()
111+ path := filepath .Join (dir , "session.jsonl" )
112+ if err := os .WriteFile (path , []byte (cwdLine ("/tmp/project-a" )+ "\n " ), 0o644 ); err != nil {
113+ t .Fatal (err )
114+ }
115+ info , err := os .Stat (path )
116+ if err != nil {
117+ t .Fatal (err )
118+ }
119+ mtime1 , size1 := info .ModTime (), info .Size ()
120+
121+ idx := NewCWDIndex ()
122+ cwd , ok := idx .headCWDIndexed (path , mtime1 , size1 )
123+ if ! ok || cwd != "/tmp/project-a" {
124+ t .Fatalf ("first scan = (%q, %v), want (/tmp/project-a, true)" , cwd , ok )
125+ }
126+
85127 if err := os .WriteFile (path , []byte (cwdLine ("/tmp/project-b" )+ "\n " ), 0o644 ); err != nil {
86128 t .Fatal (err )
87129 }
130+ replacementMtime := mtime1 .Add (2 * time .Second )
131+ if err := os .Chtimes (path , replacementMtime , replacementMtime ); err != nil {
132+ t .Fatal (err )
133+ }
88134 info2 , err := os .Stat (path )
89135 if err != nil {
90136 t .Fatal (err )
91137 }
92138 mtime2 , size2 := info2 .ModTime (), info2 .Size ()
139+ if size2 != size1 {
140+ t .Fatalf ("replacement size = %d, want original size %d" , size2 , size1 )
141+ }
142+ if mtime2 .Equal (mtime1 ) {
143+ t .Fatalf ("replacement mtime = %s, want a different mtime from original %s" , mtime2 , mtime1 )
144+ }
93145
94146 cwd , ok = idx .headCWDIndexed (path , mtime2 , size2 )
95147 if ! ok || cwd != "/tmp/project-b" {
0 commit comments