Skip to content

Commit f8d556f

Browse files
souvikghosh04Copilotaaronburtle
authored
[Phase 4] MSSQL JSON - malformed-JSON (400) + filter edge-case tests (#3751)
Resolves #3752 ## Why Phase 4 of MSSQL native `JSON` support (#2768) — error-handling and filter edge cases. Builds on the merged engine + schema-discovery work (#3691, #3720, #3738). ## What | User story | Test | Asserts | |-----------|------|---------| | **US7 — REST** | `InsertMalformedJson_ReturnsBadRequest` | Posting invalid JSON for the `json` column is rejected by SQL Server and surfaced as **HTTP 400** (not 500), exercising the `13608–13614 → BadRequest` mapping | | **US7 — GraphQL** | `JsonColumn_GraphQLCreateWithMalformedJson_Fails` | A `createProfile` mutation with malformed JSON fails with a GraphQL error (mirrors the merged vector-type pattern in `MsSqlGraphQLVectorTypesTests`) | | **US9 — REST** | `FilterJsonColumnIsNotNull_Succeeds` | Filtering a `json` column as a string (`$filter=metadata ne null`) passes through to SQL and returns the 4 non-null rows | Because DAB treats `json` as a normal string and does no pre-validation, invalid JSON is caught at the database boundary and mapped to a client error — consistent with the "nothing special" contract. ## Deferred (pending CI observation) - **Pruning `13608–13614`**: kept the full documented JSON-validation range for now. Removing a code that actually fires would regress it to a 500, so I'd rather confirm the exact code(s) SQL 2025 emits (via this PR's CI run) before narrowing the list. - **Sort / equality on a `json` column**: SQL Server disallows comparing/sorting the native `json` type (except `IS [NOT] NULL`), so `$orderby=metadata` / `metadata eq '…'` behavior + error-code mapping needs CI confirmation before I assert on it. ## Notes - Tests only — no product code changes. - Requires SQL Server 2025 / Azure SQL (native `json`); CI already runs SQL 2025. - Cannot be run locally (no SQL 2025 here) — relying on CI. ## Delivery plan | Phase | What | Status | |-------|------|--------| | 1 | .NET 10 + SqlClient 6.x | ✅ Merged (#3697/#3656) | | 2 | JSON type + error mapping (engine) | ✅ Merged (#3691) | | 3 | Test fixture + REST CRUD tests | ✅ Merged (#3720) | | 3b | json-as-string read fix + OpenAPI/GraphQL tests | ✅ Merged (#3738) | | **4** | **Error / filter edge cases (this PR)** | 🚧 | --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: aaronburtle <93220300+aaronburtle@users.noreply.github.com>
1 parent d783120 commit f8d556f

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

src/Service.Tests/SqlTests/GraphQLQueryTests/MsSqlGraphQLJsonSchemaTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,26 @@ public async Task JsonColumn_GraphQLRead_ReturnsPayloadAsString()
8282
Assert.AreEqual("admin", parsed.GetProperty("role").GetString());
8383
Assert.AreEqual(3, parsed.GetProperty("tier").GetInt32());
8484
}
85+
86+
/// <summary>
87+
/// createProfile with malformed JSON in the metadata field must fail with a GraphQL error
88+
/// (surfaced from SQL Server's json validation) rather than persisting invalid data.
89+
/// </summary>
90+
[TestMethod]
91+
public async Task JsonColumn_GraphQLCreateWithMalformedJson_Fails()
92+
{
93+
string createMutationName = "createProfile";
94+
string createMutation = @"mutation {
95+
createProfile(item: { metadata: ""{ not valid json"" }) {
96+
id
97+
metadata
98+
}
99+
}";
100+
101+
JsonElement errors = await ExecuteGraphQLRequestAsync(createMutation, createMutationName, isAuthenticated: false);
102+
103+
Assert.AreEqual(JsonValueKind.Array, errors.ValueKind, "Expected a GraphQL errors array for malformed JSON payload.");
104+
Assert.IsTrue(errors.GetArrayLength() > 0, "Expected at least one GraphQL error.");
105+
}
85106
}
86107
}

src/Service.Tests/SqlTests/RestApiTests/MsSqlRestJsonTypesTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ public async Task GetJsonTypeWithUnicode()
114114
Assert.AreEqual("éü😀", metadata.GetProperty("unicode").GetString());
115115
}
116116

117+
/// <summary>
118+
/// GET /api/Profile?$filter=metadata ne null - Verify filtering a json column (treated as a
119+
/// string) passes through to SQL: the 4 non-null rows match and the null row (id 5) does not.
120+
/// </summary>
121+
[TestMethod]
122+
public async Task FilterJsonColumnIsNotNull_Succeeds()
123+
{
124+
HttpResponseMessage response = await HttpClient.GetAsync($"{JSON_TYPE_REST_PATH}?$filter=metadata%20ne%20null");
125+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Filtering a json column as a string should pass through and succeed.");
126+
127+
JsonElement items = JsonDocument.Parse(await response.Content.ReadAsStringAsync())
128+
.RootElement.GetProperty("value");
129+
Assert.AreEqual(4, items.GetArrayLength(), "Only the 4 rows with non-null metadata should match.");
130+
}
131+
117132
#endregion
118133

119134
#region Write Tests
@@ -221,6 +236,26 @@ public async Task PatchJsonType_ToNull()
221236
}
222237
}
223238

239+
/// <summary>
240+
/// POST /api/Profile - Verify that supplying invalid JSON for the json column is rejected by
241+
/// SQL Server and surfaced as HTTP 400 (a client input error), not a 500. DAB treats the value
242+
/// as a normal string, so JSON validation happens at the database boundary.
243+
/// </summary>
244+
[DataTestMethod]
245+
[DataRow("{ \"metadata\": \"{ not valid json\" }", DisplayName = "Unclosed / unquoted object")]
246+
[DataRow("{ \"metadata\": \"{\\\"key\\\": }\" }", DisplayName = "Missing value")]
247+
public async Task InsertMalformedJson_ReturnsBadRequest(string requestBody)
248+
{
249+
HttpResponseMessage response = await HttpClient.PostAsync(
250+
JSON_TYPE_REST_PATH,
251+
new StringContent(requestBody, Encoding.UTF8, "application/json"));
252+
253+
Assert.AreEqual(
254+
HttpStatusCode.BadRequest,
255+
response.StatusCode,
256+
"SQL Server rejects invalid JSON for a native json column; DAB must surface it as HTTP 400.");
257+
}
258+
224259
#endregion
225260

226261
#region Helpers

0 commit comments

Comments
 (0)