@@ -15,6 +15,74 @@ import (
1515 "github.com/stretchr/testify/require"
1616)
1717
18+ func TestRunner_resumeAfterInterrupt (t * testing.T ) {
19+ domains := []string {"a.com" , "b.com" , "c.com" , "d.com" , "e.com" , "f.com" , "g.com" , "h.com" , "i.com" , "j.com" }
20+ interruptAfter := 4
21+
22+ // --- Full scan (reference): process all domains without interrupt ---
23+ rFull , err := New (& Options {})
24+ require .Nil (t , err , "could not create httpx runner" )
25+ rFull .options .resumeCfg = & ResumeCfg {}
26+ var fullOutput []string
27+ for _ , d := range domains {
28+ rFull .options .resumeCfg .current = d
29+ rFull .options .resumeCfg .currentIndex ++
30+ fullOutput = append (fullOutput , d )
31+ }
32+
33+ // --- Interrupted scan: process items, interrupt after interruptAfter ---
34+ rInt , err := New (& Options {})
35+ require .Nil (t , err , "could not create httpx runner" )
36+ rInt .options .resumeCfg = & ResumeCfg {}
37+ var interruptedOutput []string
38+ for _ , d := range domains {
39+ // same check as processItem: bail out if interrupted
40+ select {
41+ case <- rInt .interruptCh :
42+ continue
43+ default :
44+ }
45+
46+ rInt .options .resumeCfg .current = d
47+ rInt .options .resumeCfg .currentIndex ++
48+ interruptedOutput = append (interruptedOutput , d )
49+
50+ if len (interruptedOutput ) == interruptAfter {
51+ rInt .Interrupt ()
52+ }
53+ }
54+
55+ // simulate SaveResumeConfig: save the index after interrupt
56+ savedIndex := rInt .options .resumeCfg .currentIndex
57+
58+ // the saved index must equal exactly the number of items that were processed
59+ require .Equal (t , interruptAfter , savedIndex , "resume index should equal number of completed items" )
60+ // every domain before the index must be in the interrupted output
61+ require .Equal (t , domains [:interruptAfter ], interruptedOutput , "interrupted output should contain exactly the first N domains" )
62+
63+ // --- Resumed scan: load saved index, skip already-processed items ---
64+ rRes , err := New (& Options {})
65+ require .Nil (t , err , "could not create httpx runner" )
66+ rRes .options .resumeCfg = & ResumeCfg {Index : savedIndex }
67+ var resumedOutput []string
68+ for _ , d := range domains {
69+ // same resume-skip logic as processItem
70+ rRes .options .resumeCfg .current = d
71+ rRes .options .resumeCfg .currentIndex ++
72+ if rRes .options .resumeCfg .currentIndex <= rRes .options .resumeCfg .Index {
73+ continue
74+ }
75+ resumedOutput = append (resumedOutput , d )
76+ }
77+
78+ // every domain after the index must be in the resumed output
79+ require .Equal (t , domains [interruptAfter :], resumedOutput , "resumed output should contain exactly the remaining domains" )
80+
81+ // union of interrupted + resumed must equal the full scan
82+ combined := append (interruptedOutput , resumedOutput ... )
83+ require .Equal (t , fullOutput , combined , "interrupted + resumed should equal full scan" )
84+ }
85+
1886func TestRunner_domain_targets (t * testing.T ) {
1987 options := & Options {}
2088 r , err := New (options )
0 commit comments