From f862340622f3e808d536bbc52add4e49162823ae Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 19 May 2025 19:51:04 +0000
Subject: [PATCH 1/3] Initial plan for issue
From 7bb9aaafde92452ee7099b0fc9778d1847d24be2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 19 May 2025 20:01:56 +0000
Subject: [PATCH 2/3] Implement DataApiBuilderException to status code mapping
in DabGraphQLResultSerializer
Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com>
---
.../Services/DabGraphQLResultSerializer.cs | 71 +++++++++++++++++--
1 file changed, 65 insertions(+), 6 deletions(-)
diff --git a/src/Core/Services/DabGraphQLResultSerializer.cs b/src/Core/Services/DabGraphQLResultSerializer.cs
index b9cce8334c..dac3f4aedb 100644
--- a/src/Core/Services/DabGraphQLResultSerializer.cs
+++ b/src/Core/Services/DabGraphQLResultSerializer.cs
@@ -11,10 +11,17 @@ namespace Azure.DataApiBuilder.Core.Services;
///
/// The DabGraphQLResultSerializer inspects the IExecutionResult created by HotChocolate
/// and determines the appropriate HTTP error code to return based on the errors in the result.
-/// By Default, without this serializer, HotChocolate will return a 500 status code when database errors
-/// exist. However, there is a specific error code we check for that should return a 400 status code:
-/// - DatabaseInputError. This indicates that the client can make a change to request contents to influence
-/// a change in the response.
+///
+/// By default, without this serializer, HotChocolate will return a 500 status code for errors.
+/// This serializer maps DataApiBuilderException.SubStatusCodes to their appropriate HTTP status codes.
+///
+/// For example:
+/// - Authentication/Authorization errors will return 401/403
+/// - Database input validation errors will return 400 BadRequest
+/// - Entity not found errors will return 404 NotFound
+///
+/// This ensures that GraphQL endpoints return appropriate and consistent HTTP status codes
+/// for all types of DataApiBuilderException errors.
///
public class DabGraphQLResultSerializer : DefaultHttpResultSerializer
{
@@ -22,9 +29,61 @@ public override HttpStatusCode GetStatusCode(IExecutionResult result)
{
if (result is IQueryResult queryResult && queryResult.Errors?.Count > 0)
{
- if (queryResult.Errors.Any(error => error.Code == DataApiBuilderException.SubStatusCodes.DatabaseInputError.ToString()))
+ // Check if any of the errors are from DataApiBuilderException by looking at error.Code
+ foreach (var error in queryResult.Errors)
{
- return HttpStatusCode.BadRequest;
+ if (error.Code != null &&
+ Enum.TryParse(error.Code, out var subStatusCode))
+ {
+ // Map SubStatusCodes to appropriate HTTP status codes
+ switch (subStatusCode)
+ {
+ // Authentication/Authorization errors
+ case DataApiBuilderException.SubStatusCodes.AuthenticationChallenge:
+ return HttpStatusCode.Unauthorized; // 401
+ case DataApiBuilderException.SubStatusCodes.AuthorizationCheckFailed:
+ case DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure:
+ case DataApiBuilderException.SubStatusCodes.AuthorizationCumulativeColumnCheckFailed:
+ return HttpStatusCode.Forbidden; // 403
+
+ // Not Found errors
+ case DataApiBuilderException.SubStatusCodes.EntityNotFound:
+ case DataApiBuilderException.SubStatusCodes.ItemNotFound:
+ case DataApiBuilderException.SubStatusCodes.RelationshipNotFound:
+ case DataApiBuilderException.SubStatusCodes.RelationshipFieldNotFound:
+ case DataApiBuilderException.SubStatusCodes.DataSourceNotFound:
+ return HttpStatusCode.NotFound; // 404
+
+ // Bad Request errors
+ case DataApiBuilderException.SubStatusCodes.BadRequest:
+ case DataApiBuilderException.SubStatusCodes.DatabaseInputError:
+ case DataApiBuilderException.SubStatusCodes.InvalidIdentifierField:
+ case DataApiBuilderException.SubStatusCodes.ErrorProcessingData:
+ case DataApiBuilderException.SubStatusCodes.ExposedColumnNameMappingError:
+ case DataApiBuilderException.SubStatusCodes.UnsupportedClaimValueType:
+ case DataApiBuilderException.SubStatusCodes.ErrorProcessingEasyAuthHeader:
+ return HttpStatusCode.BadRequest; // 400
+
+ // Not Supported errors
+ case DataApiBuilderException.SubStatusCodes.NotSupported:
+ case DataApiBuilderException.SubStatusCodes.GlobalRestEndpointDisabled:
+ return HttpStatusCode.NotImplemented; // 501
+
+ // Conflict errors
+ case DataApiBuilderException.SubStatusCodes.OpenApiDocumentAlreadyExists:
+ return HttpStatusCode.Conflict; // 409
+
+ // Server errors - Internal Server Error
+ case DataApiBuilderException.SubStatusCodes.ConfigValidationError:
+ case DataApiBuilderException.SubStatusCodes.ErrorInInitialization:
+ case DataApiBuilderException.SubStatusCodes.DatabaseOperationFailed:
+ case DataApiBuilderException.SubStatusCodes.GraphQLMapping:
+ case DataApiBuilderException.SubStatusCodes.UnexpectedError:
+ case DataApiBuilderException.SubStatusCodes.OpenApiDocumentCreationFailure:
+ default:
+ return HttpStatusCode.InternalServerError; // 500
+ }
+ }
}
}
From 7c046249592454d27b85574e621c9e036d95b115 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 16 Feb 2026 23:21:28 +0000
Subject: [PATCH 3/3] Improve GraphQL error handling with switch expression and
comprehensive unit tests
Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com>
---
.../Services/DabGraphQLResultSerializer.cs | 94 ++-
.../DabGraphQLResultSerializerTests.cs | 591 ++++++++++++++++++
2 files changed, 637 insertions(+), 48 deletions(-)
create mode 100644 src/Service.Tests/Unittests/DabGraphQLResultSerializerTests.cs
diff --git a/src/Core/Services/DabGraphQLResultSerializer.cs b/src/Core/Services/DabGraphQLResultSerializer.cs
index dac3f4aedb..f71f7dc130 100644
--- a/src/Core/Services/DabGraphQLResultSerializer.cs
+++ b/src/Core/Services/DabGraphQLResultSerializer.cs
@@ -35,58 +35,56 @@ public override HttpStatusCode GetStatusCode(IExecutionResult result)
if (error.Code != null &&
Enum.TryParse(error.Code, out var subStatusCode))
{
- // Map SubStatusCodes to appropriate HTTP status codes
- switch (subStatusCode)
- {
- // Authentication/Authorization errors
- case DataApiBuilderException.SubStatusCodes.AuthenticationChallenge:
- return HttpStatusCode.Unauthorized; // 401
- case DataApiBuilderException.SubStatusCodes.AuthorizationCheckFailed:
- case DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure:
- case DataApiBuilderException.SubStatusCodes.AuthorizationCumulativeColumnCheckFailed:
- return HttpStatusCode.Forbidden; // 403
-
- // Not Found errors
- case DataApiBuilderException.SubStatusCodes.EntityNotFound:
- case DataApiBuilderException.SubStatusCodes.ItemNotFound:
- case DataApiBuilderException.SubStatusCodes.RelationshipNotFound:
- case DataApiBuilderException.SubStatusCodes.RelationshipFieldNotFound:
- case DataApiBuilderException.SubStatusCodes.DataSourceNotFound:
- return HttpStatusCode.NotFound; // 404
-
- // Bad Request errors
- case DataApiBuilderException.SubStatusCodes.BadRequest:
- case DataApiBuilderException.SubStatusCodes.DatabaseInputError:
- case DataApiBuilderException.SubStatusCodes.InvalidIdentifierField:
- case DataApiBuilderException.SubStatusCodes.ErrorProcessingData:
- case DataApiBuilderException.SubStatusCodes.ExposedColumnNameMappingError:
- case DataApiBuilderException.SubStatusCodes.UnsupportedClaimValueType:
- case DataApiBuilderException.SubStatusCodes.ErrorProcessingEasyAuthHeader:
- return HttpStatusCode.BadRequest; // 400
-
- // Not Supported errors
- case DataApiBuilderException.SubStatusCodes.NotSupported:
- case DataApiBuilderException.SubStatusCodes.GlobalRestEndpointDisabled:
- return HttpStatusCode.NotImplemented; // 501
-
- // Conflict errors
- case DataApiBuilderException.SubStatusCodes.OpenApiDocumentAlreadyExists:
- return HttpStatusCode.Conflict; // 409
-
- // Server errors - Internal Server Error
- case DataApiBuilderException.SubStatusCodes.ConfigValidationError:
- case DataApiBuilderException.SubStatusCodes.ErrorInInitialization:
- case DataApiBuilderException.SubStatusCodes.DatabaseOperationFailed:
- case DataApiBuilderException.SubStatusCodes.GraphQLMapping:
- case DataApiBuilderException.SubStatusCodes.UnexpectedError:
- case DataApiBuilderException.SubStatusCodes.OpenApiDocumentCreationFailure:
- default:
- return HttpStatusCode.InternalServerError; // 500
- }
+ return MapSubStatusCodeToHttpStatusCode(subStatusCode);
}
}
}
return base.GetStatusCode(result);
}
+
+ ///
+ /// Maps DataApiBuilderException.SubStatusCodes to appropriate HTTP status codes.
+ ///
+ private static HttpStatusCode MapSubStatusCodeToHttpStatusCode(DataApiBuilderException.SubStatusCodes subStatusCode) => subStatusCode switch
+ {
+ // Authentication/Authorization errors
+ DataApiBuilderException.SubStatusCodes.AuthenticationChallenge
+ => HttpStatusCode.Unauthorized, // 401
+
+ DataApiBuilderException.SubStatusCodes.AuthorizationCheckFailed or
+ DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure or
+ DataApiBuilderException.SubStatusCodes.AuthorizationCumulativeColumnCheckFailed
+ => HttpStatusCode.Forbidden, // 403
+
+ // Not Found errors
+ DataApiBuilderException.SubStatusCodes.EntityNotFound or
+ DataApiBuilderException.SubStatusCodes.ItemNotFound or
+ DataApiBuilderException.SubStatusCodes.RelationshipNotFound or
+ DataApiBuilderException.SubStatusCodes.RelationshipFieldNotFound or
+ DataApiBuilderException.SubStatusCodes.DataSourceNotFound
+ => HttpStatusCode.NotFound, // 404
+
+ // Bad Request errors
+ DataApiBuilderException.SubStatusCodes.BadRequest or
+ DataApiBuilderException.SubStatusCodes.DatabaseInputError or
+ DataApiBuilderException.SubStatusCodes.InvalidIdentifierField or
+ DataApiBuilderException.SubStatusCodes.ErrorProcessingData or
+ DataApiBuilderException.SubStatusCodes.ExposedColumnNameMappingError or
+ DataApiBuilderException.SubStatusCodes.UnsupportedClaimValueType or
+ DataApiBuilderException.SubStatusCodes.ErrorProcessingEasyAuthHeader
+ => HttpStatusCode.BadRequest, // 400
+
+ // Not Implemented errors
+ DataApiBuilderException.SubStatusCodes.NotSupported or
+ DataApiBuilderException.SubStatusCodes.GlobalRestEndpointDisabled
+ => HttpStatusCode.NotImplemented, // 501
+
+ // Conflict errors
+ DataApiBuilderException.SubStatusCodes.OpenApiDocumentAlreadyExists
+ => HttpStatusCode.Conflict, // 409
+
+ // Server errors - Internal Server Error (default)
+ _ => HttpStatusCode.InternalServerError // 500
+ };
}
diff --git a/src/Service.Tests/Unittests/DabGraphQLResultSerializerTests.cs b/src/Service.Tests/Unittests/DabGraphQLResultSerializerTests.cs
new file mode 100644
index 0000000000..62789130b4
--- /dev/null
+++ b/src/Service.Tests/Unittests/DabGraphQLResultSerializerTests.cs
@@ -0,0 +1,591 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+using System.Net;
+using Azure.DataApiBuilder.Core.Services;
+using Azure.DataApiBuilder.Service.Exceptions;
+using HotChocolate;
+using HotChocolate.Execution;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+
+namespace Azure.DataApiBuilder.Service.Tests.UnitTests
+{
+ ///
+ /// Unit tests for DabGraphQLResultSerializer
+ ///
+ [TestClass]
+ public class DabGraphQLResultSerializerTests
+ {
+ private DabGraphQLResultSerializer _serializer = null!;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ _serializer = new DabGraphQLResultSerializer();
+ }
+
+ #region Authentication/Authorization Tests
+
+ ///
+ /// Verify that AuthenticationChallenge maps to 401 Unauthorized
+ ///
+ [TestMethod]
+ public void AuthenticationChallenge_Returns401()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.AuthenticationChallenge);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.Unauthorized, statusCode);
+ }
+
+ ///
+ /// Verify that AuthorizationCheckFailed maps to 403 Forbidden
+ ///
+ [TestMethod]
+ public void AuthorizationCheckFailed_Returns403()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.AuthorizationCheckFailed);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.Forbidden, statusCode);
+ }
+
+ ///
+ /// Verify that DatabasePolicyFailure maps to 403 Forbidden
+ ///
+ [TestMethod]
+ public void DatabasePolicyFailure_Returns403()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.DatabasePolicyFailure);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.Forbidden, statusCode);
+ }
+
+ ///
+ /// Verify that AuthorizationCumulativeColumnCheckFailed maps to 403 Forbidden
+ ///
+ [TestMethod]
+ public void AuthorizationCumulativeColumnCheckFailed_Returns403()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.AuthorizationCumulativeColumnCheckFailed);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.Forbidden, statusCode);
+ }
+
+ #endregion
+
+ #region Not Found Tests
+
+ ///
+ /// Verify that EntityNotFound maps to 404 NotFound
+ ///
+ [TestMethod]
+ public void EntityNotFound_Returns404()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.EntityNotFound);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotFound, statusCode);
+ }
+
+ ///
+ /// Verify that ItemNotFound maps to 404 NotFound
+ ///
+ [TestMethod]
+ public void ItemNotFound_Returns404()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.ItemNotFound);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotFound, statusCode);
+ }
+
+ ///
+ /// Verify that RelationshipNotFound maps to 404 NotFound
+ ///
+ [TestMethod]
+ public void RelationshipNotFound_Returns404()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.RelationshipNotFound);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotFound, statusCode);
+ }
+
+ ///
+ /// Verify that RelationshipFieldNotFound maps to 404 NotFound
+ ///
+ [TestMethod]
+ public void RelationshipFieldNotFound_Returns404()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.RelationshipFieldNotFound);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotFound, statusCode);
+ }
+
+ ///
+ /// Verify that DataSourceNotFound maps to 404 NotFound
+ ///
+ [TestMethod]
+ public void DataSourceNotFound_Returns404()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.DataSourceNotFound);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotFound, statusCode);
+ }
+
+ #endregion
+
+ #region Bad Request Tests
+
+ ///
+ /// Verify that BadRequest maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void BadRequest_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.BadRequest);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ ///
+ /// Verify that DatabaseInputError maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void DatabaseInputError_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.DatabaseInputError);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ ///
+ /// Verify that InvalidIdentifierField maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void InvalidIdentifierField_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.InvalidIdentifierField);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ ///
+ /// Verify that ErrorProcessingData maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void ErrorProcessingData_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.ErrorProcessingData);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ ///
+ /// Verify that ExposedColumnNameMappingError maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void ExposedColumnNameMappingError_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.ExposedColumnNameMappingError);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ ///
+ /// Verify that UnsupportedClaimValueType maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void UnsupportedClaimValueType_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.UnsupportedClaimValueType);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ ///
+ /// Verify that ErrorProcessingEasyAuthHeader maps to 400 BadRequest
+ ///
+ [TestMethod]
+ public void ErrorProcessingEasyAuthHeader_Returns400()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.ErrorProcessingEasyAuthHeader);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.BadRequest, statusCode);
+ }
+
+ #endregion
+
+ #region Not Implemented Tests
+
+ ///
+ /// Verify that NotSupported maps to 501 NotImplemented
+ ///
+ [TestMethod]
+ public void NotSupported_Returns501()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.NotSupported);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotImplemented, statusCode);
+ }
+
+ ///
+ /// Verify that GlobalRestEndpointDisabled maps to 501 NotImplemented
+ ///
+ [TestMethod]
+ public void GlobalRestEndpointDisabled_Returns501()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.GlobalRestEndpointDisabled);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.NotImplemented, statusCode);
+ }
+
+ #endregion
+
+ #region Conflict Tests
+
+ ///
+ /// Verify that OpenApiDocumentAlreadyExists maps to 409 Conflict
+ ///
+ [TestMethod]
+ public void OpenApiDocumentAlreadyExists_Returns409()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.OpenApiDocumentAlreadyExists);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.Conflict, statusCode);
+ }
+
+ #endregion
+
+ #region Server Error Tests
+
+ ///
+ /// Verify that ConfigValidationError maps to 500 InternalServerError
+ ///
+ [TestMethod]
+ public void ConfigValidationError_Returns500()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.ConfigValidationError);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that ErrorInInitialization maps to 500 InternalServerError
+ ///
+ [TestMethod]
+ public void ErrorInInitialization_Returns500()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that DatabaseOperationFailed maps to 500 InternalServerError
+ ///
+ [TestMethod]
+ public void DatabaseOperationFailed_Returns500()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.DatabaseOperationFailed);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that GraphQLMapping maps to 500 InternalServerError
+ ///
+ [TestMethod]
+ public void GraphQLMapping_Returns500()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.GraphQLMapping);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that UnexpectedError maps to 500 InternalServerError
+ ///
+ [TestMethod]
+ public void UnexpectedError_Returns500()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.UnexpectedError);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that OpenApiDocumentCreationFailure maps to 500 InternalServerError
+ ///
+ [TestMethod]
+ public void OpenApiDocumentCreationFailure_Returns500()
+ {
+ // Arrange
+ IExecutionResult result = CreateResultWithError(DataApiBuilderException.SubStatusCodes.OpenApiDocumentCreationFailure);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(result);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ #endregion
+
+ #region Edge Case Tests
+
+ ///
+ /// Verify that when there are no errors, the base status code is returned (500)
+ ///
+ [TestMethod]
+ public void NoErrors_ReturnsBaseStatusCode()
+ {
+ // Arrange
+ Mock mockResult = new();
+ mockResult.Setup(r => r.Errors).Returns((IReadOnlyList)null);
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(mockResult.Object);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that when errors list is empty, the base status code is returned (500)
+ ///
+ [TestMethod]
+ public void EmptyErrorsList_ReturnsBaseStatusCode()
+ {
+ // Arrange
+ Mock mockResult = new();
+ mockResult.Setup(r => r.Errors).Returns(new List());
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(mockResult.Object);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that when error has null code, the base status code is returned (500)
+ ///
+ [TestMethod]
+ public void NullErrorCode_ReturnsBaseStatusCode()
+ {
+ // Arrange
+ Mock mockError = new();
+ mockError.Setup(e => e.Code).Returns((string)null);
+
+ Mock mockResult = new();
+ mockResult.Setup(r => r.Errors).Returns(new List { mockError.Object });
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(mockResult.Object);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that when error code is not a valid SubStatusCode, the base status code is returned (500)
+ ///
+ [TestMethod]
+ public void InvalidErrorCode_ReturnsBaseStatusCode()
+ {
+ // Arrange
+ Mock mockError = new();
+ mockError.Setup(e => e.Code).Returns("InvalidErrorCode");
+
+ Mock mockResult = new();
+ mockResult.Setup(r => r.Errors).Returns(new List { mockError.Object });
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(mockResult.Object);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ ///
+ /// Verify that when there are multiple errors, the first DAB exception error code is used
+ ///
+ [TestMethod]
+ public void MultipleErrors_ReturnsFirstDabExceptionStatusCode()
+ {
+ // Arrange
+ Mock mockError1 = new();
+ mockError1.Setup(e => e.Code).Returns("InvalidCode"); // Not a DAB exception
+
+ Mock mockError2 = new();
+ mockError2.Setup(e => e.Code).Returns(DataApiBuilderException.SubStatusCodes.EntityNotFound.ToString());
+
+ Mock mockError3 = new();
+ mockError3.Setup(e => e.Code).Returns(DataApiBuilderException.SubStatusCodes.BadRequest.ToString());
+
+ Mock mockResult = new();
+ mockResult.Setup(r => r.Errors).Returns(new List { mockError1.Object, mockError2.Object, mockError3.Object });
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(mockResult.Object);
+
+ // Assert - Should return status code for first valid DAB exception (EntityNotFound = 404)
+ Assert.AreEqual(HttpStatusCode.NotFound, statusCode);
+ }
+
+ ///
+ /// Verify that result that is not IQueryResult returns base status code (500)
+ ///
+ [TestMethod]
+ public void NonQueryResult_ReturnsBaseStatusCode()
+ {
+ // Arrange
+ Mock mockResult = new();
+
+ // Act
+ HttpStatusCode statusCode = _serializer.GetStatusCode(mockResult.Object);
+
+ // Assert
+ Assert.AreEqual(HttpStatusCode.InternalServerError, statusCode);
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ ///
+ /// Creates a mock IExecutionResult with a single error containing the specified SubStatusCode
+ ///
+ private static IExecutionResult CreateResultWithError(DataApiBuilderException.SubStatusCodes subStatusCode)
+ {
+ Mock mockError = new();
+ mockError.Setup(e => e.Code).Returns(subStatusCode.ToString());
+
+ Mock mockResult = new();
+ mockResult.Setup(r => r.Errors).Returns(new List { mockError.Object });
+
+ return mockResult.Object;
+ }
+
+ #endregion
+ }
+}