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