Files
query-orchestration/internal/usermanagement/errors_test.go
T
Jay Brown 963ccc6553 Merged in feature/demo-support (pull request #207)
Feature/demo support

* index logs now debug

* demo data loader
2026-02-02 19:08:08 +00:00

241 lines
7.1 KiB
Go

package usermanagement
import (
"errors"
"net/http"
"testing"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
"github.com/aws/smithy-go"
"github.com/stretchr/testify/assert"
)
// mockAPIError implements smithy.APIError for testing.
type mockAPIError struct {
code string
message string
}
func (e *mockAPIError) Error() string { return e.message }
func (e *mockAPIError) ErrorCode() string { return e.code }
func (e *mockAPIError) ErrorMessage() string { return e.message }
func (e *mockAPIError) ErrorFault() smithy.ErrorFault { return smithy.FaultServer }
func TestClassifyCognitoError_NilError(t *testing.T) {
result := ClassifyCognitoError(nil, "test@example.com")
assert.Equal(t, http.StatusOK, result.HTTPStatus)
assert.Equal(t, CognitoErrorType(""), result.ErrorType)
assert.Empty(t, result.Message)
}
func TestClassifyCognitoError_UserNotFoundException(t *testing.T) {
email := "test@example.com"
err := &types.UserNotFoundException{
Message: stringPtr("User does not exist"),
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusNotFound, result.HTTPStatus)
assert.Equal(t, ErrTypeUserNotFound, result.ErrorType)
assert.Contains(t, result.Message, email)
assert.NotEmpty(t, result.Details)
}
func TestClassifyCognitoError_NotAuthorizedException(t *testing.T) {
email := "test@example.com"
err := &types.NotAuthorizedException{
Message: stringPtr("User is not authorized"),
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusUnauthorized, result.HTTPStatus)
assert.Equal(t, ErrTypeNotAuthorized, result.ErrorType)
assert.NotEmpty(t, result.Message)
}
func TestClassifyCognitoError_InvalidParameterException(t *testing.T) {
email := "test@example.com"
err := &types.InvalidParameterException{
Message: stringPtr("Invalid email format"),
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusBadRequest, result.HTTPStatus)
assert.Equal(t, ErrTypeInvalidParameter, result.ErrorType)
assert.NotEmpty(t, result.Message)
}
func TestClassifyCognitoError_TooManyRequestsException(t *testing.T) {
email := "test@example.com"
err := &types.TooManyRequestsException{
Message: stringPtr("Rate exceeded"),
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusTooManyRequests, result.HTTPStatus)
assert.Equal(t, ErrTypeRateLimited, result.ErrorType)
assert.NotEmpty(t, result.Message)
}
func TestClassifyCognitoError_InternalErrorException(t *testing.T) {
email := "test@example.com"
err := &types.InternalErrorException{
Message: stringPtr("Internal server error"),
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusServiceUnavailable, result.HTTPStatus)
assert.Equal(t, ErrTypeServiceUnavailable, result.ErrorType)
assert.NotEmpty(t, result.Message)
}
func TestClassifyCognitoError_UsernameExistsException(t *testing.T) {
email := "test@example.com"
err := &types.UsernameExistsException{
Message: stringPtr("User already exists"),
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusConflict, result.HTTPStatus)
assert.Equal(t, ErrTypeUserExists, result.ErrorType)
assert.Contains(t, result.Message, email)
}
func TestClassifyCognitoError_AccessDeniedException_ViaAPIError(t *testing.T) {
email := "test@example.com"
err := &mockAPIError{
code: "AccessDeniedException",
message: "User: arn:aws:sts::123456789:assumed-role/role is not authorized",
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusForbidden, result.HTTPStatus)
assert.Equal(t, ErrTypeAccessDenied, result.ErrorType)
assert.Contains(t, result.Message, "Access denied")
}
func TestClassifyCognitoError_GenericError(t *testing.T) {
email := "test@example.com"
err := errors.New("some unknown error")
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusBadGateway, result.HTTPStatus)
assert.Equal(t, ErrTypeServiceError, result.ErrorType)
assert.Contains(t, result.Message, "Failed to communicate with Cognito")
}
func TestClassifyCognitoError_UnknownAPIError(t *testing.T) {
email := "test@example.com"
err := &mockAPIError{
code: "SomeUnknownException",
message: "Unknown error occurred",
}
result := ClassifyCognitoError(err, email)
assert.Equal(t, http.StatusBadGateway, result.HTTPStatus)
assert.Equal(t, ErrTypeServiceError, result.ErrorType)
}
func TestSanitizeErrorMessage_RemovesARNs(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "removes single ARN",
input: "User: arn:aws:sts::123456789012:assumed-role/TestRole/session is not authorized",
expected: "User: [REDACTED] is not authorized",
},
{
name: "removes multiple ARNs",
input: "arn:aws:iam::123:user/test and arn:aws:s3:::bucket",
expected: "[REDACTED] and [REDACTED]",
},
{
name: "handles no ARN",
input: "Simple error message",
expected: "Simple error message",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := sanitizeErrorMessage(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
func TestSanitizeErrorMessage_RemovesRequestIDs(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "removes RequestID",
input: "Error occurred, RequestID: abc-123-def",
expected: "Error occurred,",
},
{
name: "removes Request ID with space",
input: "Error occurred, Request ID: abc-123-def something",
expected: "Error occurred, something",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := sanitizeErrorMessage(tt.input)
assert.NotContains(t, result, "abc-123-def")
})
}
}
func TestClassifyAPIError_AllCases(t *testing.T) {
email := "user@test.com"
tests := []struct {
name string
code string
expectedStatus int
expectedType CognitoErrorType
}{
{"AccessDenied", "AccessDeniedException", http.StatusForbidden, ErrTypeAccessDenied},
{"UserNotFound", "UserNotFoundException", http.StatusNotFound, ErrTypeUserNotFound},
{"NotAuthorized", "NotAuthorizedException", http.StatusUnauthorized, ErrTypeNotAuthorized},
{"InvalidParameter", "InvalidParameterException", http.StatusBadRequest, ErrTypeInvalidParameter},
{"TooManyRequests", "TooManyRequestsException", http.StatusTooManyRequests, ErrTypeRateLimited},
{"ServiceUnavailable", "ServiceUnavailable", http.StatusServiceUnavailable, ErrTypeServiceUnavailable},
{"InternalError", "InternalError", http.StatusServiceUnavailable, ErrTypeServiceUnavailable},
{"UsernameExists", "UsernameExistsException", http.StatusConflict, ErrTypeUserExists},
{"Unknown", "SomeRandomException", http.StatusBadGateway, ErrTypeServiceError},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
apiErr := &mockAPIError{code: tt.code, message: "test message"}
result := classifyAPIError(apiErr, email)
assert.Equal(t, tt.expectedStatus, result.HTTPStatus)
assert.Equal(t, tt.expectedType, result.ErrorType)
})
}
}
// stringPtr is a helper to create a pointer to a string.
func stringPtr(s string) *string {
return &s
}