Skip to content

Commit 90dcd60

Browse files
authored
Merge pull request #2148 from TobyTheHutt/blobplugin-servemetrics-test
Improve test-coverage on blobplugin
2 parents e0cf214 + 1c53428 commit 90dcd60

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

pkg/blobplugin/main_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"context"
2021
"fmt"
22+
"io"
2123
"net"
24+
"net/http"
2225
"os"
2326
"reflect"
2427
"testing"
28+
"time"
2529
)
2630

2731
func TestMain(t *testing.T) {
@@ -80,3 +84,97 @@ func TestTrapClosedConnErr(t *testing.T) {
8084
}
8185
}
8286
}
87+
88+
func TestServeMetrics(t *testing.T) {
89+
// Open random test port
90+
l, err := net.Listen("tcp", "127.0.0.1:0")
91+
if err != nil {
92+
t.Fatalf("listen: %v", err)
93+
}
94+
95+
// Start serveMetrics in background
96+
errCh := make(chan error, 1)
97+
go func() { errCh <- serveMetrics(l) }()
98+
99+
// Build URL
100+
url := "http://" + l.Addr().String() + "/metrics"
101+
102+
// Client timeout for each request
103+
client := &http.Client{Timeout: 500 * time.Millisecond}
104+
105+
// Poll with 3-second deadlines until server is ready
106+
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
107+
defer cancel()
108+
109+
done := make(chan struct{})
110+
go func(ctx context.Context) {
111+
defer close(done)
112+
for {
113+
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
114+
// Execute probe, expecting status 200
115+
resp, err := client.Do(req)
116+
if err == nil {
117+
if resp.StatusCode == http.StatusOK {
118+
resp.Body.Close()
119+
return
120+
}
121+
resp.Body.Close()
122+
}
123+
// Abort probe if context deadline is expired or canceled
124+
select {
125+
case <-ctx.Done():
126+
return
127+
default:
128+
time.Sleep(20 * time.Millisecond)
129+
}
130+
}
131+
}(ctx)
132+
133+
// Wait for readiness or fail on timeout
134+
select {
135+
case <-done:
136+
case <-ctx.Done():
137+
t.Fatalf("server not ready: %v", ctx.Err())
138+
}
139+
140+
// Perform the request
141+
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
142+
resp, err := client.Do(req)
143+
if err != nil {
144+
t.Fatalf("Get /metrics: %v", err)
145+
}
146+
t.Cleanup(func() {
147+
if resp != nil && resp.Body != nil {
148+
_ = resp.Body.Close()
149+
}
150+
})
151+
152+
// Check for HTTP status 200
153+
if resp.StatusCode != http.StatusOK {
154+
t.Fatalf("unexpected status: %d", resp.StatusCode)
155+
}
156+
// Validate response body is non-empty
157+
body, err := io.ReadAll(resp.Body)
158+
if err != nil {
159+
t.Fatalf("read body: %v", err)
160+
}
161+
// Validate response body is non-empty
162+
if len(body) == 0 {
163+
t.Fatalf("empty metrics body")
164+
}
165+
166+
// Trigger graceful shutdown
167+
if err := l.Close(); err != nil {
168+
t.Fatalf("close listener %v", err)
169+
}
170+
171+
// Fail if errCh exits non-graceful or is not closed after 2 sec
172+
select {
173+
case err := <-errCh:
174+
if err != nil {
175+
t.Fatalf("serveMetrics error after close: %v", err)
176+
}
177+
case <-time.After(2 * time.Second):
178+
t.Fatalf("serveMetrics did not exit after listener close")
179+
}
180+
}

0 commit comments

Comments
 (0)