- CheckPorts now runs all port checks concurrently using errgroup - Added port number validation (1-65535) with ErrInvalidPort sentinel error - Updated PortChecker interface to use *PortResult return type - Added tests for invalid port numbers (0, negative, >65535) - All checks pass (make check clean)
212 lines
3.6 KiB
Go
212 lines
3.6 KiB
Go
package portcheck_test
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/dnswatcher/internal/portcheck"
|
|
)
|
|
|
|
func listenTCP(
|
|
t *testing.T,
|
|
) (net.Listener, int) {
|
|
t.Helper()
|
|
|
|
lc := &net.ListenConfig{}
|
|
|
|
ln, err := lc.Listen(
|
|
context.Background(), "tcp", "127.0.0.1:0",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("failed to start listener: %v", err)
|
|
}
|
|
|
|
addr, ok := ln.Addr().(*net.TCPAddr)
|
|
if !ok {
|
|
t.Fatal("unexpected address type")
|
|
}
|
|
|
|
return ln, addr.Port
|
|
}
|
|
|
|
func TestCheckPortOpen(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ln, port := listenTCP(t)
|
|
|
|
defer func() { _ = ln.Close() }()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
result, err := checker.CheckPort(
|
|
context.Background(), "127.0.0.1", port,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if !result.Open {
|
|
t.Error("expected port to be open")
|
|
}
|
|
|
|
if result.Error != "" {
|
|
t.Errorf("expected no error, got: %s", result.Error)
|
|
}
|
|
|
|
if result.Latency <= 0 {
|
|
t.Error("expected positive latency")
|
|
}
|
|
}
|
|
|
|
func TestCheckPortClosed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ln, port := listenTCP(t)
|
|
_ = ln.Close()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
result, err := checker.CheckPort(
|
|
context.Background(), "127.0.0.1", port,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if result.Open {
|
|
t.Error("expected port to be closed")
|
|
}
|
|
|
|
if result.Error == "" {
|
|
t.Error("expected error message for closed port")
|
|
}
|
|
}
|
|
|
|
func TestCheckPortContextCanceled(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
result, err := checker.CheckPort(ctx, "127.0.0.1", 1)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if result.Open {
|
|
t.Error("expected port to not be open")
|
|
}
|
|
}
|
|
|
|
func TestCheckPortsMultiple(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ln, openPort := listenTCP(t)
|
|
|
|
defer func() { _ = ln.Close() }()
|
|
|
|
ln2, closedPort := listenTCP(t)
|
|
_ = ln2.Close()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
results, err := checker.CheckPorts(
|
|
context.Background(),
|
|
"127.0.0.1",
|
|
[]int{openPort, closedPort},
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if len(results) != 2 {
|
|
t.Fatalf(
|
|
"expected 2 results, got %d", len(results),
|
|
)
|
|
}
|
|
|
|
if !results[openPort].Open {
|
|
t.Error("expected open port to be open")
|
|
}
|
|
|
|
if results[closedPort].Open {
|
|
t.Error("expected closed port to be closed")
|
|
}
|
|
}
|
|
|
|
func TestCheckPortInvalidPorts(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
cases := []struct {
|
|
name string
|
|
port int
|
|
}{
|
|
{"zero", 0},
|
|
{"negative", -1},
|
|
{"too high", 65536},
|
|
{"very negative", -1000},
|
|
{"very high", 100000},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := checker.CheckPort(
|
|
context.Background(), "127.0.0.1", tc.port,
|
|
)
|
|
if err == nil {
|
|
t.Errorf(
|
|
"expected error for port %d, got nil",
|
|
tc.port,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckPortsInvalidPort(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
_, err := checker.CheckPorts(
|
|
context.Background(),
|
|
"127.0.0.1",
|
|
[]int{80, 0, 443},
|
|
)
|
|
if err == nil {
|
|
t.Error("expected error for invalid port in list")
|
|
}
|
|
}
|
|
|
|
func TestCheckPortLatencyReasonable(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ln, port := listenTCP(t)
|
|
|
|
defer func() { _ = ln.Close() }()
|
|
|
|
checker := portcheck.NewStandalone()
|
|
|
|
result, err := checker.CheckPort(
|
|
context.Background(), "127.0.0.1", port,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if result.Latency > time.Second {
|
|
t.Errorf(
|
|
"latency too high for localhost: %v",
|
|
result.Latency,
|
|
)
|
|
}
|
|
}
|