package main import ( "crypto/tls" "crypto/x509" "encoding/json" "errors" "fmt" "net" "net/http" "strings" "syscall" "testing" ) func TestEnvironmentRejectsMissingConfigurationWithoutNetworking(t *testing.T) { t.Setenv("DISPATCHER_ID", "") t.Setenv("AGENT_ID", "") for _, role := range []string{"agent", "dispatcher", "unknown"} { got := environmentCheck(role) if got.Success || got.MQConnected || got.ErrorClass == "" { t.Fatalf("unexpected result: %+v", got) } } } func TestTransportFailureDistinguishesCertificateAndAvailability(t *testing.T) { for _, test := range []struct { err error want string }{ {fmt.Errorf("wrapped: %w", x509.UnknownAuthorityError{}), "tls_failed"}, {&tls.CertificateVerificationError{Err: x509.HostnameError{}}, "tls_failed"}, {&net.OpError{Op: "dial", Err: syscall.ECONNREFUSED}, "unavailable"}, {&net.DNSError{IsTimeout: true}, "unavailable"}, {errors.New("unclassified transport issue"), "transport_failed"}, } { if got := transportFailure(test.err); got != test.want { t.Fatalf("got %s want %s", got, test.want) } } } type probeRoundTripFunc func(*http.Request) (*http.Response, error) func (f probeRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } func TestTransportReportTracksOnlyLatestRequest(t *testing.T) { original := http.DefaultTransport t.Cleanup(func() { http.DefaultTransport = original }) for _, tc := range []struct { name string response *http.Response err error status int failure string }{ {name: "success clears earlier failure", response: &http.Response{StatusCode: 200}, status: 200}, {name: "HTTP failure clears earlier transport failure", response: &http.Response{StatusCode: 503}, status: 503}, {name: "connection refusal clears earlier HTTP success", err: &net.OpError{Op: "dial", Err: syscall.ECONNREFUSED}, failure: "unavailable"}, {name: "timeout clears earlier HTTP success", err: &net.DNSError{IsTimeout: true}, failure: "unavailable"}, {name: "certificate failure is not availability", err: x509.UnknownAuthorityError{}, failure: "tls_failed"}, {name: "unknown failure remains fatal", err: errors.New("synthetic-sensitive-error"), failure: "transport_failed"}, } { t.Run(tc.name, func(t *testing.T) { http.DefaultTransport = probeRoundTripFunc(func(*http.Request) (*http.Response, error) { return tc.response, tc.err }) r := report{Phase: "tasks", HTTPStatus: 200, TransportFailure: "tls_failed", Trunks: 3, Providers: 1} req, err := http.NewRequest(http.MethodGet, "http://diagnostic.invalid/tasks", nil) if err != nil { t.Fatal(err) } response, err := (transport{&r}).RoundTrip(req) if response != tc.response || err != tc.err { t.Fatal("diagnostic changed the actual transport outcome") } if r.HTTPStatus != tc.status || r.TransportFailure != tc.failure { t.Fatalf("status=%d failure=%q, want status=%d failure=%q", r.HTTPStatus, r.TransportFailure, tc.status, tc.failure) } if r.Phase != "tasks" || r.Trunks != 3 || r.Providers != 1 { t.Fatal("diagnostic discarded earlier independent verification facts") } }) } } func TestDiagnosticNeverSerializesErrorContents(t *testing.T) { r := report{Phase: "sip"} rejected(&r, errors.New("synthetic-sensitive-value")) encoded, err := json.Marshal(r) if err != nil { t.Fatal(err) } if strings.Contains(string(encoded), "synthetic-sensitive-value") { t.Fatal("diagnostic leaked error content") } }