100 lines
3.4 KiB
Go
100 lines
3.4 KiB
Go
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")
|
|
}
|
|
}
|