github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/translatableerror/convert_to_translatable_error_test.go (about) 1 package translatableerror_test 2 3 import ( 4 "encoding/json" 5 "errors" 6 "time" 7 8 "github.com/LukasHeimann/cloudfoundrycli/v8/actor/actionerror" 9 "github.com/LukasHeimann/cloudfoundrycli/v8/api/cloudcontroller/ccerror" 10 "github.com/LukasHeimann/cloudfoundrycli/v8/api/plugin/pluginerror" 11 "github.com/LukasHeimann/cloudfoundrycli/v8/api/uaa" 12 . "github.com/LukasHeimann/cloudfoundrycli/v8/command/translatableerror" 13 "github.com/LukasHeimann/cloudfoundrycli/v8/util/clissh/ssherror" 14 "github.com/LukasHeimann/cloudfoundrycli/v8/util/download" 15 "github.com/LukasHeimann/cloudfoundrycli/v8/util/manifest" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/ginkgo/extensions/table" 18 . "github.com/onsi/gomega" 19 ) 20 21 var _ = Describe("ConvertToTranslatableError", func() { 22 err := errors.New("some-error") 23 genericErr := errors.New("some-generic-error") 24 jsonErr := new(json.SyntaxError) 25 unprocessableEntityError := ccerror.UnprocessableEntityError{Message: "another message"} 26 27 DescribeTable("error translations", 28 func(passedInErr error, expectedErr error) { 29 actualErr := ConvertToTranslatableError(passedInErr) 30 Expect(actualErr).To(MatchError(expectedErr)) 31 }, 32 33 // Action Errors 34 Entry("actionerror.AddPluginRepositoryError -> AddPluginRepositoryError", 35 actionerror.AddPluginRepositoryError{Name: "some-repo", URL: "some-URL", Message: "404"}, 36 AddPluginRepositoryError{Name: "some-repo", URL: "some-URL", Message: "404"}), 37 38 Entry("actionerror.ApplicationNotFoundError -> ApplicationNotFoundError", 39 actionerror.ApplicationNotFoundError{Name: "some-app"}, 40 ApplicationNotFoundError{Name: "some-app"}), 41 42 Entry("actionerror.ApplicationNotStartedError -> ApplicationNotStartedError", 43 actionerror.ApplicationNotStartedError{Name: "some-app"}, 44 ApplicationNotStartedError{Name: "some-app"}), 45 46 Entry("actionerror.AppNotFoundInManifestError -> AppNotFoundInManifestError", 47 actionerror.AppNotFoundInManifestError{Name: "some-app"}, 48 AppNotFoundInManifestError{Name: "some-app"}), 49 50 Entry("actionerror.AssignDropletError -> AssignDropletError", 51 actionerror.AssignDropletError{Message: "some-message"}, 52 AssignDropletError{Message: "some-message"}), 53 54 Entry("actionerror.ServicePlanNotFoundError -> ServicePlanNotFoundError", 55 actionerror.ServicePlanNotFoundError{PlanName: "some-plan", OfferingName: "some-service"}, 56 ServicePlanNotFoundError{PlanName: "some-plan", OfferingName: "some-service"}), 57 58 Entry("actionerror.BuildpackNotFoundError -> BuildpackNotFoundError", 59 actionerror.BuildpackNotFoundError{}, 60 BuildpackNotFoundError{}), 61 62 Entry("actionerror.BuildpackStackChangeError-> BuildpackStackChangeError", 63 actionerror.BuildpackStackChangeError{}, 64 BuildpackStackChangeError{}), 65 66 Entry("actionerror.CommandLineOptionsWithMultipleAppsError -> CommandLineArgsWithMultipleAppsError", 67 actionerror.CommandLineOptionsWithMultipleAppsError{}, 68 CommandLineArgsWithMultipleAppsError{}), 69 70 Entry("actionerror.DockerPasswordNotSetError -> DockerPasswordNotSetError", 71 actionerror.DockerPasswordNotSetError{}, 72 DockerPasswordNotSetError{}), 73 74 Entry("actionerror.DomainNotFoundError -> DomainNotFoundError", 75 actionerror.DomainNotFoundError{Name: "some-domain-name", GUID: "some-domain-guid"}, 76 DomainNotFoundError{Name: "some-domain-name", GUID: "some-domain-guid"}), 77 78 Entry("actionerror.EmptyBuildpacksError -> EmptyBuildpacksError", 79 manifest.EmptyBuildpacksError{}, 80 EmptyBuildpacksError{}, 81 ), 82 83 Entry("actionerror.EmptyArchiveError -> EmptyDirectoryError", 84 actionerror.EmptyArchiveError{Path: "some-filename"}, 85 EmptyDirectoryError{Path: "some-filename"}), 86 87 Entry("actionerror.EmptyDirectoryError -> EmptyDirectoryError", 88 actionerror.EmptyDirectoryError{Path: "some-filename"}, 89 EmptyDirectoryError{Path: "some-filename"}), 90 91 Entry("actionerror.EmptyBuildpackDirectoryError -> EmptyBuildpackDirectoryError", 92 actionerror.EmptyBuildpackDirectoryError{Path: "some-path"}, 93 EmptyBuildpackDirectoryError{Path: "some-path"}), 94 95 Entry("actionerror.FileChangedError -> FileChangedError", 96 actionerror.FileChangedError{Filename: "some-filename"}, 97 FileChangedError{Filename: "some-filename"}), 98 99 Entry("actionerror.GettingPluginRepositoryError -> GettingPluginRepositoryError", 100 actionerror.GettingPluginRepositoryError{Name: "some-repo", Message: "404"}, 101 GettingPluginRepositoryError{Name: "some-repo", Message: "404"}), 102 103 Entry("actionerror.HostnameWithTCPDomainError -> HostnameWithTCPDomainError", 104 actionerror.HostnameWithTCPDomainError{}, 105 HostnameWithTCPDomainError{}), 106 107 Entry("actionerror.HTTPHealthCheckInvalidError -> HTTPHealthCheckInvalidError", 108 actionerror.HTTPHealthCheckInvalidError{}, 109 HTTPHealthCheckInvalidError{}), 110 111 Entry("actionerror.InvalidBuildpacksError -> InvalidBuildpacksError", 112 actionerror.InvalidBuildpacksError{}, 113 InvalidBuildpacksError{}), 114 115 Entry("actionerror.InvalidHTTPRouteSettings -> PortNotAllowedWithHTTPDomainError", 116 actionerror.InvalidHTTPRouteSettings{Domain: "some-domain"}, 117 PortNotAllowedWithHTTPDomainError{Domain: "some-domain"}), 118 119 Entry("actionerror.InvalidRouteError -> InvalidRouteError", 120 actionerror.InvalidRouteError{Route: "some-invalid-route"}, 121 InvalidRouteError{Route: "some-invalid-route"}), 122 123 Entry("actionerror.InvalidTCPRouteSettings -> HostAndPathNotAllowedWithTCPDomainError", 124 actionerror.InvalidTCPRouteSettings{Domain: "some-domain"}, 125 HostAndPathNotAllowedWithTCPDomainError{Domain: "some-domain"}), 126 127 Entry("actionerror.MissingNameError -> AppNameOrManifestRequiredError", 128 actionerror.MissingNameError{}, 129 AppNameOrManifestRequiredError{}), 130 131 Entry("actionerror.MultipleBuildpacksFoundError -> MultipleBuildpacksFoundError", 132 actionerror.MultipleBuildpacksFoundError{BuildpackName: "some-bp-name"}, 133 MultipleBuildpacksFoundError{BuildpackName: "some-bp-name"}), 134 135 Entry("actionerror.NoCompatibleBinaryError -> NoCompatibleBinaryError", 136 actionerror.NoCompatibleBinaryError{}, 137 NoCompatibleBinaryError{}), 138 139 Entry("actionerror.NoDomainsFoundError -> NoDomainsFoundError", 140 actionerror.NoDomainsFoundError{OrganizationGUID: "some-guid"}, 141 NoDomainsFoundError{}), 142 143 Entry("actionerror.NoHostnameAndSharedDomainError -> NoHostnameAndSharedDomainError", 144 actionerror.NoHostnameAndSharedDomainError{}, 145 NoHostnameAndSharedDomainError{}), 146 147 Entry("actionerror.NoMatchingDomainError -> NoMatchingDomainError", 148 actionerror.NoMatchingDomainError{Route: "some-route.com"}, 149 NoMatchingDomainError{Route: "some-route.com"}), 150 151 Entry("actionerror.NonexistentAppPathError -> FileNotFoundError", 152 actionerror.NonexistentAppPathError{Path: "some-path"}, 153 FileNotFoundError{Path: "some-path"}), 154 155 Entry("actionerror.NoOrganizationTargetedError -> NoOrganizationTargetedError", 156 actionerror.NoOrganizationTargetedError{BinaryName: "faceman"}, 157 NoOrganizationTargetedError{BinaryName: "faceman"}), 158 159 Entry("actionerror.NoSpaceTargetedError -> NoSpaceTargetedError", 160 actionerror.NoSpaceTargetedError{BinaryName: "faceman"}, 161 NoSpaceTargetedError{BinaryName: "faceman"}), 162 163 Entry("actionerror.NotLoggedInError -> NotLoggedInError", 164 actionerror.NotLoggedInError{BinaryName: "faceman"}, 165 NotLoggedInError{BinaryName: "faceman"}), 166 167 Entry("actionerror.OrganizationNotFoundError -> OrgNotFoundError", 168 actionerror.OrganizationNotFoundError{Name: "some-org"}, 169 OrganizationNotFoundError{Name: "some-org"}), 170 171 Entry("actionerror.QuotaNotFoundForNameError -> QuotaNotFoundForNameError", 172 actionerror.QuotaNotFoundForNameError{Name: "some-quota"}, 173 QuotaNotFoundForNameError{Name: "some-quota"}), 174 175 Entry("actionerror.PasswordGrantTypeLogoutRequiredError -> PasswordGrantTypeLogoutRequiredError", 176 actionerror.PasswordGrantTypeLogoutRequiredError{}, 177 PasswordGrantTypeLogoutRequiredError{}), 178 179 Entry("actionerror.PluginCommandConflictError -> PluginCommandConflictError", 180 actionerror.PluginCommandsConflictError{ 181 PluginName: "some-plugin", 182 PluginVersion: "1.1.1", 183 CommandNames: []string{"some-command", "some-other-command"}, 184 CommandAliases: []string{"sc", "soc"}, 185 }, 186 PluginCommandsConflictError{ 187 PluginName: "some-plugin", 188 PluginVersion: "1.1.1", 189 CommandNames: []string{"some-command", "some-other-command"}, 190 CommandAliases: []string{"sc", "soc"}, 191 }), 192 193 Entry("actionerror.PluginInvalidError -> PluginInvalidError", 194 actionerror.PluginInvalidError{}, 195 PluginInvalidError{}), 196 197 Entry("actionerror.PluginInvalidError -> PluginInvalidError", 198 actionerror.PluginInvalidError{Err: genericErr}, 199 PluginInvalidError{Err: genericErr}), 200 201 Entry("actionerror.PluginNotFoundError -> PluginNotFoundError", 202 actionerror.PluginNotFoundError{PluginName: "some-plugin"}, 203 PluginNotFoundError{PluginName: "some-plugin"}), 204 205 Entry("actionerror.ProcessInstanceNotFoundError -> ProcessInstanceNotFoundError", 206 actionerror.ProcessInstanceNotFoundError{ProcessType: "some-process-type", InstanceIndex: 42}, 207 ProcessInstanceNotFoundError{ProcessType: "some-process-type", InstanceIndex: 42}), 208 209 Entry("actionerror.ProcessInstanceNotRunningError -> ProcessInstanceNotRunningError", 210 actionerror.ProcessInstanceNotRunningError{ProcessType: "some-process-type", InstanceIndex: 42}, 211 ProcessInstanceNotRunningError{ProcessType: "some-process-type", InstanceIndex: 42}), 212 213 Entry("actionerror.ProcessNotFoundError -> ProcessNotFoundError", 214 actionerror.ProcessNotFoundError{ProcessType: "some-process-type"}, 215 ProcessNotFoundError{ProcessType: "some-process-type"}), 216 217 Entry("actionerror.PropertyCombinationError -> PropertyCombinationError", 218 actionerror.PropertyCombinationError{Properties: []string{"property-1", "property-2"}}, 219 PropertyCombinationError{Properties: []string{"property-1", "property-2"}}), 220 221 Entry("actionerror.RepositoryNameTakenError -> RepositoryNameTakenError", 222 actionerror.RepositoryNameTakenError{Name: "some-repo"}, 223 RepositoryNameTakenError{Name: "some-repo"}), 224 225 Entry("actionerror.RepositoryNotRegisteredError -> RepositoryNotRegisteredError", 226 actionerror.RepositoryNotRegisteredError{Name: "some-repo"}, 227 RepositoryNotRegisteredError{Name: "some-repo"}), 228 229 Entry("actionerror.RevisionNotFoundErrors-> RevisionNotFoundError", 230 actionerror.RevisionNotFoundError{Version: 1}, 231 RevisionNotFoundError{Version: 1}), 232 233 Entry("actionerror.RevisionAmbiguousError-> RevisionAmbiguousError", 234 actionerror.RevisionAmbiguousError{Version: 1}, 235 RevisionAmbiguousError{Version: 1}), 236 237 Entry("actionerror.RouteInDifferentSpaceError -> RouteInDifferentSpaceError", 238 actionerror.RouteInDifferentSpaceError{Route: "some-route"}, 239 RouteInDifferentSpaceError{Route: "some-route"}), 240 241 Entry("actionerror.RoutePathWithTCPDomainError -> RoutePathWithTCPDomainError", 242 actionerror.RoutePathWithTCPDomainError{}, 243 RoutePathWithTCPDomainError{}), 244 245 Entry("actionerror.RouterGroupNotFoundError -> RouterGroupNotFoundError", 246 actionerror.RouterGroupNotFoundError{Name: "some-group"}, 247 RouterGroupNotFoundError{Name: "some-group"}, 248 ), 249 250 Entry("actionerror.SecurityGroupNotFoundError -> SecurityGroupNotFoundError", 251 actionerror.SecurityGroupNotFoundError{Name: "some-security-group"}, 252 SecurityGroupNotFoundError{Name: "some-security-group"}), 253 254 Entry("actionerror.ServiceInstanceNotFoundError -> ServiceInstanceNotFoundError", 255 actionerror.ServiceInstanceNotFoundError{Name: "some-service-instance"}, 256 ServiceInstanceNotFoundError{Name: "some-service-instance"}), 257 258 Entry("actionerror.ServiceInstanceNotShareableError -> ServiceInstanceNotShareableError", 259 actionerror.ServiceInstanceNotShareableError{ 260 FeatureFlagEnabled: true, 261 ServiceBrokerSharingEnabled: false}, 262 ServiceInstanceNotShareableError{ 263 FeatureFlagEnabled: true, 264 ServiceBrokerSharingEnabled: false}), 265 266 Entry("actionerror.SharedServiceInstanceNotFoundError -> SharedServiceInstanceNotFoundError", 267 actionerror.SharedServiceInstanceNotFoundError{}, 268 SharedServiceInstanceNotFoundError{}), 269 270 Entry("actionerror.SpaceNotFoundError -> SpaceNotFoundError", 271 actionerror.SpaceNotFoundError{Name: "some-space"}, 272 SpaceNotFoundError{Name: "some-space"}), 273 274 Entry("actionerror.StackNotFoundError -> StackNotFoundError", 275 actionerror.StackNotFoundError{Name: "some-stack-name", GUID: "some-stack-guid"}, 276 StackNotFoundError{Name: "some-stack-name", GUID: "some-stack-guid"}), 277 278 Entry("actionerror.TaskWorkersUnavailableError -> RunTaskError", 279 actionerror.TaskWorkersUnavailableError{Message: "fooo: Banana Pants"}, 280 RunTaskError{Message: "Task workers are unavailable."}), 281 282 Entry("actionerror.TCPRouteOptionsNotProvidedError-> TCPRouteOptionsNotProvidedError", 283 actionerror.TCPRouteOptionsNotProvidedError{}, 284 TCPRouteOptionsNotProvidedError{}), 285 286 Entry("actionerror.TriggerLegacyPushError -> TriggerLegacyPushError", 287 actionerror.TriggerLegacyPushError{DomainHostRelated: []string{"domain", "host"}}, 288 TriggerLegacyPushError{DomainHostRelated: []string{"domain", "host"}}), 289 290 Entry("actionerror.UploadFailedError -> UploadFailedError", 291 actionerror.UploadFailedError{Err: actionerror.NoDomainsFoundError{}}, 292 UploadFailedError{Err: NoDomainsFoundError{}}), 293 294 Entry("v3action.StagingTimeoutError -> StagingTimeoutError", 295 actionerror.StagingTimeoutError{AppName: "some-app", Timeout: time.Nanosecond}, 296 StagingTimeoutError{AppName: "some-app", Timeout: time.Nanosecond}), 297 298 Entry("actionerror.CommandLineOptionsAndManifestConflictError -> CommandLineOptionsAndManifestConflictError", 299 actionerror.CommandLineOptionsAndManifestConflictError{ 300 ManifestAttribute: "some-attribute", 301 CommandLineOptions: []string{"option-1", "option-2"}, 302 }, 303 CommandLineOptionsAndManifestConflictError{ 304 ManifestAttribute: "some-attribute", 305 CommandLineOptions: []string{"option-1", "option-2"}, 306 }), 307 308 Entry("actionerror.ServiceInstanceNotSharedToSpaceError -> ServiceInstanceNotSharedToSpaceError", 309 actionerror.ServiceInstanceNotSharedToSpaceError{ServiceInstanceName: "some-service-instance-name"}, 310 ServiceInstanceNotSharedToSpaceError{ServiceInstanceName: "some-service-instance-name"}), 311 312 Entry("TipDecoratorError calls translates error on base error", 313 TipDecoratorError{BaseError: ccerror.APINotFoundError{URL: "some-url"}}, 314 TipDecoratorError{BaseError: APINotFoundError{URL: "some-url"}}), 315 316 // CC Errors 317 Entry("ccerror.APINotFoundError -> APINotFoundError", 318 ccerror.APINotFoundError{URL: "some-url"}, 319 APINotFoundError{URL: "some-url"}), 320 321 Entry("ccerror.V2JobFailedError -> V2JobFailedError", 322 ccerror.V2JobFailedError{JobGUID: "some-job-guid", Message: "some-message"}, 323 JobFailedError{JobGUID: "some-job-guid", Message: "some-message"}), 324 325 Entry("ccerror.V3JobFailedError -> V3JobFailedError", 326 ccerror.V3JobFailedError{JobGUID: "some-job-guid", Detail: "some-detail"}, 327 JobFailedError{JobGUID: "some-job-guid", Message: "some-detail"}), 328 329 Entry("ccerror.JobTimeoutError -> JobTimeoutError", 330 ccerror.JobTimeoutError{JobGUID: "some-job-guid"}, 331 JobTimeoutError{JobGUID: "some-job-guid"}), 332 333 Entry("ccerror.JobFailedNoErrorError -> JobFailedNoErrorError", 334 ccerror.JobFailedNoErrorError{JobGUID: "some-job-guid"}, 335 JobFailedNoErrorError{JobGUID: "some-job-guid"}), 336 337 Entry("ccerror.MultiError -> MultiError", 338 ccerror.MultiError{ResponseCode: 418, Errors: []ccerror.V3Error{ 339 { 340 Code: 282010, 341 Detail: "detail 1", 342 Title: "title-1", 343 }, 344 { 345 Code: 10242013, 346 Detail: "detail 2", 347 Title: "title-2", 348 }, 349 }}, 350 MultiError{Messages: []string{"detail 1", "detail 2"}}, 351 ), 352 353 Entry("ccerror.RequestError -> APIRequestError", 354 ccerror.RequestError{Err: err}, 355 APIRequestError{Err: err}), 356 357 Entry("ccerror.SSLValidationHostnameError -> SSLCertErrorError", 358 ccerror.SSLValidationHostnameError{Message: "some-message"}, 359 SSLCertError{Message: "some-message"}), 360 361 Entry("ccerror.UnverifiedServerError -> InvalidSSLCertError", 362 ccerror.UnverifiedServerError{URL: "some-url"}, 363 InvalidSSLCertError{URL: "some-url", SuggestedCommand: "api"}), 364 365 Entry("ccerror.UnprocessableEntityError with droplet message -> RunTaskError", 366 ccerror.UnprocessableEntityError{Message: "The request is semantically invalid: Task must have a droplet. Specify droplet or assign current droplet to app."}, 367 RunTaskError{Message: "App is not staged."}), 368 369 // This changed in CF254 370 Entry("ccerror.UnprocessableEntityError with droplet message -> RunTaskError", 371 ccerror.UnprocessableEntityError{Message: "Task must have a droplet. Specify droplet or assign current droplet to app."}, 372 RunTaskError{Message: "App is not staged."}), 373 374 Entry("ccerror.UnprocessableEntityError without droplet message -> original error", 375 unprocessableEntityError, 376 unprocessableEntityError), 377 378 Entry("download.RawHTTPStatusError -> HTTPStatusError", 379 download.RawHTTPStatusError{Status: "some status"}, 380 HTTPStatusError{Status: "some status"}, 381 ), 382 383 Entry("json.SyntaxError -> JSONSyntaxError", 384 jsonErr, 385 JSONSyntaxError{Err: jsonErr}, 386 ), 387 388 // Manifest Errors 389 Entry("manifest.ManifestCreationError -> ManifestCreationError", 390 manifest.ManifestCreationError{Err: errors.New("some-error")}, 391 FileCreationError{Err: errors.New("some-error")}), 392 393 Entry("manifest.InheritanceFieldError -> TriggerLegacyPushError", 394 manifest.InheritanceFieldError{}, 395 TriggerLegacyPushError{InheritanceRelated: true}), 396 397 Entry("manifest.GlobalFieldError -> TriggerLegacyPushError", 398 manifest.GlobalFieldsError{Fields: []string{"some-field"}}, 399 TriggerLegacyPushError{GlobalRelated: []string{"some-field"}}), 400 401 Entry("manifest.InterpolationError -> InterpolationError", 402 manifest.InterpolationError{Err: errors.New("an-error")}, 403 InterpolationError{Err: errors.New("an-error")}), 404 405 // Plugin Errors 406 Entry("pluginerror.RawHTTPStatusError -> DownloadPluginHTTPError", 407 pluginerror.RawHTTPStatusError{Status: "some status"}, 408 DownloadPluginHTTPError{Message: "some status"}, 409 ), 410 Entry("pluginerror.SSLValidationHostnameError -> DownloadPluginHTTPError", 411 pluginerror.SSLValidationHostnameError{Message: "some message"}, 412 DownloadPluginHTTPError{Message: "some message"}, 413 ), 414 Entry("pluginerror.UnverifiedServerError -> DownloadPluginHTTPError", 415 pluginerror.UnverifiedServerError{URL: "some URL"}, 416 DownloadPluginHTTPError{Message: "x509: certificate signed by unknown authority"}, 417 ), 418 419 // SSH Error 420 Entry("ssherror.UnableToAuthenticateError -> UnableToAuthenticateError", 421 ssherror.UnableToAuthenticateError{}, 422 SSHUnableToAuthenticateError{}), 423 424 // UAA Errors 425 Entry("uaa.BadCredentialsError -> BadCredentialsError", 426 uaa.UnauthorizedError{Message: "some message"}, 427 UnauthorizedError{Message: "some message"}), 428 429 Entry("uaa.AccountLockedError -> AccountLockedError", 430 uaa.AccountLockedError{Message: "locked out"}, 431 AccountLockedError{Message: "locked out"}), 432 433 Entry("uaa.InsufficientScopeError -> UnauthorizedToPerformActionError", 434 uaa.InsufficientScopeError{}, 435 UnauthorizedToPerformActionError{}), 436 437 Entry("uaa.InvalidAuthTokenError -> InvalidRefreshTokenError", 438 uaa.InvalidAuthTokenError{}, 439 InvalidRefreshTokenError{}), 440 441 Entry("default case -> original error", 442 err, 443 err), 444 ) 445 446 It("returns nil for a nil error", func() { 447 nilErr := ConvertToTranslatableError(nil) 448 Expect(nilErr).To(BeNil()) 449 }) 450 })