Skip to content

Commit 9639b33

Browse files
committed
test(mssql-json): add OpenAPI + GraphQL schema-discovery tests (Phase 3b)
Proves the 'JSON is treated as a normal string' contract at the schema-discovery layer, complementing the merged REST round-trip tests (#3720). - JsonTypeSchemaTests (T008): the profiles.metadata json column is exposed in the OpenAPI document as type:string, nullable, with no bespoke format. - MsSqlGraphQLJsonSchemaTests (T009): GraphQL introspection reports Profile.metadata as the built-in nullable String scalar (no custom JSON scalar). MCP describe_entities (T010) is intentionally omitted: describe_entities projects only config field name/description, not DB column data types, so there is no JSON-specific behavior to assert there.
1 parent b4e06b5 commit 9639b33

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections.Generic;
5+
using System.Threading.Tasks;
6+
using Azure.DataApiBuilder.Config.ObjectModel;
7+
using Microsoft.OpenApi.Models;
8+
using Microsoft.VisualStudio.TestTools.UnitTesting;
9+
10+
namespace Azure.DataApiBuilder.Service.Tests.OpenApiIntegration
11+
{
12+
/// <summary>
13+
/// Validates how a SQL Server 2025 native <c>json</c> column is surfaced in the generated
14+
/// OpenAPI document. DAB does nothing special for a JSON column - it is treated as a normal
15+
/// <c>string</c>, so it must be described with <c>type: string</c>, no custom <c>format</c>,
16+
/// and honoring the column's nullability. This is the schema-discovery counterpart to the
17+
/// REST round-trip coverage in
18+
/// <see cref="SqlTests.RestApiTests.MsSqlRestJsonTypesTests"/>.
19+
/// NOTE: The native JSON data type requires SQL Server 2025 / Azure SQL.
20+
/// </summary>
21+
[TestCategory(TestCategory.MSSQL)]
22+
[TestClass]
23+
public class JsonTypeSchemaTests
24+
{
25+
private const string CONFIG_FILE = "json-type-openapi-config.MsSql.json";
26+
private const string DB_ENV = TestCategory.MSSQL;
27+
28+
/// <summary>
29+
/// The <c>profiles.metadata</c> (json, nullable) column must appear in the OpenAPI schema as
30+
/// a plain nullable string with no format - proving JSON is not given a bespoke scalar/format.
31+
/// </summary>
32+
[TestMethod]
33+
public async Task JsonColumn_IsDescribedAsNullableStringWithoutFormat()
34+
{
35+
OpenApiDocument doc = await GenerateProfileDocumentAsync();
36+
37+
Assert.IsTrue(doc.Components.Schemas.ContainsKey("Profile"), "Schema should exist for the Profile entity.");
38+
39+
OpenApiSchema profileSchema = doc.Components.Schemas["Profile"];
40+
Assert.IsTrue(profileSchema.Properties.ContainsKey("metadata"), "The json 'metadata' column should be present in the schema.");
41+
42+
OpenApiSchema metadataSchema = profileSchema.Properties["metadata"];
43+
Assert.AreEqual("string", metadataSchema.Type, "A json column must be described as a plain string (treated like any string column).");
44+
Assert.IsTrue(string.IsNullOrEmpty(metadataSchema.Format), "A json column must not carry a bespoke OpenAPI format.");
45+
Assert.IsTrue(metadataSchema.Nullable, "The nullable json column must be described as nullable.");
46+
}
47+
48+
/// <summary>
49+
/// Builds an OpenAPI document for a Profile entity sourced from the <c>profiles</c> table,
50+
/// with REST + GraphQL enabled and anonymous/authenticated CRUD permissions.
51+
/// </summary>
52+
private static async Task<OpenApiDocument> GenerateProfileDocumentAsync()
53+
{
54+
Entity entity = new(
55+
Source: new("profiles", EntitySourceType.Table, null, null),
56+
Fields: null,
57+
GraphQL: new("Profile", "Profiles", true),
58+
Rest: new(EntityRestOptions.DEFAULT_SUPPORTED_VERBS),
59+
Permissions: OpenApiTestBootstrap.CreateBasicPermissions(),
60+
Mappings: null,
61+
Relationships: null);
62+
63+
RuntimeEntities entities = new(new Dictionary<string, Entity> { { "Profile", entity } });
64+
return await OpenApiTestBootstrap.GenerateOpenApiDocumentAsync(entities, CONFIG_FILE, DB_ENV);
65+
}
66+
}
67+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Linq;
5+
using System.Text.Json;
6+
using System.Threading.Tasks;
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
9+
namespace Azure.DataApiBuilder.Service.Tests.SqlTests.GraphQLQueryTests
10+
{
11+
/// <summary>
12+
/// GraphQL schema-discovery (introspection) tests for a SQL Server 2025 native <c>json</c> column.
13+
/// DAB does nothing special for a JSON column - it is treated as a normal <c>string</c>, so the
14+
/// GraphQL schema must expose it using the built-in <c>String</c> scalar (no bespoke JSON scalar),
15+
/// honoring the column's nullability. This is the GraphQL counterpart to the REST/OpenAPI coverage
16+
/// in <see cref="RestApiTests.MsSqlRestJsonTypesTests"/> and <c>JsonTypeSchemaTests</c>.
17+
/// NOTE: The native JSON data type requires SQL Server 2025 / Azure SQL.
18+
/// </summary>
19+
[TestClass, TestCategory(TestCategory.MSSQL)]
20+
public class MsSqlGraphQLJsonSchemaTests : SqlTestBase
21+
{
22+
[ClassInitialize]
23+
public static async Task SetupAsync(TestContext context)
24+
{
25+
DatabaseEngine = TestCategory.MSSQL;
26+
await InitializeTestFixture();
27+
}
28+
29+
/// <summary>
30+
/// Introspecting the Profile type must report its json-backed <c>metadata</c> field as the
31+
/// built-in nullable <c>String</c> scalar - proving JSON gets no custom scalar in the schema.
32+
/// </summary>
33+
[TestMethod]
34+
public async Task JsonColumn_IsIntrospectedAsBuiltInStringScalar()
35+
{
36+
string introspectionQuery = @"{
37+
__type(name: ""Profile"") {
38+
name
39+
fields {
40+
name
41+
type { kind name ofType { kind name } }
42+
}
43+
}
44+
}";
45+
46+
JsonElement type = await ExecuteGraphQLRequestAsync(introspectionQuery, "__type", isAuthenticated: false);
47+
48+
Assert.AreEqual("Profile", type.GetProperty("name").GetString(), "Introspection should resolve the Profile GraphQL type.");
49+
50+
JsonElement metadataField = type.GetProperty("fields").EnumerateArray()
51+
.Single(f => f.GetProperty("name").GetString() == "metadata");
52+
53+
// A nullable column surfaces as the bare scalar (no NON_NULL wrapper), so type.kind/name
54+
// describe the scalar directly.
55+
JsonElement metadataType = metadataField.GetProperty("type");
56+
Assert.AreEqual("SCALAR", metadataType.GetProperty("kind").GetString(), "A json column must map to a scalar, not a custom object/scalar type.");
57+
Assert.AreEqual("String", metadataType.GetProperty("name").GetString(), "A json column must use the built-in String scalar (no bespoke JSON scalar).");
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)