11package main
22
33import (
4+ "fmt"
45 "sync"
56 "testing"
67 "time"
@@ -97,6 +98,25 @@ func TestTracker_Concurrent(t *testing.T) {
9798 assert .Len (t , active , 1 )
9899}
99100
101+ func TestTracker_ConcurrentWithCleanup (t * testing.T ) {
102+ tracker := NewTracker (5 * time .Millisecond )
103+ defer tracker .Stop ()
104+
105+ var wg sync.WaitGroup
106+ for i := 0 ; i < 100 ; i ++ {
107+ wg .Add (1 )
108+ go func (id int ) {
109+ defer wg .Done ()
110+ tracker .Update (& LocationReport {
111+ VehicleID : fmt .Sprintf ("bus-%d" , id ),
112+ Timestamp : 1 ,
113+ })
114+ tracker .ActiveVehicles ()
115+ }(i )
116+ }
117+ wg .Wait ()
118+ }
119+
100120func TestNewTracker_PanicsOnInvalidMaxAge (t * testing.T ) {
101121 assert .PanicsWithValue (t , "maxAge must be positive" , func () {
102122 NewTracker (0 )
@@ -121,3 +141,28 @@ func TestTracker_Cleanup(t *testing.T) {
121141 return len (tracker .vehicles ) == 0
122142 }, 100 * time .Millisecond , 5 * time .Millisecond , "stale vehicle should be deleted from internal map" )
123143}
144+
145+ func TestTracker_StopIsIdempotent (t * testing.T ) {
146+ tracker := NewTracker (5 * time .Minute )
147+ tracker .Stop ()
148+ assert .NotPanics (t , func () { tracker .Stop () })
149+ }
150+
151+ func TestTracker_CleanupPreservesFreshVehicles (t * testing.T ) {
152+ maxAge := 50 * time .Millisecond
153+ tracker := NewTracker (maxAge )
154+ defer tracker .Stop ()
155+
156+ tracker .Update (& LocationReport {VehicleID : "old-bus" , Timestamp : 1 })
157+ time .Sleep (60 * time .Millisecond )
158+ tracker .Update (& LocationReport {VehicleID : "new-bus" , Timestamp : 2 })
159+
160+ assert .Eventually (t , func () bool {
161+ tracker .mu .RLock ()
162+ defer tracker .mu .RUnlock ()
163+ _ , hasOld := tracker .vehicles ["old-bus" ]
164+ _ , hasNew := tracker .vehicles ["new-bus" ]
165+ return ! hasOld && hasNew
166+ }, 200 * time .Millisecond , 5 * time .Millisecond ,
167+ "cleanup should remove old-bus but keep new-bus" )
168+ }
0 commit comments