An ACME Order object can contain an error field when it is invalid. Previously, OrderError.Error() ignored this field, making it difficult to diagnose why an order failed. This change includes the problem details if available. Change-Id: I461fd02ef02251a2ba1e8cea39b5df8070938117 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/772880 Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Sean Liao <sean@liao.dev> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
236 lines
5.3 KiB
Go
236 lines
5.3 KiB
Go
// Copyright 2017 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package acme
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestExternalAccountBindingString(t *testing.T) {
|
|
eab := ExternalAccountBinding{
|
|
KID: "kid",
|
|
Key: []byte("key"),
|
|
}
|
|
got := eab.String()
|
|
want := `&{KID: "kid", Key: redacted}`
|
|
if got != want {
|
|
t.Errorf("eab.String() = %q, want: %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestRateLimit(t *testing.T) {
|
|
now := time.Date(2017, 04, 27, 10, 0, 0, 0, time.UTC)
|
|
f := timeNow
|
|
defer func() { timeNow = f }()
|
|
timeNow = func() time.Time { return now }
|
|
|
|
h120, hTime := http.Header{}, http.Header{}
|
|
h120.Set("Retry-After", "120")
|
|
hTime.Set("Retry-After", "Tue Apr 27 11:00:00 2017")
|
|
|
|
err1 := &Error{
|
|
ProblemType: "urn:ietf:params:acme:error:nolimit",
|
|
Header: h120,
|
|
}
|
|
err2 := &Error{
|
|
ProblemType: "urn:ietf:params:acme:error:rateLimited",
|
|
Header: h120,
|
|
}
|
|
err3 := &Error{
|
|
ProblemType: "urn:ietf:params:acme:error:rateLimited",
|
|
Header: nil,
|
|
}
|
|
err4 := &Error{
|
|
ProblemType: "urn:ietf:params:acme:error:rateLimited",
|
|
Header: hTime,
|
|
}
|
|
|
|
tt := []struct {
|
|
err error
|
|
res time.Duration
|
|
ok bool
|
|
}{
|
|
{nil, 0, false},
|
|
{errors.New("dummy"), 0, false},
|
|
{err1, 0, false},
|
|
{err2, 2 * time.Minute, true},
|
|
{err3, 0, true},
|
|
{err4, time.Hour, true},
|
|
}
|
|
for i, test := range tt {
|
|
res, ok := RateLimit(test.err)
|
|
if ok != test.ok {
|
|
t.Errorf("%d: RateLimit(%+v): ok = %v; want %v", i, test.err, ok, test.ok)
|
|
continue
|
|
}
|
|
if res != test.res {
|
|
t.Errorf("%d: RateLimit(%+v) = %v; want %v", i, test.err, res, test.res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAuthorizationError(t *testing.T) {
|
|
tests := []struct {
|
|
desc string
|
|
err *AuthorizationError
|
|
msg string
|
|
}{
|
|
{
|
|
desc: "when auth error identifier is set",
|
|
err: &AuthorizationError{
|
|
Identifier: "domain.com",
|
|
Errors: []error{
|
|
(&wireError{
|
|
Status: 403,
|
|
Type: "urn:ietf:params:acme:error:caa",
|
|
Detail: "CAA record for domain.com prevents issuance",
|
|
}).error(nil),
|
|
},
|
|
},
|
|
msg: "acme: authorization error for domain.com: 403 urn:ietf:params:acme:error:caa: CAA record for domain.com prevents issuance",
|
|
},
|
|
|
|
{
|
|
desc: "when auth error identifier is unset",
|
|
err: &AuthorizationError{
|
|
Errors: []error{
|
|
(&wireError{
|
|
Status: 403,
|
|
Type: "urn:ietf:params:acme:error:caa",
|
|
Detail: "CAA record for domain.com prevents issuance",
|
|
}).error(nil),
|
|
},
|
|
},
|
|
msg: "acme: authorization error: 403 urn:ietf:params:acme:error:caa: CAA record for domain.com prevents issuance",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if tt.err.Error() != tt.msg {
|
|
t.Errorf("got: %s\nwant: %s", tt.err, tt.msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSubproblems(t *testing.T) {
|
|
tests := []struct {
|
|
wire wireError
|
|
expectedOut Error
|
|
}{
|
|
{
|
|
wire: wireError{
|
|
Status: 1,
|
|
Type: "urn:error",
|
|
Detail: "it's an error",
|
|
},
|
|
expectedOut: Error{
|
|
StatusCode: 1,
|
|
ProblemType: "urn:error",
|
|
Detail: "it's an error",
|
|
},
|
|
},
|
|
{
|
|
wire: wireError{
|
|
Status: 1,
|
|
Type: "urn:error",
|
|
Detail: "it's an error",
|
|
Subproblems: []Subproblem{
|
|
{
|
|
Type: "urn:error:sub",
|
|
Detail: "it's a subproblem",
|
|
},
|
|
},
|
|
},
|
|
expectedOut: Error{
|
|
StatusCode: 1,
|
|
ProblemType: "urn:error",
|
|
Detail: "it's an error",
|
|
Subproblems: []Subproblem{
|
|
{
|
|
Type: "urn:error:sub",
|
|
Detail: "it's a subproblem",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
wire: wireError{
|
|
Status: 1,
|
|
Type: "urn:error",
|
|
Detail: "it's an error",
|
|
Subproblems: []Subproblem{
|
|
{
|
|
Type: "urn:error:sub",
|
|
Detail: "it's a subproblem",
|
|
Identifier: &AuthzID{Type: "dns", Value: "example"},
|
|
},
|
|
},
|
|
},
|
|
expectedOut: Error{
|
|
StatusCode: 1,
|
|
ProblemType: "urn:error",
|
|
Detail: "it's an error",
|
|
Subproblems: []Subproblem{
|
|
{
|
|
Type: "urn:error:sub",
|
|
Detail: "it's a subproblem",
|
|
Identifier: &AuthzID{Type: "dns", Value: "example"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
out := tc.wire.error(nil)
|
|
if !reflect.DeepEqual(*out, tc.expectedOut) {
|
|
t.Errorf("Unexpected error: wanted %v, got %v", tc.expectedOut, *out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestErrorStringerWithSubproblems(t *testing.T) {
|
|
err := Error{
|
|
StatusCode: 1,
|
|
ProblemType: "urn:error",
|
|
Detail: "it's an error",
|
|
Subproblems: []Subproblem{
|
|
{
|
|
Type: "urn:error:sub",
|
|
Detail: "it's a subproblem",
|
|
},
|
|
{
|
|
Type: "urn:error:sub",
|
|
Detail: "it's a subproblem",
|
|
Identifier: &AuthzID{Type: "dns", Value: "example"},
|
|
},
|
|
},
|
|
}
|
|
expectedStr := "1 urn:error: it's an error; subproblems:\n\turn:error:sub: it's a subproblem\n\turn:error:sub: [dns: example] it's a subproblem"
|
|
if err.Error() != expectedStr {
|
|
t.Errorf("Unexpected error string: wanted %q, got %q", expectedStr, err.Error())
|
|
}
|
|
}
|
|
|
|
func TestOrderErrorProblem(t *testing.T) {
|
|
oe := &OrderError{
|
|
OrderURL: "url",
|
|
Status: "invalid",
|
|
Problem: &Error{
|
|
StatusCode: 400,
|
|
ProblemType: "type",
|
|
Detail: "detail",
|
|
},
|
|
}
|
|
want := "acme: order url status: invalid; problem: 400 type: detail"
|
|
if got := oe.Error(); got != want {
|
|
t.Errorf("oe.Error() = %q, want %q", got, want)
|
|
}
|
|
}
|