Skip to content

Commit 0b43e3b

Browse files
Partial fix for MCP test (#2925)
## Why make this change? - #2874 There is a test that is failing and it needs to be fixed in order to comply with the creation of the new MCP endpoint. The test already existed and covered the scenarios for REST and GraphQL endpoints. It will only be partially fixed, only fixing the issues related to the non-hosted scenario, this is due to the fact that fixing the hosted-scenario will need more changes than expected. ## What is this change? This change partially fixes the test that was failing, the non-hosted scenario was failing because the MCP server was not able to start in time before the test tried to access the MCP endpoint. In order to fix it we added a delay so the server is available before the test tries to access the endpoint. On the other hand, the hosted scenario is failing because of the way that DAB initializes its MCP service, which means that the base needs to be changed. Which is a bigger task than what is expected of this PR. ## How was this tested? - [ ] Integration Tests - [X] Unit Tests
1 parent 9dad3d5 commit 0b43e3b

File tree

1 file changed

+119
-43
lines changed

1 file changed

+119
-43
lines changed

src/Service.Tests/Configuration/ConfigurationTests.cs

Lines changed: 119 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class ConfigurationTests
7474
private const string BROWSER_ACCEPT_HEADER = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9";
7575

7676
private const int RETRY_COUNT = 5;
77-
private const int RETRY_WAIT_SECONDS = 1;
77+
private const int RETRY_WAIT_SECONDS = 2;
7878

7979
/// <summary>
8080
///
@@ -1128,7 +1128,7 @@ public async Task TestSqlSettingPostStartupConfigurations(string configurationEn
11281128
await httpClient.GetAsync($"/{OPENAPI_SWAGGER_ENDPOINT}");
11291129
Assert.AreEqual(HttpStatusCode.ServiceUnavailable, preConfigOpenApiSwaggerEndpointAvailability.StatusCode);
11301130

1131-
HttpStatusCode responseCode = await HydratePostStartupConfiguration(httpClient, content, configurationEndpoint);
1131+
HttpStatusCode responseCode = await HydratePostStartupConfiguration(httpClient, content, configurationEndpoint, configuration.Runtime.Rest);
11321132

11331133
// When the authorization resolver is properly configured, authorization will have failed
11341134
// because no auth headers are present.
@@ -2538,26 +2538,37 @@ public async Task TestRuntimeBaseRouteInNextLinkForPaginatedRestResponse()
25382538
/// <param name="expectedStatusCodeForGraphQL">Expected HTTP status code code for the GraphQL request</param>
25392539
[DataTestMethod]
25402540
[TestCategory(TestCategory.MSSQL)]
2541-
[DataRow(true, true, HttpStatusCode.OK, HttpStatusCode.OK, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Both Rest and GraphQL endpoints enabled globally")]
2542-
[DataRow(true, false, HttpStatusCode.OK, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest enabled and GraphQL endpoints disabled globally")]
2543-
[DataRow(false, true, HttpStatusCode.NotFound, HttpStatusCode.OK, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest disabled and GraphQL endpoints enabled globally")]
2544-
[DataRow(true, true, HttpStatusCode.OK, HttpStatusCode.OK, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Both Rest and GraphQL endpoints enabled globally")]
2545-
[DataRow(true, false, HttpStatusCode.OK, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest enabled and GraphQL endpoints disabled globally")]
2546-
[DataRow(false, true, HttpStatusCode.NotFound, HttpStatusCode.OK, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest disabled and GraphQL endpoints enabled globally")]
2547-
public async Task TestGlobalFlagToEnableRestAndGraphQLForHostedAndNonHostedEnvironment(
2541+
[DataRow(true, true, true, HttpStatusCode.OK, HttpStatusCode.OK, HttpStatusCode.OK, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest, GraphQL, and MCP enabled globally")]
2542+
[DataRow(true, true, false, HttpStatusCode.OK, HttpStatusCode.OK, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest and GraphQL enabled, MCP disabled globally")]
2543+
[DataRow(true, false, true, HttpStatusCode.OK, HttpStatusCode.NotFound, HttpStatusCode.OK, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest enabled, GraphQL disabled, and MCP enabled globally")]
2544+
[DataRow(true, false, false, HttpStatusCode.OK, HttpStatusCode.NotFound, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest enabled, GraphQL and MCP disabled globally")]
2545+
[DataRow(false, true, true, HttpStatusCode.NotFound, HttpStatusCode.OK, HttpStatusCode.OK, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest disabled, GraphQL and MCP enabled globally")]
2546+
[DataRow(false, true, false, HttpStatusCode.NotFound, HttpStatusCode.OK, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest disabled, GraphQL enabled, and MCP disabled globally")]
2547+
[DataRow(false, false, true, HttpStatusCode.NotFound, HttpStatusCode.NotFound, HttpStatusCode.OK, CONFIGURATION_ENDPOINT, DisplayName = "V1 - Rest and GraphQL disabled, MCP enabled globally")]
2548+
[DataRow(true, true, true, HttpStatusCode.OK, HttpStatusCode.OK, HttpStatusCode.OK, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest, GraphQL, and MCP enabled globally")]
2549+
[DataRow(true, true, false, HttpStatusCode.OK, HttpStatusCode.OK, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest and GraphQL enabled, MCP disabled globally")]
2550+
[DataRow(true, false, true, HttpStatusCode.OK, HttpStatusCode.NotFound, HttpStatusCode.OK, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest enabled, GraphQL disabled, and MCP enabled globally")]
2551+
[DataRow(true, false, false, HttpStatusCode.OK, HttpStatusCode.NotFound, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest enabled, GraphQL and MCP disabled globally")]
2552+
[DataRow(false, true, true, HttpStatusCode.NotFound, HttpStatusCode.OK, HttpStatusCode.OK, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest disabled, GraphQL and MCP enabled globally")]
2553+
[DataRow(false, true, false, HttpStatusCode.NotFound, HttpStatusCode.OK, HttpStatusCode.NotFound, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest disabled, GraphQL enabled, and MCP disabled globally")]
2554+
[DataRow(false, false, true, HttpStatusCode.NotFound, HttpStatusCode.NotFound, HttpStatusCode.OK, CONFIGURATION_ENDPOINT_V2, DisplayName = "V2 - Rest and GraphQL disabled, MCP enabled globally")]
2555+
public async Task TestGlobalFlagToEnableRestGraphQLAndMcpForHostedAndNonHostedEnvironment(
25482556
bool isRestEnabled,
25492557
bool isGraphQLEnabled,
2558+
bool isMcpEnabled,
25502559
HttpStatusCode expectedStatusCodeForREST,
25512560
HttpStatusCode expectedStatusCodeForGraphQL,
2561+
HttpStatusCode expectedStatusCodeForMcp,
25522562
string configurationEndpoint)
25532563
{
25542564
GraphQLRuntimeOptions graphqlOptions = new(Enabled: isGraphQLEnabled);
25552565
RestRuntimeOptions restRuntimeOptions = new(Enabled: isRestEnabled);
2566+
McpRuntimeOptions mcpRuntimeOptions = new(Enabled: isMcpEnabled);
25562567

25572568
DataSource dataSource = new(DatabaseType.MSSQL,
25582569
GetConnectionStringFromEnvironmentConfig(environment: TestCategory.MSSQL), Options: null);
25592570

2560-
RuntimeConfig configuration = InitMinimalRuntimeConfig(dataSource, graphqlOptions, restRuntimeOptions, null);
2571+
RuntimeConfig configuration = InitMinimalRuntimeConfig(dataSource, graphqlOptions, restRuntimeOptions, mcpRuntimeOptions);
25612572
const string CUSTOM_CONFIG = "custom-config.json";
25622573
File.WriteAllText(CUSTOM_CONFIG, configuration.ToJson());
25632574

@@ -2580,17 +2591,23 @@ public async Task TestGlobalFlagToEnableRestAndGraphQLForHostedAndNonHostedEnvir
25802591

25812592
object payload = new { query };
25822593

2583-
HttpRequestMessage graphQLRequest = new(HttpMethod.Post, "/graphql")
2594+
// GraphQL request
2595+
HttpRequestMessage graphQLRequest = new(HttpMethod.Post, configuration.Runtime.GraphQL.Path)
25842596
{
25852597
Content = JsonContent.Create(payload)
25862598
};
25872599

25882600
HttpResponseMessage graphQLResponse = await client.SendAsync(graphQLRequest);
2589-
Assert.AreEqual(expectedStatusCodeForGraphQL, graphQLResponse.StatusCode);
2601+
Assert.AreEqual(expectedStatusCodeForGraphQL, graphQLResponse.StatusCode, "The GraphQL response is different from the expected result.");
25902602

2591-
HttpRequestMessage restRequest = new(HttpMethod.Get, "/api/Book");
2603+
// REST request
2604+
HttpRequestMessage restRequest = new(HttpMethod.Get, $"{configuration.Runtime.Rest.Path}/Book");
25922605
HttpResponseMessage restResponse = await client.SendAsync(restRequest);
2593-
Assert.AreEqual(expectedStatusCodeForREST, restResponse.StatusCode);
2606+
Assert.AreEqual(expectedStatusCodeForREST, restResponse.StatusCode, "The REST response is different from the expected result.");
2607+
2608+
// MCP request
2609+
HttpStatusCode mcpResponseCode = await GetMcpResponse(client, configuration.Runtime.Mcp);
2610+
Assert.AreEqual(expectedStatusCodeForMcp, mcpResponseCode, "The MCP response is different from the expected result.");
25942611
}
25952612

25962613
// Hosted Scenario
@@ -2600,18 +2617,19 @@ public async Task TestGlobalFlagToEnableRestAndGraphQLForHostedAndNonHostedEnvir
26002617
{
26012618
JsonContent content = GetPostStartupConfigParams(MSSQL_ENVIRONMENT, configuration, configurationEndpoint);
26022619

2603-
HttpResponseMessage postResult =
2604-
await client.PostAsync(configurationEndpoint, content);
2605-
Assert.AreEqual(HttpStatusCode.OK, postResult.StatusCode);
2606-
2607-
HttpStatusCode restResponseCode = await GetRestResponsePostConfigHydration(client);
2608-
2609-
Assert.AreEqual(expected: expectedStatusCodeForREST, actual: restResponseCode);
2620+
HttpResponseMessage postResult = await client.PostAsync(configurationEndpoint, content);
2621+
Assert.AreEqual(HttpStatusCode.OK, postResult.StatusCode, "The hydration post-response is different from the expected result.");
26102622

2611-
HttpStatusCode graphqlResponseCode = await GetGraphQLResponsePostConfigHydration(client);
2623+
HttpStatusCode restResponseCode = await GetRestResponsePostConfigHydration(client, configuration.Runtime.Rest);
2624+
Assert.AreEqual(expected: expectedStatusCodeForREST, actual: restResponseCode, "The REST hydration post-response is different from the expected result.");
26122625

2613-
Assert.AreEqual(expected: expectedStatusCodeForGraphQL, actual: graphqlResponseCode);
2626+
HttpStatusCode graphqlResponseCode = await GetGraphQLResponsePostConfigHydration(client, configuration.Runtime.GraphQL);
2627+
Assert.AreEqual(expected: expectedStatusCodeForGraphQL, actual: graphqlResponseCode, "The GraphQL hydration post-response is different from the expected result.");
26142628

2629+
// TODO: Issue #3012 - Currently DAB is unable to start MCP with the hydration post-response.
2630+
// This needs to be fixed before uncommenting the MCP check
2631+
// HttpStatusCode mcpResponseCode = await GetMcpResponse(client, configuration.Runtime.Mcp);
2632+
// Assert.AreEqual(expected: expectedStatusCodeForMcp, actual: mcpResponseCode, "The MCP hydration post-response is different from the expected result.");
26152633
}
26162634
}
26172635

@@ -3661,7 +3679,7 @@ public async Task TestSchemaIntrospectionQuery(bool enableIntrospection, bool ex
36613679
using (HttpClient client = server.CreateClient())
36623680
{
36633681
JsonContent content = GetPostStartupConfigParams(MSSQL_ENVIRONMENT, configuration, configurationEndpoint);
3664-
HttpStatusCode responseCode = await HydratePostStartupConfiguration(client, content, configurationEndpoint);
3682+
HttpStatusCode responseCode = await HydratePostStartupConfiguration(client, content, configurationEndpoint, configuration.Runtime.Rest);
36653683

36663684
Assert.AreEqual(expected: HttpStatusCode.OK, actual: responseCode, message: "Configuration hydration failed.");
36673685

@@ -5256,41 +5274,48 @@ private static JsonContent GetPostStartupConfigParams(string environment, Runtim
52565274
/// by executing HTTP requests against the engine until a non-503 error is received.
52575275
/// </summary>
52585276
/// <param name="httpClient">Client used for request execution.</param>
5259-
/// <param name="config">Post-startup configuration</param>
5277+
/// <param name="content">New config file content that will be added to DAB.</param>
5278+
/// <param name="configurationEndpoint">Endpoint through which content will be sent to DAB."</param>
5279+
/// <param name="rest">Global settings used at runtime for REST APIs.</param>
52605280
/// <returns>ServiceUnavailable if service is not successfully hydrated with config</returns>
5261-
private static async Task<HttpStatusCode> HydratePostStartupConfiguration(HttpClient httpClient, JsonContent content, string configurationEndpoint)
5281+
private static async Task<HttpStatusCode> HydratePostStartupConfiguration(HttpClient httpClient, JsonContent content, string configurationEndpoint, RestRuntimeOptions rest)
52625282
{
52635283
// Hydrate configuration post-startup
52645284
HttpResponseMessage postResult =
52655285
await httpClient.PostAsync(configurationEndpoint, content);
52665286
Assert.AreEqual(HttpStatusCode.OK, postResult.StatusCode);
52675287

5268-
return await GetRestResponsePostConfigHydration(httpClient);
5288+
return await GetRestResponsePostConfigHydration(httpClient, rest);
52695289
}
52705290

52715291
/// <summary>
52725292
/// Executing REST requests against the engine until a non-503 error is received.
52735293
/// </summary>
52745294
/// <param name="httpClient">Client used for request execution.</param>
5295+
/// <param name="rest">Global settings used at runtime for REST APIs.</param>
52755296
/// <returns>ServiceUnavailable if service is not successfully hydrated with config,
52765297
/// else the response code from the REST request</returns>
5277-
private static async Task<HttpStatusCode> GetRestResponsePostConfigHydration(HttpClient httpClient)
5298+
private static async Task<HttpStatusCode> GetRestResponsePostConfigHydration(HttpClient httpClient, RestRuntimeOptions rest)
52785299
{
5279-
// Retry request RETRY_COUNT times in 1 second increments to allow required services
5280-
// time to instantiate and hydrate permissions.
5281-
int retryCount = RETRY_COUNT;
5300+
// Retry request RETRY_COUNT times in exponential increments to allow
5301+
// required services time to instantiate and hydrate permissions because
5302+
// the DAB services may take an unpredictable amount of time to become ready.
5303+
//
5304+
// The service might still fail due to the service not being available yet,
5305+
// but it is highly unlikely to be the case.
5306+
int retryCount = 0;
52825307
HttpStatusCode responseCode = HttpStatusCode.ServiceUnavailable;
5283-
while (retryCount > 0)
5308+
while (retryCount < RETRY_COUNT)
52845309
{
52855310
// Spot test authorization resolver utilization to ensure configuration is used.
52865311
HttpResponseMessage postConfigHydrationResult =
5287-
await httpClient.GetAsync($"api/{POST_STARTUP_CONFIG_ENTITY}");
5312+
await httpClient.GetAsync($"{rest.Path}/{POST_STARTUP_CONFIG_ENTITY}");
52885313
responseCode = postConfigHydrationResult.StatusCode;
52895314

52905315
if (postConfigHydrationResult.StatusCode == HttpStatusCode.ServiceUnavailable)
52915316
{
5292-
retryCount--;
5293-
Thread.Sleep(TimeSpan.FromSeconds(RETRY_WAIT_SECONDS));
5317+
retryCount++;
5318+
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(RETRY_WAIT_SECONDS, retryCount)));
52945319
continue;
52955320
}
52965321

@@ -5306,13 +5331,17 @@ private static async Task<HttpStatusCode> GetRestResponsePostConfigHydration(Htt
53065331
/// <param name="httpClient">Client used for request execution.</param>
53075332
/// <returns>ServiceUnavailable if service is not successfully hydrated with config,
53085333
/// else the response code from the GRAPHQL request</returns>
5309-
private static async Task<HttpStatusCode> GetGraphQLResponsePostConfigHydration(HttpClient httpClient)
5334+
private static async Task<HttpStatusCode> GetGraphQLResponsePostConfigHydration(HttpClient httpClient, GraphQLRuntimeOptions graphQL)
53105335
{
5311-
// Retry request RETRY_COUNT times in 1 second increments to allow required services
5312-
// time to instantiate and hydrate permissions.
5313-
int retryCount = RETRY_COUNT;
5336+
// Retry request RETRY_COUNT times in exponential increments to allow
5337+
// required services time to instantiate and hydrate permissions because
5338+
// the DAB services may take an unpredictable amount of time to become ready.
5339+
//
5340+
// The service might still fail due to the service not being available yet,
5341+
// but it is highly unlikely to be the case.
5342+
int retryCount = 0;
53145343
HttpStatusCode responseCode = HttpStatusCode.ServiceUnavailable;
5315-
while (retryCount > 0)
5344+
while (retryCount < RETRY_COUNT)
53165345
{
53175346
string query = @"{
53185347
book_by_pk(id: 1) {
@@ -5324,7 +5353,7 @@ private static async Task<HttpStatusCode> GetGraphQLResponsePostConfigHydration(
53245353

53255354
object payload = new { query };
53265355

5327-
HttpRequestMessage graphQLRequest = new(HttpMethod.Post, "/graphql")
5356+
HttpRequestMessage graphQLRequest = new(HttpMethod.Post, graphQL.Path)
53285357
{
53295358
Content = JsonContent.Create(payload)
53305359
};
@@ -5334,8 +5363,55 @@ private static async Task<HttpStatusCode> GetGraphQLResponsePostConfigHydration(
53345363

53355364
if (responseCode == HttpStatusCode.ServiceUnavailable)
53365365
{
5337-
retryCount--;
5338-
Thread.Sleep(TimeSpan.FromSeconds(RETRY_WAIT_SECONDS));
5366+
retryCount++;
5367+
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(RETRY_WAIT_SECONDS, retryCount)));
5368+
continue;
5369+
}
5370+
5371+
break;
5372+
}
5373+
5374+
return responseCode;
5375+
}
5376+
5377+
/// <summary>
5378+
/// Executing MCP POST requests against the engine until a non-503 error is received.
5379+
/// </summary>
5380+
/// <param name="httpClient">Client used for request execution.</param>
5381+
/// <returns>ServiceUnavailable if service is not successfully hydrated with config,
5382+
/// else the response code from the MCP request</returns>
5383+
public static async Task<HttpStatusCode> GetMcpResponse(HttpClient httpClient, McpRuntimeOptions mcp)
5384+
{
5385+
// Retry request RETRY_COUNT times in exponential increments to allow
5386+
// required services time to instantiate and hydrate permissions because
5387+
// the DAB services may take an unpredictable amount of time to become ready.
5388+
//
5389+
// The service might still fail due to the service not being available yet,
5390+
// but it is highly unlikely to be the case.
5391+
int retryCount = 0;
5392+
HttpStatusCode responseCode = HttpStatusCode.ServiceUnavailable;
5393+
while (retryCount < RETRY_COUNT)
5394+
{
5395+
// Minimal MCP request (list tools) – valid JSON-RPC request
5396+
object payload = new
5397+
{
5398+
jsonrpc = "2.0",
5399+
id = 1,
5400+
method = "tools/list"
5401+
};
5402+
HttpRequestMessage mcpRequest = new(HttpMethod.Post, mcp.Path)
5403+
{
5404+
Content = JsonContent.Create(payload)
5405+
};
5406+
mcpRequest.Headers.Add("Accept", "*/*");
5407+
5408+
HttpResponseMessage mcpResponse = await httpClient.SendAsync(mcpRequest);
5409+
responseCode = mcpResponse.StatusCode;
5410+
5411+
if (responseCode == HttpStatusCode.ServiceUnavailable || responseCode == HttpStatusCode.NotFound)
5412+
{
5413+
retryCount++;
5414+
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(RETRY_WAIT_SECONDS, retryCount)));
53395415
continue;
53405416
}
53415417

0 commit comments

Comments
 (0)