blob: 4c67790b1f9b16f5e7fa1f995a4b61517310e1c9 [file] [log] [blame]
Philip Zeyliger5f26a342025-07-04 01:30:29 +00001package loop
2
3import (
4 "context"
5 "fmt"
6 "net"
7 "net/http"
8 "sync"
9 "testing"
10 "time"
11)
12
13// TestPortMonitor_IntegrationDemo demonstrates the full integration of PortMonitor with an Agent.
14// This test shows how the PortMonitor detects port changes and sends notifications to an Agent.
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000015// The test focuses on specific ports it creates rather than making assumptions about total port counts.
Philip Zeyliger5f26a342025-07-04 01:30:29 +000016func TestPortMonitor_IntegrationDemo(t *testing.T) {
17 // Create a test agent
18 agent := createTestAgent(t)
19
20 // Create and start the port monitor
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000021 pm := NewPortMonitor(agent, 25*time.Millisecond) // Fast polling for demo
Philip Zeyliger5f26a342025-07-04 01:30:29 +000022 ctx := context.Background()
23 err := pm.Start(ctx)
24 if err != nil {
25 t.Fatalf("Failed to start port monitor: %v", err)
26 }
27 defer pm.Stop()
28
Philip Zeyliger5f26a342025-07-04 01:30:29 +000029 // Show current ports
30 currentPorts := pm.GetPorts()
31 t.Logf("Initial TCP ports detected: %d", len(currentPorts))
32 for _, port := range currentPorts {
33 t.Logf(" - Port %d (process: %s, pid: %d)", port.Port, port.Process, port.Pid)
34 }
35
36 // Start multiple test servers to demonstrate detection
37 var listeners []net.Listener
38 var wg sync.WaitGroup
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000039 var testPorts []uint16
Philip Zeyliger5f26a342025-07-04 01:30:29 +000040
41 // Start 3 test HTTP servers
42 for i := 0; i < 3; i++ {
43 listener, err := net.Listen("tcp", "127.0.0.1:0")
44 if err != nil {
45 t.Fatalf("Failed to start test listener %d: %v", i, err)
46 }
47 listeners = append(listeners, listener)
48
49 addr := listener.Addr().(*net.TCPAddr)
50 port := addr.Port
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000051 testPorts = append(testPorts, uint16(port))
Philip Zeyliger5f26a342025-07-04 01:30:29 +000052 t.Logf("Started test HTTP server %d on port %d", i+1, port)
53
54 // Start a simple HTTP server
55 wg.Add(1)
56 go func(l net.Listener, serverID int) {
57 defer wg.Done()
58 mux := http.NewServeMux()
59 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
60 fmt.Fprintf(w, "Hello from test server %d!\n", serverID)
61 })
62 server := &http.Server{Handler: mux}
63 server.Serve(l)
64 }(listener, i+1)
65 }
66
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000067 // Wait for ports to be detected with timeout
68 detectionTimeout := time.After(15 * time.Second)
69 detectionTicker := time.NewTicker(25 * time.Millisecond)
70 defer detectionTicker.Stop()
Philip Zeyliger5f26a342025-07-04 01:30:29 +000071
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000072 allPortsDetected := false
73detectionLoop:
74 for !allPortsDetected {
75 select {
76 case <-detectionTimeout:
77 t.Fatalf("Timeout waiting for test ports to be detected")
78 case <-detectionTicker.C:
79 // Check if all test ports are detected
80 updatedPorts := pm.GetPorts()
81 portMap := make(map[uint16]bool)
82 for _, port := range updatedPorts {
83 portMap[port.Port] = true
84 }
Philip Zeyliger5f26a342025-07-04 01:30:29 +000085
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +000086 allPortsDetected = true
87 for _, testPort := range testPorts {
88 if !portMap[testPort] {
89 allPortsDetected = false
90 break
91 }
92 }
93 if allPortsDetected {
94 t.Logf("All test ports successfully detected: %v", testPorts)
95 break detectionLoop
96 }
Philip Zeyliger5f26a342025-07-04 01:30:29 +000097 }
98 }
99
Philip Zeyliger5f26a342025-07-04 01:30:29 +0000100 // Close all listeners
101 for i, listener := range listeners {
102 t.Logf("Closing test server %d", i+1)
103 listener.Close()
104 }
105
106 // Wait for servers to stop
107 wg.Wait()
108
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +0000109 // Wait for ports to be removed with timeout
110 removalTimeout := time.After(15 * time.Second)
111 removalTicker := time.NewTicker(25 * time.Millisecond)
112 defer removalTicker.Stop()
Philip Zeyliger5f26a342025-07-04 01:30:29 +0000113
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +0000114 allPortsRemoved := false
115removalLoop:
116 for !allPortsRemoved {
117 select {
118 case <-removalTimeout:
119 t.Fatalf("Timeout waiting for test ports to be removed")
120 case <-removalTicker.C:
121 // Check if all test ports are removed
122 finalPorts := pm.GetPorts()
123 portMap := make(map[uint16]bool)
124 for _, port := range finalPorts {
125 portMap[port.Port] = true
126 }
Philip Zeyliger5f26a342025-07-04 01:30:29 +0000127
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +0000128 allPortsRemoved = true
129 for _, testPort := range testPorts {
130 if portMap[testPort] {
131 allPortsRemoved = false
132 break
133 }
134 }
135 if allPortsRemoved {
136 t.Logf("All test ports successfully removed: %v", testPorts)
137 break removalLoop
138 }
Philip Zeyliger5f26a342025-07-04 01:30:29 +0000139 }
140 }
141
Philip Zeyliger5f26a342025-07-04 01:30:29 +0000142 t.Logf("Integration test completed successfully!")
Josh Bleecher Snyder828161b2025-07-08 03:44:39 +0000143 t.Logf("- Test ports created and monitored: %v", testPorts)
Philip Zeyliger5f26a342025-07-04 01:30:29 +0000144}
145
146// contains checks if a string contains a substring.
147func contains(s, substr string) bool {
148 return len(s) >= len(substr) && (s == substr ||
149 (len(s) > len(substr) &&
150 (s[:len(substr)] == substr ||
151 s[len(s)-len(substr):] == substr ||
152 indexOfSubstring(s, substr) >= 0)))
153}
154
155// indexOfSubstring finds the index of substring in string.
156func indexOfSubstring(s, substr string) int {
157 if len(substr) == 0 {
158 return 0
159 }
160 for i := 0; i <= len(s)-len(substr); i++ {
161 if s[i:i+len(substr)] == substr {
162 return i
163 }
164 }
165 return -1
166}