@@ -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