Files
pulse/internal/monitoring/availability_udp_test.go
T
2026-07-29 21:28:26 +01:00

107 lines
3.7 KiB
Go

package monitoring
import (
"context"
"net"
"testing"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
)
func udpTestTarget(address string, port int) config.AvailabilityTarget {
return config.NormalizeAvailabilityTarget(config.AvailabilityTarget{
ID: "udp-test", Name: "UDP test", TargetKind: config.AvailabilityTargetService,
Address: address, Protocol: config.AvailabilityProbeUDP, Port: port, Enabled: true,
TimeoutMillis: 250, UDPMode: config.AvailabilityUDPResponseRequired, UDPRequest: "PING",
})
}
func TestProbeAvailabilityTargetUDPResponseRequired(t *testing.T) {
listener, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
if err != nil {
t.Fatal(err)
}
defer listener.Close()
go func() {
buffer := make([]byte, 32)
_, remote, readErr := listener.ReadFromUDP(buffer)
if readErr == nil {
_, _ = listener.WriteToUDP([]byte("PONG"), remote)
}
}()
port := listener.LocalAddr().(*net.UDPAddr).Port
target := udpTestTarget("127.0.0.1", port)
target.UDPExpected = "PONG"
outcome, err := ProbeAvailabilityTargetResult(context.Background(), target)
if err != nil || outcome != AvailabilityProbeReachable {
t.Fatalf("UDP outcome = %q, error = %v", outcome, err)
}
}
func TestProbeAvailabilityTargetUDPOpenOrFilteredIsIndeterminate(t *testing.T) {
listener, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
if err != nil {
t.Fatal(err)
}
defer listener.Close()
go func() {
buffer := make([]byte, 32)
_, _, _ = listener.ReadFromUDP(buffer)
}()
port := listener.LocalAddr().(*net.UDPAddr).Port
target := udpTestTarget("127.0.0.1", port)
target.UDPMode = config.AvailabilityUDPOpenOrFiltered
target.UDPRequest = ""
started := time.Now()
outcome, err := ProbeAvailabilityTargetResult(context.Background(), target)
if err != nil || outcome != AvailabilityProbeIndeterminate {
t.Fatalf("UDP outcome = %q, error = %v", outcome, err)
}
if time.Since(started) < 200*time.Millisecond {
t.Fatalf("indeterminate result returned before the response deadline")
}
}
func TestAvailabilityUDPValidationRequiresPayloadInResponseMode(t *testing.T) {
target := udpTestTarget("127.0.0.1", 53)
target.UDPRequest = ""
if err := target.Validate(); err == nil {
t.Fatal("Validate() error = nil, want response-required payload error")
}
target.UDPMode = config.AvailabilityUDPOpenOrFiltered
if err := target.Validate(); err != nil {
t.Fatalf("open-or-filtered Validate() error = %v", err)
}
}
func TestAvailabilityUDPIndeterminateDoesNotClaimReachabilityOrAccumulateFailures(t *testing.T) {
target := udpTestTarget("127.0.0.1", 53)
target.UDPMode = config.AvailabilityUDPOpenOrFiltered
target.UDPRequest = ""
monitor := &Monitor{availabilityStatuses: map[string]AvailabilityProbeStatus{
target.ID: {TargetID: target.ID, ConsecutiveFailures: 2},
}}
checkedAt := time.Now().UTC()
monitor.setAvailabilityStatus(target, checkedAt, 250*time.Millisecond, AvailabilityProbeIndeterminate, nil, "", time.Time{})
status := monitor.AvailabilityStatusSnapshot()[target.ID]
if status.Available {
t.Fatal("indeterminate UDP result must not be reported as reachable")
}
if status.Outcome != string(AvailabilityProbeIndeterminate) {
t.Fatalf("outcome = %q, want indeterminate", status.Outcome)
}
if status.ConsecutiveFailures != 0 {
t.Fatalf("consecutive failures = %d, want 0", status.ConsecutiveFailures)
}
if got := availabilityResourceStatus(target, status); got != "warning" {
t.Fatalf("resource status = %q, want warning", got)
}
if incident := availabilityIncident(target, status, checkedAt); incident != nil {
t.Fatalf("indeterminate UDP result created an incident: %#v", incident)
}
}