1472f7530b
Later we will try to write comprehensive integration tests for the whole netxlite package. We want just unit tests here.
282 lines
8.7 KiB
Go
282 lines
8.7 KiB
Go
package errorsx
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"errors"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/lucas-clemente/quic-go"
|
|
"github.com/pion/stun"
|
|
)
|
|
|
|
func TestClassifyGenericError(t *testing.T) {
|
|
// Please, keep this list sorted in the same order
|
|
// in which checks appear on the code
|
|
|
|
t.Run("for input being already an ErrWrapper", func(t *testing.T) {
|
|
err := &ErrWrapper{Failure: FailureEOFError}
|
|
if ClassifyGenericError(err) != FailureEOFError {
|
|
t.Fatal("did not classify existing ErrWrapper correctly")
|
|
}
|
|
})
|
|
|
|
t.Run("for a system call error", func(t *testing.T) {
|
|
if ClassifyGenericError(EWOULDBLOCK) != FailureOperationWouldBlock {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
// Now we enter into classifyWithStringSuffix. We test it here
|
|
// since we want to test the ClassifyGenericError in is
|
|
// entirety here and the classifyWithStringSuffix function
|
|
// is just an implementation detail.
|
|
|
|
t.Run("for operation was canceled", func(t *testing.T) {
|
|
if ClassifyGenericError(errors.New("operation was canceled")) != FailureInterrupted {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for EOF", func(t *testing.T) {
|
|
if ClassifyGenericError(io.EOF) != FailureEOFError {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for context deadline exceeded", func(t *testing.T) {
|
|
if ClassifyGenericError(context.DeadlineExceeded) != FailureGenericTimeoutError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for stun's transaction is timed out", func(t *testing.T) {
|
|
if ClassifyGenericError(stun.ErrTransactionTimeOut) != FailureGenericTimeoutError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for i/o timeout", func(t *testing.T) {
|
|
if ClassifyGenericError(errors.New("i/o timeout")) != FailureGenericTimeoutError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for TLS handshake timeout", func(t *testing.T) {
|
|
err := errors.New("net/http: TLS handshake timeout")
|
|
if ClassifyGenericError(err) != FailureGenericTimeoutError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for no such host", func(t *testing.T) {
|
|
if ClassifyGenericError(errors.New("no such host")) != FailureDNSNXDOMAINError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
// Now we're back in ClassifyGenericError
|
|
|
|
t.Run("for context.Canceled", func(t *testing.T) {
|
|
if ClassifyGenericError(context.Canceled) != FailureInterrupted {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for unknown errors", func(t *testing.T) {
|
|
t.Run("with an IPv4 address", func(t *testing.T) {
|
|
input := errors.New("read tcp 10.0.2.15:56948->93.184.216.34:443: use of closed network connection")
|
|
expected := "unknown_failure: read tcp [scrubbed]->[scrubbed]: use of closed network connection"
|
|
out := ClassifyGenericError(input)
|
|
if out != expected {
|
|
t.Fatal(cmp.Diff(expected, out))
|
|
}
|
|
})
|
|
|
|
t.Run("with an IPv6 address", func(t *testing.T) {
|
|
input := errors.New("read tcp [::1]:56948->[::1]:443: use of closed network connection")
|
|
expected := "unknown_failure: read tcp [scrubbed]->[scrubbed]: use of closed network connection"
|
|
out := ClassifyGenericError(input)
|
|
if out != expected {
|
|
t.Fatal(cmp.Diff(expected, out))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestClassifyQUICHandshakeError(t *testing.T) {
|
|
// Please, keep this list sorted in the same order
|
|
// in which checks appear on the code
|
|
|
|
t.Run("for input being already an ErrWrapper", func(t *testing.T) {
|
|
err := &ErrWrapper{Failure: FailureEOFError}
|
|
if ClassifyQUICHandshakeError(err) != FailureEOFError {
|
|
t.Fatal("did not classify existing ErrWrapper correctly")
|
|
}
|
|
})
|
|
|
|
t.Run("for incompatible quic version", func(t *testing.T) {
|
|
if ClassifyQUICHandshakeError(&quic.VersionNegotiationError{}) != FailureQUICIncompatibleVersion {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for stateless reset", func(t *testing.T) {
|
|
if ClassifyQUICHandshakeError(&quic.StatelessResetError{}) != FailureConnectionReset {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for handshake timeout", func(t *testing.T) {
|
|
if ClassifyQUICHandshakeError(&quic.HandshakeTimeoutError{}) != FailureGenericTimeoutError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for idle timeout", func(t *testing.T) {
|
|
if ClassifyQUICHandshakeError(&quic.IdleTimeoutError{}) != FailureGenericTimeoutError {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for connection refused", func(t *testing.T) {
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: quic.ConnectionRefused}) != FailureConnectionRefused {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for bad certificate", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertBadCertificate
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLInvalidCertificate {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for unsupported certificate", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertUnsupportedCertificate
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLInvalidCertificate {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for certificate expired", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertCertificateExpired
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLInvalidCertificate {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for certificate revoked", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertCertificateRevoked
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLInvalidCertificate {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for certificate unknown", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertCertificateUnknown
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLInvalidCertificate {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for decrypt error", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertDecryptError
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLFailedHandshake {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for handshake failure", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertHandshakeFailure
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLFailedHandshake {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for unknown CA", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSAlertUnknownCA
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLUnknownAuthority {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for unrecognized hostname", func(t *testing.T) {
|
|
var err quic.TransportErrorCode = quicTLSUnrecognizedName
|
|
if ClassifyQUICHandshakeError(&quic.TransportError{ErrorCode: err}) != FailureSSLInvalidHostname {
|
|
t.Fatal("unexpected results")
|
|
}
|
|
})
|
|
|
|
t.Run("for another kind of error", func(t *testing.T) {
|
|
if ClassifyQUICHandshakeError(io.EOF) != FailureEOFError {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestClassifyResolverError(t *testing.T) {
|
|
// Please, keep this list sorted in the same order
|
|
// in which checks appear on the code
|
|
|
|
t.Run("for input being already an ErrWrapper", func(t *testing.T) {
|
|
err := &ErrWrapper{Failure: FailureEOFError}
|
|
if ClassifyResolverError(err) != FailureEOFError {
|
|
t.Fatal("did not classify existing ErrWrapper correctly")
|
|
}
|
|
})
|
|
|
|
t.Run("for ErrDNSBogon", func(t *testing.T) {
|
|
if ClassifyResolverError(ErrDNSBogon) != FailureDNSBogonError {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for another kind of error", func(t *testing.T) {
|
|
if ClassifyResolverError(io.EOF) != FailureEOFError {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestClassifyTLSHandshakeError(t *testing.T) {
|
|
// Please, keep this list sorted in the same order
|
|
// in which checks appear on the code
|
|
|
|
t.Run("for input being already an ErrWrapper", func(t *testing.T) {
|
|
err := &ErrWrapper{Failure: FailureEOFError}
|
|
if ClassifyTLSHandshakeError(err) != FailureEOFError {
|
|
t.Fatal("did not classify existing ErrWrapper correctly")
|
|
}
|
|
})
|
|
|
|
t.Run("for x509.HostnameError", func(t *testing.T) {
|
|
var err x509.HostnameError
|
|
if ClassifyTLSHandshakeError(err) != FailureSSLInvalidHostname {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for x509.UnknownAuthorityError", func(t *testing.T) {
|
|
var err x509.UnknownAuthorityError
|
|
if ClassifyTLSHandshakeError(err) != FailureSSLUnknownAuthority {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for x509.CertificateInvalidError", func(t *testing.T) {
|
|
var err x509.CertificateInvalidError
|
|
if ClassifyTLSHandshakeError(err) != FailureSSLInvalidCertificate {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
|
|
t.Run("for another kind of error", func(t *testing.T) {
|
|
if ClassifyTLSHandshakeError(io.EOF) != FailureEOFError {
|
|
t.Fatal("unexpected result")
|
|
}
|
|
})
|
|
}
|