44package harvest
55
66import (
7+ "context"
78 "errors"
89 "os"
910 "path/filepath"
@@ -21,56 +22,64 @@ import (
2122)
2223
2324// successfulExtractRoutes simulates a successful route extraction
24- func successfulExtractRoutes (app.PID ) (* RouteHarvesterResult , error ) {
25+ func successfulExtractRoutes (context. Context , app.PID ) (* RouteHarvesterResult , error ) {
2526 return & RouteHarvesterResult {
2627 Routes : []string {"/api/users" , "/api/orders" },
2728 Kind : CompleteRoutes ,
2829 }, nil
2930}
3031
3132// errorExtractRoutes simulates an error during route extraction
32- func errorExtractRoutes (app.PID ) (* RouteHarvesterResult , error ) {
33+ func errorExtractRoutes (context. Context , app.PID ) (* RouteHarvesterResult , error ) {
3334 return nil , errors .New ("failed to connect to Java process" )
3435}
3536
3637// timeoutExtractRoutes simulates a slow operation that will timeout
37- func timeoutExtractRoutes (app.PID ) (* RouteHarvesterResult , error ) {
38- // Sleep longer than any reasonable timeout
39- time .Sleep (5 * time .Second )
40- return & RouteHarvesterResult {
41- Routes : []string {"/api/delayed" },
42- Kind : CompleteRoutes ,
43- }, nil
38+ func timeoutExtractRoutes (ctx context.Context , _ app.PID ) (* RouteHarvesterResult , error ) {
39+ <- ctx .Done ()
40+ return nil , ctx .Err ()
4441}
4542
4643// panicExtractRoutes simulates a panic during route extraction
47- func panicExtractRoutes (app.PID ) (* RouteHarvesterResult , error ) {
44+ func panicExtractRoutes (context. Context , app.PID ) (* RouteHarvesterResult , error ) {
4845 panic ("unexpected error in java route extraction" )
4946}
5047
5148// slowButSuccessfulExtractRoutes simulates a slow but successful operation
52- func slowButSuccessfulExtractRoutes (app.PID ) (* RouteHarvesterResult , error ) {
53- time .Sleep (50 * time .Millisecond ) // Slow but within timeout
49+ func slowButSuccessfulExtractRoutes (ctx context.Context , _ app.PID ) (* RouteHarvesterResult , error ) {
50+ select {
51+ case <- ctx .Done ():
52+ return nil , ctx .Err ()
53+ case <- time .After (50 * time .Millisecond ): // Slow but within timeout
54+ }
5455 return & RouteHarvesterResult {
5556 Routes : []string {"/api/slow" },
5657 Kind : PartialRoutes ,
5758 }, nil
5859}
5960
6061// emptyResultExtractRoutes simulates successful extraction with no routes
61- func emptyResultExtractRoutes (app.PID ) (* RouteHarvesterResult , error ) {
62+ func emptyResultExtractRoutes (context. Context , app.PID ) (* RouteHarvesterResult , error ) {
6263 return & RouteHarvesterResult {
6364 Routes : []string {},
6465 Kind : CompleteRoutes ,
6566 }, nil
6667}
6768
68- func javaExtract (fn func (app.PID ) (* RouteHarvesterResult , error )) func (* exec.FileInfo ) (* RouteHarvesterResult , error ) {
69- return func (fileInfo * exec.FileInfo ) (* RouteHarvesterResult , error ) {
70- return fn (fileInfo .Pid ())
69+ func javaExtract (fn func (context. Context , app.PID ) (* RouteHarvesterResult , error )) func (context. Context , * exec.FileInfo ) (* RouteHarvesterResult , error ) {
70+ return func (ctx context. Context , fileInfo * exec.FileInfo ) (* RouteHarvesterResult , error ) {
71+ return fn (ctx , fileInfo .Pid ())
7172 }
7273}
7374
75+ func successfulNodeExtractRoutes (pid app.PID ) (* RouteHarvesterResult , error ) {
76+ return successfulExtractRoutes (context .Background (), pid )
77+ }
78+
79+ func errorNodeExtractRoutes (pid app.PID ) (* RouteHarvesterResult , error ) {
80+ return errorExtractRoutes (context .Background (), pid )
81+ }
82+
7483func createTestFileInfo (language svc.InstrumentableType ) * exec.FileInfo {
7584 return exec .New (exec.Init {
7685 Pid : 12345 ,
@@ -130,6 +139,24 @@ func TestHarvestRoutes_Timeout(t *testing.T) {
130139 assert .Greater (t , elapsed , 90 * time .Millisecond )
131140}
132141
142+ func TestHarvestRoutes_ContextDeadlineResultReturnsTimeout (t * testing.T ) {
143+ harvester := NewRouteHarvester (& services.RouteHarvestingConfig {}, []services.RouteHarvesterLanguage {}, 1 * time .Second )
144+ harvester .javaExtractRoutes = func (context.Context , * exec.FileInfo ) (* RouteHarvesterResult , error ) {
145+ return nil , context .DeadlineExceeded
146+ }
147+
148+ fileInfo := createTestFileInfo (svc .InstrumentableJava )
149+
150+ result , err := harvester .HarvestRoutes (fileInfo )
151+
152+ require .Error (t , err )
153+ assert .Nil (t , result )
154+
155+ var harvestErr * HarvestError
156+ require .ErrorAs (t , err , & harvestErr )
157+ assert .Equal (t , "route harvesting timed out" , harvestErr .Message )
158+ }
159+
133160func TestHarvestRoutes_Panic (t * testing.T ) {
134161 harvester := NewRouteHarvester (& services.RouteHarvestingConfig {}, []services.RouteHarvesterLanguage {}, 1 * time .Second )
135162 harvester .javaExtractRoutes = javaExtract (panicExtractRoutes )
@@ -178,7 +205,7 @@ func TestHarvestRoutes_EmptyResult(t *testing.T) {
178205func TestHarvestRoutes_NonJavaLanguage (t * testing.T ) {
179206 harvester := NewRouteHarvester (& services.RouteHarvestingConfig {}, []services.RouteHarvesterLanguage {}, 1 * time .Second )
180207 // javaExtractRoutes should not be called for non-Java languages
181- harvester .javaExtractRoutes = func (_ * exec.FileInfo ) (* RouteHarvesterResult , error ) {
208+ harvester .javaExtractRoutes = func (context. Context , * exec.FileInfo ) (* RouteHarvesterResult , error ) {
182209 t .Fatal ("javaExtractRoutes should not be called for non-Java languages" )
183210 return nil , nil
184211 }
@@ -193,7 +220,14 @@ func TestHarvestRoutes_NonJavaLanguage(t *testing.T) {
193220
194221func TestHarvestRoutes_MultipleTimeouts (t * testing.T ) {
195222 harvester := NewRouteHarvester (& services.RouteHarvestingConfig {}, []services.RouteHarvesterLanguage {}, 50 * time .Millisecond )
196- harvester .javaExtractRoutes = javaExtract (timeoutExtractRoutes )
223+
224+ exited := make (chan struct {}, 3 )
225+ harvester .javaExtractRoutes = func (ctx context.Context , fileInfo * exec.FileInfo ) (* RouteHarvesterResult , error ) {
226+ defer func () {
227+ exited <- struct {}{}
228+ }()
229+ return timeoutExtractRoutes (ctx , fileInfo .Pid ())
230+ }
197231
198232 fileInfo := createTestFileInfo (svc .InstrumentableJava )
199233
@@ -208,11 +242,15 @@ func TestHarvestRoutes_MultipleTimeouts(t *testing.T) {
208242 require .ErrorAs (t , err , & harvestErr , "iteration %d should return HarvestError" , i )
209243 assert .Equal (t , "route harvesting timed out" , harvestErr .Message , "iteration %d should have timeout message" , i )
210244 }
245+
246+ require .Eventually (t , func () bool {
247+ return len (exited ) == 3
248+ }, time .Second , time .Millisecond )
211249}
212250
213251func TestHarvestNodejsRoutes_Successful (t * testing.T ) {
214252 harvester := NewRouteHarvester (& services.RouteHarvestingConfig {}, []services.RouteHarvesterLanguage {}, 1 * time .Second )
215- harvester .nodeExtractRoutes = successfulExtractRoutes
253+ harvester .nodeExtractRoutes = successfulNodeExtractRoutes
216254
217255 fileInfo := createTestFileInfo (svc .InstrumentableNodejs )
218256
@@ -226,7 +264,7 @@ func TestHarvestNodejsRoutes_Successful(t *testing.T) {
226264
227265func TestHarvestNodejsRoutes_Error (t * testing.T ) {
228266 harvester := NewRouteHarvester (& services.RouteHarvestingConfig {}, []services.RouteHarvesterLanguage {}, 1 * time .Second )
229- harvester .nodeExtractRoutes = errorExtractRoutes
267+ harvester .nodeExtractRoutes = errorNodeExtractRoutes
230268
231269 fileInfo := createTestFileInfo (svc .InstrumentableNodejs )
232270
0 commit comments