diff --git a/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLJsonSchemaTests.cs b/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLJsonSchemaTests.cs index 66ee6156a7..54dc30e753 100644 --- a/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLJsonSchemaTests.cs +++ b/src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLJsonSchemaTests.cs @@ -82,5 +82,26 @@ public async Task JsonColumn_GraphQLRead_ReturnsPayloadAsString() Assert.AreEqual("admin", parsed.GetProperty("role").GetString()); Assert.AreEqual(3, parsed.GetProperty("tier").GetInt32()); } + + /// + /// createProfile with malformed JSON in the metadata field must fail with a GraphQL error + /// (surfaced from SQL Server's json validation) rather than persisting invalid data. + /// + [TestMethod] + public async Task JsonColumn_GraphQLCreateWithMalformedJson_Fails() + { + string createMutationName = "createProfile"; + string createMutation = @"mutation { + createProfile(item: { metadata: ""{ not valid json"" }) { + id + metadata + } + }"; + + JsonElement errors = await ExecuteGraphQLRequestAsync(createMutation, createMutationName, isAuthenticated: false); + + Assert.AreEqual(JsonValueKind.Array, errors.ValueKind, "Expected a GraphQL errors array for malformed JSON payload."); + Assert.IsTrue(errors.GetArrayLength() > 0, "Expected at least one GraphQL error."); + } } } diff --git a/src/Service.Tests/SqlTests/RestApiTests/MsSqlRestJsonTypesTests.cs b/src/Service.Tests/SqlTests/RestApiTests/MsSqlRestJsonTypesTests.cs index 8e8c20417d..04d2addfab 100644 --- a/src/Service.Tests/SqlTests/RestApiTests/MsSqlRestJsonTypesTests.cs +++ b/src/Service.Tests/SqlTests/RestApiTests/MsSqlRestJsonTypesTests.cs @@ -114,6 +114,21 @@ public async Task GetJsonTypeWithUnicode() Assert.AreEqual("éü😀", metadata.GetProperty("unicode").GetString()); } + /// + /// GET /api/Profile?$filter=metadata ne null - Verify filtering a json column (treated as a + /// string) passes through to SQL: the 4 non-null rows match and the null row (id 5) does not. + /// + [TestMethod] + public async Task FilterJsonColumnIsNotNull_Succeeds() + { + HttpResponseMessage response = await HttpClient.GetAsync($"{JSON_TYPE_REST_PATH}?$filter=metadata%20ne%20null"); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Filtering a json column as a string should pass through and succeed."); + + JsonElement items = JsonDocument.Parse(await response.Content.ReadAsStringAsync()) + .RootElement.GetProperty("value"); + Assert.AreEqual(4, items.GetArrayLength(), "Only the 4 rows with non-null metadata should match."); + } + #endregion #region Write Tests @@ -221,6 +236,26 @@ public async Task PatchJsonType_ToNull() } } + /// + /// POST /api/Profile - Verify that supplying invalid JSON for the json column is rejected by + /// SQL Server and surfaced as HTTP 400 (a client input error), not a 500. DAB treats the value + /// as a normal string, so JSON validation happens at the database boundary. + /// + [DataTestMethod] + [DataRow("{ \"metadata\": \"{ not valid json\" }", DisplayName = "Unclosed / unquoted object")] + [DataRow("{ \"metadata\": \"{\\\"key\\\": }\" }", DisplayName = "Missing value")] + public async Task InsertMalformedJson_ReturnsBadRequest(string requestBody) + { + HttpResponseMessage response = await HttpClient.PostAsync( + JSON_TYPE_REST_PATH, + new StringContent(requestBody, Encoding.UTF8, "application/json")); + + Assert.AreEqual( + HttpStatusCode.BadRequest, + response.StatusCode, + "SQL Server rejects invalid JSON for a native json column; DAB must surface it as HTTP 400."); + } + #endregion #region Helpers