diff --git a/Microsoft.Azure.Cosmos/src/CosmosClient.cs b/Microsoft.Azure.Cosmos/src/CosmosClient.cs index bf79ba3532..643bb3e505 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClient.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClient.cs @@ -5,11 +5,12 @@ namespace Microsoft.Azure.Cosmos { using System; + using System.Text; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Cosmos.Query; using Microsoft.Azure.Documents; - using System.Text; /// /// Provides a client-side logical representation of the Azure Cosmos DB database account. @@ -202,7 +203,7 @@ internal CosmosClient( internal CosmosOffers Offers => this.offerSet.Value; internal DocumentClient DocumentClient { get; set; } - internal CosmosRequestHandler RequestHandler { get; private set; } + internal RequestInvokerHandler RequestHandler { get; private set; } internal ConsistencyLevel AccountConsistencyLevel { get; private set; } internal CosmosResponseFactory ResponseFactory => diff --git a/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs b/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs index ff8ee95a87..6b967a24c7 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/ClientPipelineBuilder.cs @@ -57,9 +57,9 @@ private set internal CosmosRequestHandler PartitionKeyRangeHandler { get; set; } - public CosmosRequestHandler Build() + public RequestInvokerHandler Build() { - CosmosRequestHandler root = new RequestInvokerHandler(this.client); + RequestInvokerHandler root = new RequestInvokerHandler(this.client); CosmosRequestHandler current = root; if (this.CustomHandlers != null && this.CustomHandlers.Any()) diff --git a/Microsoft.Azure.Cosmos/src/Handler/CosmosRequestMessage.cs b/Microsoft.Azure.Cosmos/src/Handler/CosmosRequestMessage.cs index b99cebf29a..f29a2796e1 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/CosmosRequestMessage.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/CosmosRequestMessage.cs @@ -155,8 +155,7 @@ internal async Task AssertPartitioningDetailsAsync(CosmosClient client, Cancella { Debug.Assert(this.AssertPartitioningPropertiesAndHeaders()); } -#endif -#if !DEBUG +#else await Task.CompletedTask; #endif } diff --git a/Microsoft.Azure.Cosmos/src/Handler/RequestInvokerHandler.cs b/Microsoft.Azure.Cosmos/src/Handler/RequestInvokerHandler.cs index bd6cd3dd0b..22e427b3be 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/RequestInvokerHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/RequestInvokerHandler.cs @@ -4,12 +4,15 @@ namespace Microsoft.Azure.Cosmos.Handlers { - using Microsoft.Azure.Cosmos.Internal; - using Microsoft.Azure.Documents; using System; using System.Globalization; + using System.IO; + using System.Net; + using System.Net.Http; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Routing; /// /// HttpMessageHandler can only be invoked by derived classed or internal classes inside http assembly @@ -82,6 +85,127 @@ public override Task SendAsync( .Unwrap(); } + public virtual async Task SendAsync( + Uri resourceUri, + ResourceType resourceType, + OperationType operationType, + CosmosRequestOptions requestOptions, + CosmosContainerCore cosmosContainerCore, + Object partitionKey, + Stream streamPayload, + Action requestEnricher, + Func responseCreator, + CancellationToken cancellation = default(CancellationToken)) + { + if (responseCreator == null) + { + throw new ArgumentNullException(nameof(responseCreator)); + } + + CosmosResponseMessage responseMessage = await this.SendAsync( + resourceUri: resourceUri, + resourceType: resourceType, + operationType: operationType, + requestOptions: requestOptions, + cosmosContainerCore: cosmosContainerCore, + partitionKey: partitionKey, + streamPayload: streamPayload, + requestEnricher: requestEnricher, + cancellation: cancellation); + + return responseCreator(responseMessage); + } + + public virtual async Task SendAsync( + Uri resourceUri, + ResourceType resourceType, + OperationType operationType, + CosmosRequestOptions requestOptions, + CosmosContainerCore cosmosContainerCore, + Object partitionKey, + Stream streamPayload, + Action requestEnricher, + CancellationToken cancellation = default(CancellationToken)) + { + if (resourceUri == null) + { + throw new ArgumentNullException(nameof(resourceUri)); + } + + HttpMethod method = RequestInvokerHandler.GetHttpMethod(operationType); + + CosmosRequestMessage request = new CosmosRequestMessage(method, resourceUri); + request.OperationType = operationType; + request.ResourceType = resourceType; + request.RequestOptions = requestOptions; + request.Content = streamPayload; + + if (partitionKey != null) + { + if (cosmosContainerCore == null && Object.ReferenceEquals(partitionKey, CosmosContainerSettings.NonePartitionKeyValue)) + { + throw new ArgumentException($"{nameof(cosmosContainerCore)} can not be null with partition key as PartitionKey.None"); + } + else if (Object.ReferenceEquals(partitionKey, CosmosContainerSettings.NonePartitionKeyValue)) + { + try + { + PartitionKeyInternal partitionKeyInternal = await cosmosContainerCore.GetNonePartitionKeyValueAsync(cancellation); + request.Headers.PartitionKey = partitionKeyInternal.ToJsonString(); + } + catch (DocumentClientException dce) + { + return dce.ToCosmosResponseMessage(request); + } + } + else + { + PartitionKey pk = new PartitionKey(partitionKey); + request.Headers.PartitionKey = pk.InternalKey.ToJsonString(); + } + } + + if (operationType == OperationType.Upsert) + { + request.Headers.IsUpsert = bool.TrueString; + } + + requestEnricher?.Invoke(request); + return await this.SendAsync(request, cancellation); + } + + internal static HttpMethod GetHttpMethod( + OperationType operationType) + { + HttpMethod httpMethod = HttpMethod.Head; + if (operationType == OperationType.Create || + operationType == OperationType.Upsert || + operationType == OperationType.Query || + operationType == OperationType.SqlQuery || + operationType == OperationType.Batch || + operationType == OperationType.ExecuteJavaScript) + { + return HttpMethod.Post; + } + else if (operationType == OperationType.Read || + operationType == OperationType.ReadFeed) + { + return HttpMethod.Get; + } + else if (operationType == OperationType.Replace) + { + return HttpMethod.Put; + } + else if (operationType == OperationType.Delete) + { + return HttpMethod.Delete; + } + else + { + throw new NotImplementedException(); + } + } + private void FillMultiMasterContext(CosmosRequestMessage request) { if (this.client.DocumentClient.UseMultipleWriteLocations) diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/CosmosContainerCore.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/CosmosContainerCore.cs index f60ede5766..ec9e15af8c 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/CosmosContainerCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/CosmosContainerCore.cs @@ -190,7 +190,7 @@ internal Task ReplaceProvisionedThroughputIfExistsAsync( internal async Task GetCachedContainerSettingsAsync(CancellationToken cancellationToken = default(CancellationToken)) { ClientCollectionCache collectionCache = await this.ClientContext.DocumentClient.GetCollectionCacheAsync(); - return await collectionCache.GetByNameAsync(HttpConstants.Versions.CurrentVersion, this.LinkUri.OriginalString, cancellationToken); + return await collectionCache.ResolveByNameAsync(HttpConstants.Versions.CurrentVersion, this.LinkUri.OriginalString, cancellationToken); } // Name based look-up, needs re-computation and can't be cached @@ -213,10 +213,12 @@ internal Task GetRID(CancellationToken cancellationToken) /// The function selects the right partition key constant for inserting documents that don't have /// a value for partition key. The constant selection is based on whether the collection is migrated /// or user partitioned + /// + /// For non-existing container will throw with 404 as status code /// - internal async Task GetNonePartitionKeyValue(CancellationToken cancellationToken = default(CancellationToken)) + internal async Task GetNonePartitionKeyValueAsync(CancellationToken cancellation = default(CancellationToken)) { - CosmosContainerSettings containerSettings = await this.GetCachedContainerSettingsAsync(cancellationToken); + CosmosContainerSettings containerSettings = await this.GetCachedContainerSettingsAsync(cancellation); return containerSettings.GetNoneValue(); } diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs index 7442442b6c..e01f17e9d5 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContext.cs @@ -10,6 +10,7 @@ namespace Microsoft.Azure.Cosmos using System.Text; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Cosmos.Query; using Microsoft.Azure.Documents; @@ -32,7 +33,7 @@ internal abstract class CosmosClientContext internal abstract CosmosResponseFactory ResponseFactory { get; } - internal abstract CosmosRequestHandler RequestHandler { get; } + internal abstract RequestInvokerHandler RequestHandler { get; } internal abstract CosmosClientConfiguration ClientConfiguration { get; } diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContextCore.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContextCore.cs index 97f9c33cbb..287cf49362 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContextCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosClientContextCore.cs @@ -5,11 +5,11 @@ namespace Microsoft.Azure.Cosmos { using System; - using System.Globalization; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Cosmos.Query; using Microsoft.Azure.Documents; @@ -20,7 +20,7 @@ internal CosmosClientContextCore( CosmosClientConfiguration clientConfiguration, CosmosJsonSerializer cosmosJsonSerializer, CosmosResponseFactory cosmosResponseFactory, - CosmosRequestHandler requestHandler, + RequestInvokerHandler requestHandler, DocumentClient documentClient, IDocumentQueryClient documentQueryClient) { @@ -46,7 +46,7 @@ internal CosmosClientContextCore( internal override CosmosResponseFactory ResponseFactory { get; } - internal override CosmosRequestHandler RequestHandler { get; } + internal override RequestInvokerHandler RequestHandler { get; } internal override CosmosClientConfiguration ClientConfiguration { get; } @@ -94,17 +94,16 @@ internal override Task ProcessResourceOperationStreamAsyn Action requestEnricher, CancellationToken cancellationToken) { - return ExecUtils.ProcessResourceOperationStreamAsync( - requestHandler: this.RequestHandler, - cosmosContainerCore: cosmosContainerCore, + return this.RequestHandler.SendAsync( resourceUri: resourceUri, resourceType: resourceType, operationType: operationType, requestOptions: requestOptions, + cosmosContainerCore: cosmosContainerCore, partitionKey: partitionKey, streamPayload: streamPayload, requestEnricher: requestEnricher, - cancellationToken: cancellationToken); + cancellation: cancellationToken); } internal override Task ProcessResourceOperationAsync( @@ -117,10 +116,9 @@ internal override Task ProcessResourceOperationAsync( Stream streamPayload, Action requestEnricher, Func responseCreator, - CancellationToken cancellationToken) + CancellationToken cancellation) { - return ExecUtils.ProcessResourceOperationAsync( - requestHandler: this.RequestHandler, + return this.RequestHandler.SendAsync( resourceUri: resourceUri, resourceType: resourceType, operationType: operationType, @@ -130,7 +128,7 @@ internal override Task ProcessResourceOperationAsync( streamPayload: streamPayload, requestEnricher: requestEnricher, responseCreator: responseCreator, - cancellationToken: cancellationToken); + cancellation: cancellation); } } } diff --git a/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs b/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs index b0c3799efd..212365d69e 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/ClientCollectionCache.cs @@ -45,7 +45,7 @@ protected override Task GetByRidAsync(string apiVersion cancellationToken); } - internal override Task GetByNameAsync(string apiVersion, string resourceAddress, CancellationToken cancellationToken) + protected override Task GetByNameAsync(string apiVersion, string resourceAddress, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); IDocumentClientRetryPolicy retryPolicyInstance = new ClearingSessionContainerClientRetryPolicy(this.sessionContainer, this.retryPolicy.GetRequestPolicy()); diff --git a/Microsoft.Azure.Cosmos/src/Routing/CollectionCache.cs b/Microsoft.Azure.Cosmos/src/Routing/CollectionCache.cs index f39196d833..5653953f22 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/CollectionCache.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/CollectionCache.cs @@ -187,7 +187,7 @@ public void Refresh(string resourceAddress, string apiVersion = null) protected abstract Task GetByRidAsync(string apiVersion, string collectionRid, CancellationToken cancellationToken); - internal abstract Task GetByNameAsync(string apiVersion, string resourceAddress, CancellationToken cancellationToken); + protected abstract Task GetByNameAsync(string apiVersion, string resourceAddress, CancellationToken cancellationToken); private async Task ResolveByPartitionKeyRangeIdentityAsync(string apiVersion, PartitionKeyRangeIdentity partitionKeyRangeIdentity, CancellationToken cancellationToken) { @@ -234,7 +234,7 @@ private Task ResolveByRidAsync( cancellationToken); } - private async Task ResolveByNameAsync( + internal virtual async Task ResolveByNameAsync( string apiVersion, string resourceAddress, CancellationToken cancellationToken) diff --git a/Microsoft.Azure.Cosmos/src/Util/ExecUtils.cs b/Microsoft.Azure.Cosmos/src/Util/ExecUtils.cs index f115d6003c..fcbaebbdd1 100644 --- a/Microsoft.Azure.Cosmos/src/Util/ExecUtils.cs +++ b/Microsoft.Azure.Cosmos/src/Util/ExecUtils.cs @@ -6,10 +6,9 @@ namespace Microsoft.Azure.Cosmos { using System; using System.IO; - using System.Net.Http; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Documents.Routing; + using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Documents; internal static class ExecUtils @@ -122,7 +121,7 @@ internal static Task ProcessResourceOperationAsync( } internal static async Task ProcessResourceOperationAsync( - CosmosRequestHandler requestHandler, + RequestInvokerHandler requestHandler, Uri resourceUri, ResourceType resourceType, OperationType operationType, @@ -149,7 +148,7 @@ internal static async Task ProcessResourceOperationAsync( throw new ArgumentNullException(nameof(responseCreator)); } - CosmosRequestMessage request = await ExecUtils.GenerateCosmosRequestMessage( + CosmosResponseMessage response = await requestHandler.SendAsync( resourceUri, resourceType, operationType, @@ -159,141 +158,7 @@ internal static async Task ProcessResourceOperationAsync( streamPayload, requestEnricher); - CosmosResponseMessage response = await requestHandler.SendAsync(request, cancellationToken); return responseCreator(response); } - - /// - /// Used internally by friends ensure robust argument and - /// exception-less handling - /// - internal static Task ProcessResourceOperationAsync( - CosmosClient client, - Uri resourceUri, - ResourceType resourceType, - OperationType operationType, - CosmosRequestOptions requestOptions, - Object partitionKey, - Stream streamPayload, - Action requestEnricher, - Func responseCreator, - CancellationToken cancellationToken) - { - return ProcessResourceOperationAsync( - requestHandler: client.RequestHandler, - resourceUri: resourceUri, - resourceType: resourceType, - operationType: operationType, - requestOptions: requestOptions, - cosmosContainerCore: null, - partitionKey: partitionKey, - streamPayload: streamPayload, - requestEnricher: requestEnricher, - responseCreator: responseCreator, - cancellationToken: cancellationToken); - } - - internal static async Task ProcessResourceOperationStreamAsync( - CosmosRequestHandler requestHandler, - Uri resourceUri, - ResourceType resourceType, - OperationType operationType, - CosmosRequestOptions requestOptions, - CosmosContainerCore cosmosContainerCore, - Object partitionKey, - Stream streamPayload, - Action requestEnricher, - CancellationToken cancellationToken) - { - CosmosRequestMessage request = await ExecUtils.GenerateCosmosRequestMessage( - resourceUri, - resourceType, - operationType, - requestOptions, - cosmosContainerCore, - partitionKey, - streamPayload, - requestEnricher); - - return await requestHandler.SendAsync(request, cancellationToken); - } - - private static async Task GenerateCosmosRequestMessage( - Uri resourceUri, - ResourceType resourceType, - OperationType operationType, - CosmosRequestOptions requestOptions, - CosmosContainerCore cosmosContainerCore, - Object partitionKey, - Stream streamPayload, - Action requestEnricher) - { - HttpMethod method = ExecUtils.GetHttpMethod(operationType); - - CosmosRequestMessage request = new CosmosRequestMessage(method, resourceUri); - request.OperationType = operationType; - request.ResourceType = resourceType; - request.RequestOptions = requestOptions; - request.Content = streamPayload; - - if (partitionKey != null) - { - if (cosmosContainerCore == null && partitionKey.Equals(PartitionKey.None)) - { - throw new ArgumentException($"{nameof(cosmosContainerCore)} can not be null with partition key as PartitionKey.None"); - } - else if (partitionKey.Equals(PartitionKey.None)) - { - PartitionKeyInternal partitionKeyInternal = await cosmosContainerCore.GetNonePartitionKeyValue(); - request.Headers.PartitionKey = partitionKeyInternal.ToJsonString(); - } - else - { - PartitionKey pk = new PartitionKey(partitionKey); - request.Headers.PartitionKey = pk.InternalKey.ToJsonString(); - } - } - - if (operationType == OperationType.Upsert) - { - request.Headers.IsUpsert = bool.TrueString; - } - - requestEnricher?.Invoke(request); - - return request; - } - - internal static HttpMethod GetHttpMethod( - OperationType operationType) - { - HttpMethod httpMethod = HttpMethod.Head; - if (operationType == OperationType.Create || - operationType == OperationType.Upsert || - operationType == OperationType.Query || - operationType == OperationType.SqlQuery || - operationType == OperationType.Batch || - operationType == OperationType.ExecuteJavaScript) - { - return HttpMethod.Post; - } - else if (operationType == OperationType.Read || - operationType == OperationType.ReadFeed) - { - return HttpMethod.Get; - } - else if (operationType == OperationType.Replace) - { - return HttpMethod.Put; - } - else if (operationType == OperationType.Delete) - { - return HttpMethod.Delete; - } - else - { - throw new NotImplementedException(); - } - } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs index 047008f965..7805534b7b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using System.Net; using System.Net.Http; using System.Text; + using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Json; using Microsoft.Azure.Cosmos.Query; @@ -22,6 +23,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; + using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.TransportWrapperTests; using JsonReader = Json.JsonReader; using JsonWriter = Json.JsonWriter; @@ -92,6 +94,109 @@ public async Task CreateDropItemUndefinedPartitionKeyTest() Assert.IsNotNull(deleteResponse); } + [TestMethod] + public async Task ReadCollectionNotExists() + { + string collectionName = Guid.NewGuid().ToString(); + CosmosContainer testContainer = this.database.Containers[collectionName]; + await CosmosItemTests.TestNonePKForNonExistingContainer(testContainer); + + // Item -> Container -> Database contract + string dbName = Guid.NewGuid().ToString(); + testContainer = this.cosmosClient.Databases[dbName].Containers[collectionName]; + await CosmosItemTests.TestNonePKForNonExistingContainer(testContainer); + } + + [TestMethod] + public async Task NonPartitionKeyLookupCacheTest() + { + int count = 0; + CosmosClient client = TestCommon.CreateCosmosClient(builder => + { + builder.UseConnectionModeDirect(); + builder.UseSendingRequestEventArgs((sender, e) => + { + if (e.DocumentServiceRequest != null) + { + Trace.TraceInformation($"{e.DocumentServiceRequest.ToString()}"); + } + + if (e.HttpRequest != null) + { + Trace.TraceInformation($"{e.HttpRequest.ToString()}"); + } + + if (e.IsHttpRequest() + && e.HttpRequest.RequestUri.AbsolutePath.Contains("/colls/")) + { + count++; + } + + if (e.IsHttpRequest() + && e.HttpRequest.RequestUri.AbsolutePath.Contains("/pkranges")) + { + Debugger.Break(); + } + }); + }); + + string dbName = Guid.NewGuid().ToString(); + string containerName = Guid.NewGuid().ToString(); + CosmosContainerCore testContainer = (CosmosContainerCore)client.Databases[dbName].Containers[containerName]; + + int loopCount = 2; + for (int i = 0; i < loopCount; i++) + { + try + { + await testContainer.GetNonePartitionKeyValueAsync(); + Assert.Fail(); + } + catch (DocumentClientException dce) when (dce.StatusCode == HttpStatusCode.NotFound) + { + } + } + + Assert.AreEqual(loopCount, count); + + // Create real container and address + CosmosDatabase db = await client.Databases.CreateDatabaseAsync(dbName); + CosmosContainer container = await db.Containers.CreateContainerAsync(containerName, "/id"); + + // reset counter + count = 0; + for (int i = 0; i < loopCount; i++) + { + await testContainer.GetNonePartitionKeyValueAsync(); + } + + // expected once post create + Assert.AreEqual(1, count); + + // reset counter + count = 0; + for (int i = 0; i < loopCount; i++) + { + await testContainer.GetRID(default(CancellationToken)); + } + + // Already cached by GetNonePartitionKeyValueAsync before + Assert.AreEqual(0, count); + + // reset counter + count = 0; + int expected = 0; + for (int i = 0; i < loopCount; i++) + { + await testContainer.GetRoutingMapAsync(default(CancellationToken)); + expected = count; + } + + // OkRagnes should be fetched only once. + // Possible to make multiple calls for ranges + Assert.AreEqual(expected, count); + } + [TestMethod] public async Task CreateDropItemStreamTest() { @@ -1024,5 +1129,19 @@ private ToDoActivityAfterMigration CreateRandomToDoActivityAfterMigration(string cost = double.MaxValue }; } + + private static async Task TestNonePKForNonExistingContainer(CosmosContainer cosmosContainer) + { + // Stream implementation should not throw + CosmosResponseMessage response = await cosmosContainer.Items.ReadItemStreamAsync(CosmosContainerSettings.NonePartitionKeyValue, "id1"); + Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); + Assert.IsNotNull(response.Headers.ActivityId); + Assert.IsNotNull(response.ErrorMessage); + + // FOr typed also its not error + var typedResponse = await cosmosContainer.Items.ReadItemAsync(CosmosContainerSettings.NonePartitionKeyValue, "id1"); + Assert.AreEqual(HttpStatusCode.NotFound, typedResponse.StatusCode); + Assert.IsNotNull(typedResponse.Headers.ActivityId); + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs index 0e5e3a738d..457238c800 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs @@ -59,7 +59,7 @@ private class TestPayload public string id { get; set; } } - private class TransportClientWrapper : TransportClient + internal class TransportClientWrapper : TransportClient { private readonly TransportClient baseClient; private readonly Action interceptor;