diff --git a/AzureSearchEmulator.IntegrationTests/SemanticQueryIntegrationTests.cs b/AzureSearchEmulator.IntegrationTests/SemanticQueryIntegrationTests.cs new file mode 100644 index 0000000..3c03c8e --- /dev/null +++ b/AzureSearchEmulator.IntegrationTests/SemanticQueryIntegrationTests.cs @@ -0,0 +1,44 @@ +using System.Net; +using System.Text; +using Azure.Search.Documents.Indexes.Models; +using Xunit; + +namespace AzureSearchEmulator.IntegrationTests; + +/// +/// Semantic ranking depends on a hosted reranker, so the emulator refuses it with a 400 rather +/// than silently returning lexical results. +/// +public class SemanticQueryIntegrationTests(EmulatorFactory factory) + : IClassFixture +{ + [Fact] + public async Task SemanticQueryType_IsRejectedWithBadRequest() + { + var indexName = "semantic-" + Guid.NewGuid().ToString("N")[..8]; + var cancellationToken = TestContext.Current.CancellationToken; + + await factory.CreateSearchIndexClient().CreateIndexAsync( + new SearchIndex(indexName) + { + Fields = + { + new SearchField("id", SearchFieldDataType.String) { IsKey = true }, + new SearchField("name", SearchFieldDataType.String) { IsSearchable = true }, + } + }, + cancellationToken); + + using var http = factory.CreateHttpClient(); + + var post = await http.PostAsync( + $"/indexes/{indexName}/docs/search?api-version=2024-07-01", + new StringContent("""{ "search": "x", "queryType": "semantic" }""", Encoding.UTF8, "application/json"), + cancellationToken); + Assert.Equal(HttpStatusCode.BadRequest, post.StatusCode); + Assert.Contains("semantic", await post.Content.ReadAsStringAsync(cancellationToken)); + + var get = await http.GetAsync($"/indexes/{indexName}/docs?search=x&queryType=semantic&api-version=2024-07-01", cancellationToken); + Assert.Equal(HttpStatusCode.BadRequest, get.StatusCode); + } +} diff --git a/AzureSearchEmulator/Controllers/DocumentSearchingController.cs b/AzureSearchEmulator/Controllers/DocumentSearchingController.cs index 8ac28cf..dc7c1ca 100644 --- a/AzureSearchEmulator/Controllers/DocumentSearchingController.cs +++ b/AzureSearchEmulator/Controllers/DocumentSearchingController.cs @@ -127,6 +127,13 @@ public async Task SearchPost(string indexKey, [FromBody] SearchRe ModelState.AddModelError(nameof(request.Skip), "Skip must be between 0 and 100,000"); } + // Semantic ranking needs a Microsoft-hosted reranker that cannot be emulated. Refusing is + // better than quietly returning lexical results a caller would take for reranked ones. + if (string.Equals(request.QueryType, "semantic", StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError(nameof(request.QueryType), "queryType 'semantic' is not supported by the emulator."); + } + if (!ModelState.IsValid) { return BadRequest(ModelState);