github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/errors_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common_test 5 6 import ( 7 stderrors "errors" 8 "net/http" 9 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/txn" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 "gopkg.in/macaroon.v1" 16 17 "github.com/juju/juju/apiserver/common" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/core/leadership" 20 "github.com/juju/juju/core/lease" 21 "github.com/juju/juju/state" 22 "github.com/juju/juju/testing" 23 ) 24 25 type errorsSuite struct { 26 testing.BaseSuite 27 } 28 29 var _ = gc.Suite(&errorsSuite{}) 30 31 var errorTransformTests = []struct { 32 err error 33 code string 34 status int 35 helperFunc func(error) bool 36 }{{ 37 err: errors.NotFoundf("hello"), 38 code: params.CodeNotFound, 39 status: http.StatusNotFound, 40 helperFunc: params.IsCodeNotFound, 41 }, { 42 err: errors.UserNotFoundf("xxxx"), 43 code: params.CodeUserNotFound, 44 status: http.StatusNotFound, 45 helperFunc: params.IsCodeUserNotFound, 46 }, { 47 err: errors.Unauthorizedf("hello"), 48 code: params.CodeUnauthorized, 49 status: http.StatusUnauthorized, 50 helperFunc: params.IsCodeUnauthorized, 51 }, { 52 err: state.ErrCannotEnterScopeYet, 53 code: params.CodeCannotEnterScopeYet, 54 status: http.StatusInternalServerError, 55 helperFunc: params.IsCodeCannotEnterScopeYet, 56 }, { 57 err: state.ErrCannotEnterScope, 58 code: params.CodeCannotEnterScope, 59 status: http.StatusInternalServerError, 60 helperFunc: params.IsCodeCannotEnterScope, 61 }, { 62 err: state.ErrDead, 63 code: params.CodeDead, 64 status: http.StatusInternalServerError, 65 helperFunc: params.IsCodeDead, 66 }, { 67 err: txn.ErrExcessiveContention, 68 code: params.CodeExcessiveContention, 69 status: http.StatusInternalServerError, 70 helperFunc: params.IsCodeExcessiveContention, 71 }, { 72 err: state.ErrUnitHasSubordinates, 73 code: params.CodeUnitHasSubordinates, 74 status: http.StatusInternalServerError, 75 helperFunc: params.IsCodeUnitHasSubordinates, 76 }, { 77 err: common.ErrBadId, 78 code: params.CodeNotFound, 79 status: http.StatusNotFound, 80 helperFunc: params.IsCodeNotFound, 81 }, { 82 err: common.NoAddressSetError(names.NewUnitTag("mysql/0"), "public"), 83 code: params.CodeNoAddressSet, 84 status: http.StatusInternalServerError, 85 helperFunc: params.IsCodeNoAddressSet, 86 }, { 87 err: common.ErrBadCreds, 88 code: params.CodeUnauthorized, 89 status: http.StatusUnauthorized, 90 helperFunc: params.IsCodeUnauthorized, 91 }, { 92 err: common.ErrPerm, 93 code: params.CodeUnauthorized, 94 status: http.StatusUnauthorized, 95 helperFunc: params.IsCodeUnauthorized, 96 }, { 97 err: common.ErrNotLoggedIn, 98 code: params.CodeUnauthorized, 99 status: http.StatusUnauthorized, 100 helperFunc: params.IsCodeUnauthorized, 101 }, { 102 err: errors.NotProvisionedf("machine 0"), 103 code: params.CodeNotProvisioned, 104 status: http.StatusInternalServerError, 105 helperFunc: params.IsCodeNotProvisioned, 106 }, { 107 err: errors.AlreadyExistsf("blah"), 108 code: params.CodeAlreadyExists, 109 status: http.StatusInternalServerError, 110 helperFunc: params.IsCodeAlreadyExists, 111 }, { 112 err: common.ErrUnknownWatcher, 113 code: params.CodeNotFound, 114 status: http.StatusNotFound, 115 helperFunc: params.IsCodeNotFound, 116 }, { 117 err: errors.NotAssignedf("unit mysql/0"), 118 code: params.CodeNotAssigned, 119 status: http.StatusInternalServerError, 120 helperFunc: params.IsCodeNotAssigned, 121 }, { 122 err: common.ErrStoppedWatcher, 123 code: params.CodeStopped, 124 status: http.StatusInternalServerError, 125 helperFunc: params.IsCodeStopped, 126 }, { 127 err: &state.HasAssignedUnitsError{"42", []string{"a"}}, 128 code: params.CodeHasAssignedUnits, 129 status: http.StatusInternalServerError, 130 helperFunc: params.IsCodeHasAssignedUnits, 131 }, { 132 err: common.ErrTryAgain, 133 code: params.CodeTryAgain, 134 status: http.StatusInternalServerError, 135 helperFunc: params.IsCodeTryAgain, 136 }, { 137 err: leadership.ErrClaimDenied, 138 code: params.CodeLeadershipClaimDenied, 139 status: http.StatusInternalServerError, 140 helperFunc: params.IsCodeLeadershipClaimDenied, 141 }, { 142 err: lease.ErrClaimDenied, 143 code: params.CodeLeaseClaimDenied, 144 status: http.StatusInternalServerError, 145 helperFunc: params.IsCodeLeaseClaimDenied, 146 }, { 147 err: common.OperationBlockedError("test"), 148 code: params.CodeOperationBlocked, 149 status: http.StatusBadRequest, 150 helperFunc: params.IsCodeOperationBlocked, 151 }, { 152 err: errors.NotSupportedf("needed feature"), 153 code: params.CodeNotSupported, 154 status: http.StatusInternalServerError, 155 helperFunc: params.IsCodeNotSupported, 156 }, { 157 err: errors.BadRequestf("something"), 158 code: params.CodeBadRequest, 159 status: http.StatusBadRequest, 160 helperFunc: params.IsBadRequest, 161 }, { 162 err: errors.MethodNotAllowedf("something"), 163 code: params.CodeMethodNotAllowed, 164 status: http.StatusMethodNotAllowed, 165 helperFunc: params.IsMethodNotAllowed, 166 }, { 167 err: stderrors.New("an error"), 168 status: http.StatusInternalServerError, 169 code: "", 170 }, { 171 err: &common.DischargeRequiredError{ 172 Cause: errors.New("something"), 173 Macaroon: sampleMacaroon, 174 }, 175 status: http.StatusUnauthorized, 176 code: params.CodeDischargeRequired, 177 helperFunc: func(err error) bool { 178 err1, ok := err.(*params.Error) 179 if !ok || err1.Info == nil || err1.Info.Macaroon != sampleMacaroon { 180 return false 181 } 182 return true 183 }, 184 }, { 185 err: unhashableError{"foo"}, 186 status: http.StatusInternalServerError, 187 code: "", 188 }, { 189 err: common.UnknownModelError("dead-beef-123456"), 190 code: params.CodeModelNotFound, 191 status: http.StatusNotFound, 192 helperFunc: params.IsCodeModelNotFound, 193 }, { 194 err: nil, 195 code: "", 196 status: http.StatusOK, 197 }} 198 199 var sampleMacaroon = func() *macaroon.Macaroon { 200 m, err := macaroon.New([]byte("key"), "id", "loc") 201 if err != nil { 202 panic(err) 203 } 204 return m 205 }() 206 207 type unhashableError []string 208 209 func (err unhashableError) Error() string { 210 return err[0] 211 } 212 213 func (s *errorsSuite) TestErrorTransform(c *gc.C) { 214 for i, t := range errorTransformTests { 215 c.Logf("running test %d: %T{%q}", i, t.err, t.err) 216 err1, status := common.ServerErrorAndStatus(t.err) 217 218 // Sanity check that ServerError returns the same thing. 219 err2 := common.ServerError(t.err) 220 c.Assert(err2, gc.DeepEquals, err1) 221 c.Assert(status, gc.Equals, t.status) 222 223 if t.err == nil { 224 c.Assert(err1, gc.IsNil) 225 c.Assert(status, gc.Equals, http.StatusOK) 226 continue 227 } 228 c.Assert(err1.Message, gc.Equals, t.err.Error()) 229 c.Assert(err1.Code, gc.Equals, t.code) 230 if t.helperFunc != nil { 231 c.Assert(err1, jc.Satisfies, t.helperFunc) 232 } 233 234 // TODO(ericsnow) Remove this switch once the other error types are supported. 235 switch t.code { 236 case params.CodeHasAssignedUnits, 237 params.CodeNoAddressSet, 238 params.CodeUpgradeInProgress, 239 params.CodeMachineHasAttachedStorage, 240 params.CodeDischargeRequired, 241 params.CodeModelNotFound, 242 params.CodeRetry: 243 continue 244 case params.CodeOperationBlocked: 245 // ServerError doesn't actually have a case for this code. 246 continue 247 } 248 249 c.Logf(" checking restore (%#v)", err1) 250 restored := common.RestoreError(err1) 251 if t.err == nil { 252 c.Check(restored, jc.ErrorIsNil) 253 } else if t.code == "" { 254 c.Check(restored.Error(), gc.Equals, t.err.Error()) 255 } else { 256 // TODO(ericsnow) Use a stricter DeepEquals check. 257 c.Check(errors.Cause(restored), gc.FitsTypeOf, t.err) 258 c.Check(restored.Error(), gc.Equals, t.err.Error()) 259 } 260 } 261 } 262 263 func (s *errorsSuite) TestUnknownModel(c *gc.C) { 264 err := common.UnknownModelError("dead-beef") 265 c.Check(err, gc.ErrorMatches, `unknown model: "dead-beef"`) 266 } 267 268 func (s *errorsSuite) TestDestroyErr(c *gc.C) { 269 errs := []string{ 270 "error one", 271 "error two", 272 "error three", 273 } 274 ids := []string{ 275 "id1", 276 "id2", 277 "id3", 278 } 279 280 c.Assert(common.DestroyErr("entities", ids, nil), jc.ErrorIsNil) 281 282 err := common.DestroyErr("entities", ids, errs) 283 c.Assert(err, gc.ErrorMatches, "no entities were destroyed: error one; error two; error three") 284 285 err = common.DestroyErr("entities", ids, errs[1:]) 286 c.Assert(err, gc.ErrorMatches, "some entities were not destroyed: error two; error three") 287 }