1// Copyright 2017 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package acme 6 7import ( 8 "errors" 9 "net/http" 10 "reflect" 11 "testing" 12 "time" 13) 14 15func TestExternalAccountBindingString(t *testing.T) { 16 eab := ExternalAccountBinding{ 17 KID: "kid", 18 Key: []byte("key"), 19 } 20 got := eab.String() 21 want := `&{KID: "kid", Key: redacted}` 22 if got != want { 23 t.Errorf("eab.String() = %q, want: %q", got, want) 24 } 25} 26 27func TestRateLimit(t *testing.T) { 28 now := time.Date(2017, 04, 27, 10, 0, 0, 0, time.UTC) 29 f := timeNow 30 defer func() { timeNow = f }() 31 timeNow = func() time.Time { return now } 32 33 h120, hTime := http.Header{}, http.Header{} 34 h120.Set("Retry-After", "120") 35 hTime.Set("Retry-After", "Tue Apr 27 11:00:00 2017") 36 37 err1 := &Error{ 38 ProblemType: "urn:ietf:params:acme:error:nolimit", 39 Header: h120, 40 } 41 err2 := &Error{ 42 ProblemType: "urn:ietf:params:acme:error:rateLimited", 43 Header: h120, 44 } 45 err3 := &Error{ 46 ProblemType: "urn:ietf:params:acme:error:rateLimited", 47 Header: nil, 48 } 49 err4 := &Error{ 50 ProblemType: "urn:ietf:params:acme:error:rateLimited", 51 Header: hTime, 52 } 53 54 tt := []struct { 55 err error 56 res time.Duration 57 ok bool 58 }{ 59 {nil, 0, false}, 60 {errors.New("dummy"), 0, false}, 61 {err1, 0, false}, 62 {err2, 2 * time.Minute, true}, 63 {err3, 0, true}, 64 {err4, time.Hour, true}, 65 } 66 for i, test := range tt { 67 res, ok := RateLimit(test.err) 68 if ok != test.ok { 69 t.Errorf("%d: RateLimit(%+v): ok = %v; want %v", i, test.err, ok, test.ok) 70 continue 71 } 72 if res != test.res { 73 t.Errorf("%d: RateLimit(%+v) = %v; want %v", i, test.err, res, test.res) 74 } 75 } 76} 77 78func TestAuthorizationError(t *testing.T) { 79 tests := []struct { 80 desc string 81 err *AuthorizationError 82 msg string 83 }{ 84 { 85 desc: "when auth error identifier is set", 86 err: &AuthorizationError{ 87 Identifier: "domain.com", 88 Errors: []error{ 89 (&wireError{ 90 Status: 403, 91 Type: "urn:ietf:params:acme:error:caa", 92 Detail: "CAA record for domain.com prevents issuance", 93 }).error(nil), 94 }, 95 }, 96 msg: "acme: authorization error for domain.com: 403 urn:ietf:params:acme:error:caa: CAA record for domain.com prevents issuance", 97 }, 98 99 { 100 desc: "when auth error identifier is unset", 101 err: &AuthorizationError{ 102 Errors: []error{ 103 (&wireError{ 104 Status: 403, 105 Type: "urn:ietf:params:acme:error:caa", 106 Detail: "CAA record for domain.com prevents issuance", 107 }).error(nil), 108 }, 109 }, 110 msg: "acme: authorization error: 403 urn:ietf:params:acme:error:caa: CAA record for domain.com prevents issuance", 111 }, 112 } 113 114 for _, tt := range tests { 115 if tt.err.Error() != tt.msg { 116 t.Errorf("got: %s\nwant: %s", tt.err, tt.msg) 117 } 118 } 119} 120 121func TestSubproblems(t *testing.T) { 122 tests := []struct { 123 wire wireError 124 expectedOut Error 125 }{ 126 { 127 wire: wireError{ 128 Status: 1, 129 Type: "urn:error", 130 Detail: "it's an error", 131 }, 132 expectedOut: Error{ 133 StatusCode: 1, 134 ProblemType: "urn:error", 135 Detail: "it's an error", 136 }, 137 }, 138 { 139 wire: wireError{ 140 Status: 1, 141 Type: "urn:error", 142 Detail: "it's an error", 143 Subproblems: []Subproblem{ 144 { 145 Type: "urn:error:sub", 146 Detail: "it's a subproblem", 147 }, 148 }, 149 }, 150 expectedOut: Error{ 151 StatusCode: 1, 152 ProblemType: "urn:error", 153 Detail: "it's an error", 154 Subproblems: []Subproblem{ 155 { 156 Type: "urn:error:sub", 157 Detail: "it's a subproblem", 158 }, 159 }, 160 }, 161 }, 162 { 163 wire: wireError{ 164 Status: 1, 165 Type: "urn:error", 166 Detail: "it's an error", 167 Subproblems: []Subproblem{ 168 { 169 Type: "urn:error:sub", 170 Detail: "it's a subproblem", 171 Identifier: &AuthzID{Type: "dns", Value: "example"}, 172 }, 173 }, 174 }, 175 expectedOut: Error{ 176 StatusCode: 1, 177 ProblemType: "urn:error", 178 Detail: "it's an error", 179 Subproblems: []Subproblem{ 180 { 181 Type: "urn:error:sub", 182 Detail: "it's a subproblem", 183 Identifier: &AuthzID{Type: "dns", Value: "example"}, 184 }, 185 }, 186 }, 187 }, 188 } 189 190 for _, tc := range tests { 191 out := tc.wire.error(nil) 192 if !reflect.DeepEqual(*out, tc.expectedOut) { 193 t.Errorf("Unexpected error: wanted %v, got %v", tc.expectedOut, *out) 194 } 195 } 196} 197 198func TestErrorStringerWithSubproblems(t *testing.T) { 199 err := Error{ 200 StatusCode: 1, 201 ProblemType: "urn:error", 202 Detail: "it's an error", 203 Subproblems: []Subproblem{ 204 { 205 Type: "urn:error:sub", 206 Detail: "it's a subproblem", 207 }, 208 { 209 Type: "urn:error:sub", 210 Detail: "it's a subproblem", 211 Identifier: &AuthzID{Type: "dns", Value: "example"}, 212 }, 213 }, 214 } 215 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" 216 if err.Error() != expectedStr { 217 t.Errorf("Unexpected error string: wanted %q, got %q", expectedStr, err.Error()) 218 } 219} 220