github.com/letsencrypt/boulder@v0.20251208.0/web/probs_test.go (about) 1 package web 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 berrors "github.com/letsencrypt/boulder/errors" 9 "github.com/letsencrypt/boulder/identifier" 10 "github.com/letsencrypt/boulder/probs" 11 "github.com/letsencrypt/boulder/test" 12 ) 13 14 func TestProblemDetailsForError(t *testing.T) { 15 // errMsg is used as the msg argument for `ProblemDetailsForError` and is 16 // always returned in the problem detail. 17 const errMsg = "testError" 18 // detailMsg is used as the msg argument for the individual error types and is 19 // sometimes not present in the produced problem's detail. 20 const detailMsg = "testDetail" 21 // fullDetail is what we expect the problem detail to look like when it 22 // contains both the error message and the detail message 23 fullDetail := fmt.Sprintf("%s :: %s", errMsg, detailMsg) 24 testCases := []struct { 25 err error 26 statusCode int 27 problem probs.ProblemType 28 detail string 29 }{ 30 // boulder/errors error types 31 // Internal server errors expect just the `errMsg` in detail. 32 {berrors.InternalServerError(detailMsg), 500, probs.ServerInternalProblem, errMsg}, 33 // Other errors expect the full detail message 34 {berrors.MalformedError(detailMsg), 400, probs.MalformedProblem, fullDetail}, 35 {berrors.UnauthorizedError(detailMsg), 403, probs.UnauthorizedProblem, fullDetail}, 36 {berrors.NotFoundError(detailMsg), 404, probs.MalformedProblem, fullDetail}, 37 {berrors.RateLimitError(0, detailMsg), 429, probs.RateLimitedProblem, fullDetail + ": see https://letsencrypt.org/docs/rate-limits/"}, 38 {berrors.InvalidEmailError(detailMsg), 400, probs.InvalidContactProblem, fullDetail}, 39 {berrors.RejectedIdentifierError(detailMsg), 400, probs.RejectedIdentifierProblem, fullDetail}, 40 } 41 for _, c := range testCases { 42 p := ProblemDetailsForError(c.err, errMsg) 43 if p.HTTPStatus != c.statusCode { 44 t.Errorf("Incorrect status code for %s. Expected %d, got %d", reflect.TypeOf(c.err).Name(), c.statusCode, p.HTTPStatus) 45 } 46 if p.Type != c.problem { 47 t.Errorf("Expected problem urn %#v, got %#v", c.problem, p.Type) 48 } 49 if p.Detail != c.detail { 50 t.Errorf("Expected detailed message %q, got %q", c.detail, p.Detail) 51 } 52 } 53 } 54 55 func TestSubProblems(t *testing.T) { 56 topErr := (&berrors.BoulderError{ 57 Type: berrors.CAA, 58 Detail: "CAA policy forbids issuance", 59 }).WithSubErrors( 60 []berrors.SubBoulderError{ 61 { 62 Identifier: identifier.NewDNS("threeletter.agency"), 63 BoulderError: &berrors.BoulderError{ 64 Type: berrors.CAA, 65 Detail: "Forbidden by ■■■■■■■■■■■ and directive ■■■■", 66 }, 67 }, 68 { 69 Identifier: identifier.NewDNS("area51.threeletter.agency"), 70 BoulderError: &berrors.BoulderError{ 71 Type: berrors.NotFound, 72 Detail: "No Such Area...", 73 }, 74 }, 75 }) 76 77 prob := problemDetailsForBoulderError(topErr, "problem with subproblems") 78 test.AssertEquals(t, len(prob.SubProblems), len(topErr.SubErrors)) 79 80 subProbsMap := make(map[string]probs.SubProblemDetails, len(prob.SubProblems)) 81 82 for _, subProb := range prob.SubProblems { 83 subProbsMap[subProb.Identifier.Value] = subProb 84 } 85 86 subProbA, foundA := subProbsMap["threeletter.agency"] 87 subProbB, foundB := subProbsMap["area51.threeletter.agency"] 88 test.AssertEquals(t, foundA, true) 89 test.AssertEquals(t, foundB, true) 90 91 test.AssertEquals(t, subProbA.Type, probs.CAAProblem) 92 test.AssertEquals(t, subProbB.Type, probs.MalformedProblem) 93 }