// Copyright IBM Corp. 2021, 2026 // SPDX-License-Identifier: MPL-2.0 package errors import ( "fmt" "testing" "net/http" pb "repo.anhonesthost.net/CyberCoveLLC/threshold/sdk/pbs/plugin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" ) func Test_ParseAWSError(t *testing.T) { t.Parallel() cases := []struct { name string err error expectedStatusCode codes.Code expectedStatusMsg string expectedPermission *pb.Permission }{ { name: "nil-error", expectedStatusCode: codes.OK, }, { name: "default-to-unknwon", err: fmt.Errorf("what can this error be?"), expectedStatusCode: codes.Unknown, expectedStatusMsg: "aws service unknown: unknown error: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-bad-request", err: TestAwsHttpResponseError(http.StatusBadRequest, "bad request"), expectedStatusCode: codes.InvalidArgument, expectedStatusMsg: "aws service unknown: bad request: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-unauthorized", err: TestAwsHttpResponseError(http.StatusUnauthorized, "unauthorized request"), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "unauthorized request", CheckedAt: timestamppb.Now(), }, }, { name: "http-status-code-forbidden", err: TestAwsHttpResponseError(http.StatusForbidden, "forbidden request"), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "forbidden request", CheckedAt: timestamppb.Now(), }, }, { name: "http-status-code-not-found", err: TestAwsHttpResponseError(http.StatusNotFound, "not found request"), expectedStatusCode: codes.NotFound, expectedStatusMsg: "aws service unknown: resource not found: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-too-many-requests", err: TestAwsHttpResponseError(http.StatusTooManyRequests, "too many requests"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-request-timeout", err: TestAwsHttpResponseError(http.StatusRequestTimeout, "request timeout"), expectedStatusCode: codes.DeadlineExceeded, expectedStatusMsg: "aws service unknown: timeout: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-internal-server-error", err: TestAwsHttpResponseError(http.StatusInternalServerError, "internal server error"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: connectivity: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-bad-gateway", err: TestAwsHttpResponseError(http.StatusBadGateway, "bad gateway"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: connectivity: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-service-unavailable", err: TestAwsHttpResponseError(http.StatusServiceUnavailable, "service unavailable"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: connectivity: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "http-status-code-gateway-timeout", err: TestAwsHttpResponseError(http.StatusGatewayTimeout, "gateway timeout"), expectedStatusCode: codes.DeadlineExceeded, expectedStatusMsg: "aws service unknown: timeout: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "wrapped-http-status-code-forbidden", err: fmt.Errorf("wrap: %w", TestAwsHttpResponseError(http.StatusForbidden, "forbidden request")), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "forbidden request", CheckedAt: timestamppb.Now(), }, }, { name: "aws-throttling", err: TestAwsError("Throttling", "throttling error"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-throttling-exception", err: TestAwsError("ThrottlingException", "throttling exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-throttled-exception", err: TestAwsError("ThrottledException", "throttled exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-throttled-exception", err: TestAwsError("RequestThrottledException", "request throttled exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-too-many-requests-exception", err: TestAwsError("TooManyRequestsException", "too many requests exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-provisioned-throughput-exceeded-exception", err: TestAwsError("ProvisionedThroughputExceededException", "provisioned throughput exceeded exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-transaction-in-progress-exception", err: TestAwsError("TransactionInProgressException", "transaction in progress exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-limit-exceeded", err: TestAwsError("RequestLimitExceeded", "request limit exceeded"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-bandwidth-limit-exceeded", err: TestAwsError("BandwidthLimitExceeded", "bandwidth limit exceeded"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-limit-exceeded-exception", err: TestAwsError("LimitExceededException", "limit exceeded exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-throttled", err: TestAwsError("RequestThrottled", "request throttled"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-slow-down", err: TestAwsError("SlowDown", "slow down"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-prior-request-not-complete", err: TestAwsError("PriorRequestNotComplete", "prior request not complete"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-ec2-throttled-exception", err: TestAwsError("EC2ThrottledException", "ec2 throttled exception"), expectedStatusCode: codes.Unavailable, expectedStatusMsg: "aws service unknown: throttling: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-timeout", err: TestAwsError("RequestTimeout", "request timeout"), expectedStatusCode: codes.DeadlineExceeded, expectedStatusMsg: "aws service unknown: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-timeout-exception", err: TestAwsError("RequestTimeoutException", "request timeout exception"), expectedStatusCode: codes.DeadlineExceeded, expectedStatusMsg: "aws service unknown: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-request-access-denied", err: TestAwsError(awsErrorAccessDenied, "not authorized to perform action"), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "not authorized to perform action", CheckedAt: timestamppb.Now(), }, }, { name: "aws-request-unauthorized-operation", err: TestAwsError(awsErrorUnauthorizedOperation, "You are not authorized to perform this operation."), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "You are not authorized to perform this operation.", CheckedAt: timestamppb.Now(), }, }, { name: "aws-request-invalid-access-key-id", err: TestAwsError(awsErrorInvalidAccessKeyId, "The AWS Access Key Id you provided does not exist in our records."), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "The AWS Access Key Id you provided does not exist in our records.", CheckedAt: timestamppb.Now(), }, }, { name: "aws-request-expired-token", err: TestAwsError(awsErrorExpiredToken, "The security token included in the request is expired."), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "The security token included in the request is expired.", CheckedAt: timestamppb.Now(), }, }, { name: "aws-s3-typed-not-found", err: TestAwsS3NotFoundError("object was not found"), expectedStatusCode: codes.NotFound, expectedStatusMsg: "aws service unknown: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-s3-typed-no-such-key", err: TestAwsS3NoSuchKeyError("key does not exist"), expectedStatusCode: codes.NotFound, expectedStatusMsg: "aws service unknown: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-s3-typed-invalid-object-state", err: TestAwsS3InvalidObjectStateError("object is archived"), expectedStatusCode: codes.NotFound, expectedStatusMsg: "aws service unknown: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_UNKNOWN, }, }, { name: "aws-s3-typed-no-such-bucket", err: TestAwsS3NoSuchBucketError("bucket does not exist"), expectedStatusCode: codes.NotFound, expectedStatusMsg: "aws service unknown: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "bucket does not exist", CheckedAt: timestamppb.Now(), }, }, { name: "aws-s3-typed-access-denied", err: TestAwsS3AccessDeniedError("access denied"), expectedStatusCode: codes.PermissionDenied, expectedStatusMsg: "aws service unknown: invalid credentials: test", expectedPermission: &pb.Permission{ State: pb.StateType_STATE_TYPE_ERROR, ErrorDetails: "access denied", CheckedAt: timestamppb.Now(), }, }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { require, assert := require.New(t), assert.New(t) actualStatus, permission := ParseAWSError("test", tt.err) if tt.expectedStatusCode == codes.OK { require.Nil(actualStatus) return } require.Equal(tt.expectedStatusCode, actualStatus.Code()) require.Contains(actualStatus.Message(), tt.expectedStatusMsg) require.Len(actualStatus.Details(), 0) CheckSimilarPermission(assert, tt.expectedPermission, permission, true) }) } } func Test_IsDryRunSuccess(t *testing.T) { t.Parallel() cases := []struct { name string err error want bool }{ { name: "nil", want: false, }, { name: "plain error", err: fmt.Errorf("not an aws error"), want: false, }, { name: "other api error", err: TestAwsError(awsErrorAccessDenied, "not authorized"), want: false, }, { name: "dry run operation", err: TestAwsError(dryRunOperationErrorCode, "Request would have succeeded, but DryRun flag is set"), want: true, }, { name: "wrapped dry run operation", err: fmt.Errorf("describe instances: %w", TestAwsError(dryRunOperationErrorCode, "Request would have succeeded, but DryRun flag is set")), want: true, }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, IsDryRunSuccess(tt.err)) }) } } func Test_BadRequestStatusf(t *testing.T) { require, assert := require.New(t), assert.New(t) err := BadRequestStatusf("test: %s", "hello world") assert.ErrorContains(err, "test: hello world") assert.Equal(status.Code(err), codes.InvalidArgument) _, ok := status.FromError(err) require.True(ok) } func Test_BadRequestStatus(t *testing.T) { require, assert := require.New(t), assert.New(t) err := BadRequestStatus("test: hello world") assert.ErrorContains(err, "test: hello world") assert.Equal(status.Code(err), codes.InvalidArgument) _, ok := status.FromError(err) require.True(ok) } func Test_UnknownStatusf(t *testing.T) { require, assert := require.New(t), assert.New(t) err := UnknownStatusf("test: %s", "hello world") assert.ErrorContains(err, "test: hello world") assert.Equal(status.Code(err), codes.Internal) _, ok := status.FromError(err) require.True(ok) } func Test_UnknownStatus(t *testing.T) { require, assert := require.New(t), assert.New(t) err := UnknownStatusf("test: hello world") assert.ErrorContains(err, "test: hello world") assert.Equal(status.Code(err), codes.Internal) _, ok := status.FromError(err) require.True(ok) }