From 8527bc25a967ef66abc46d8281168b34ebaae550 Mon Sep 17 00:00:00 2001 From: Alex Shovlin Date: Tue, 16 Jul 2024 12:25:08 -0400 Subject: [PATCH 1/4] fix: Delay the cast from IAmazonDynamoDB to AmazonDynamoDBClient in Amazon.DynamoDBv2.DocumentModel to only when it's necessary for sync-over-async code paths. This allows mocking for async callers. --- .../Custom/DocumentModel/DocumentBatchGet.cs | 23 +- .../DocumentModel/DocumentBatchWrite.cs | 21 +- .../DocumentModel/DocumentTransactGet.cs | 14 +- .../DocumentModel/DocumentTransactWrite.cs | 15 +- .../DynamoDBv2/Custom/DocumentModel/Search.cs | 31 +- .../DynamoDBv2/Custom/DocumentModel/Table.cs | 84 +++- .../UnitTests/Services/DynamoDBTests.cs | 179 +++++++++ .../UnitTests/Custom/DynamoDBTests.cs | 35 -- .../UnitTests/Custom/MockingTests.cs | 372 ++++++++++++++++++ 9 files changed, 695 insertions(+), 79 deletions(-) create mode 100644 sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs index 59eddee6e49b..696e5b51c4e1 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs @@ -427,15 +427,22 @@ private Results GetAttributeItems() return results; } -#if NETSTANDARD - private static void CallUntilCompletion(AmazonDynamoDBClient client, BatchGetItemRequest request, Results allResults) -#else private static void CallUntilCompletion(IAmazonDynamoDB client, BatchGetItemRequest request, Results allResults) -#endif { do { - var serviceResponse = client.BatchGetItem(request); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = client as AmazonDynamoDBClient; + if (internalClient == null) + { + throw new InvalidOperationException("Calling the synchronous DocumentBatchGet.Execute() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); + } +#else + internalClient = DDBClient; +#endif + var serviceResponse = internalClient.BatchGetItem(request); foreach (var kvp in serviceResponse.Responses) { @@ -448,12 +455,8 @@ private static void CallUntilCompletion(IAmazonDynamoDB client, BatchGetItemRequ } while (request.RequestItems.Count > 0); } -#if AWS_ASYNC_API -#if NETSTANDARD - private static async Task CallUntilCompletionAsync(AmazonDynamoDBClient client, BatchGetItemRequest request, Results allResults, CancellationToken cancellationToken) -#else +#if AWS_ASYNC_API private static async Task CallUntilCompletionAsync(IAmazonDynamoDB client, BatchGetItemRequest request, Results allResults, CancellationToken cancellationToken) -#endif { do { diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs index 5f5959d526fd..1b2a1441ca7d 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs @@ -469,15 +469,22 @@ private static Dictionary> GetNextWriteI return nextItems; } -#if NETSTANDARD - private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, AmazonDynamoDBClient client) -#else private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, IAmazonDynamoDB client) -#endif { do { - var result = client.BatchWriteItem(request); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = client as AmazonDynamoDBClient; + if (internalClient == null) + { + throw new InvalidOperationException("Calling the synchronous DocumentBatchWrite.Execute() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); + } +#else + internalClient = DDBClient; +#endif + var result = internalClient.BatchWriteItem(request); request.RequestItems = result.UnprocessedItems; Dictionary unprocessedDocuments = new Dictionary(keyComparer); @@ -529,11 +536,7 @@ private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, AmazonDynamoDBClient client, CancellationToken cancellationToken) -#else private async Task CallUntilCompletionAsync(BatchWriteItemRequest request, Dictionary> documentMap, IAmazonDynamoDB client, CancellationToken cancellationToken) -#endif { do { diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs index 32f82e2943aa..6d4dfc1813f4 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs @@ -307,8 +307,18 @@ private Dictionary> GetItemsHelper() if (Items == null || !Items.Any()) return new Dictionary>(); var request = ConstructRequest(isAsync: false); - var dynamoDbClient = Items[0].TransactionPart.TargetTable.DDBClient; - var response = dynamoDbClient.TransactGetItems(request); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = Items[0].TransactionPart.TargetTable.DDBClient as AmazonDynamoDBClient; + if (internalClient == null) + { + throw new InvalidOperationException("Calling the synchronous DocumentBatchGet.Execute() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); + } +#else + internalClient = Items[0].TransactionPart.TargetTable.DDBClient; +#endif + var response = internalClient.TransactGetItems(request); return GetDocuments(response.Responses); } diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs index 28d2f3e484a5..fb89d960cebe 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs @@ -598,8 +598,19 @@ private void WriteItemsHelper() try { - var dynamoDbClient = Items[0].TransactionPart.TargetTable.DDBClient; - dynamoDbClient.TransactWriteItems(request); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = Items[0].TransactionPart.TargetTable.DDBClient as AmazonDynamoDBClient; + if (internalClient == null) + { + throw new InvalidOperationException("Calling the synchronous DocumentTransactWrite.Execute() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); + } +#else + internalClient = DDBClient; +#endif + + internalClient.TransactWriteItems(request); } catch (TransactionCanceledException ex) { diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs index 44cfe23415cf..baf6de0db1c5 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs @@ -212,6 +212,18 @@ internal List GetNextSetHelper() { List ret = new List(); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = SourceTable.DDBClient as AmazonDynamoDBClient; + if (internalClient == null) + { + throw new InvalidOperationException("Calling the synchronous GetNextSet() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling GetNextSetAsync instead."); + } +#else + var internalClient = SourceTable.DDBClient; +#endif + if (!IsDone) { switch (SearchMethod) @@ -253,7 +265,7 @@ internal List GetNextSetHelper() SourceTable.AddRequestHandler(scanReq, isAsync: false); - var scanResult = SourceTable.DDBClient.Scan(scanReq); + var scanResult = internalClient.Scan(scanReq); foreach (var item in scanResult.Items) { Document doc = SourceTable.FromAttributeMap(item); @@ -303,7 +315,7 @@ internal List GetNextSetHelper() SourceTable.AddRequestHandler(queryReq, isAsync: false); - var queryResult = SourceTable.DDBClient.Query(queryReq); + var queryResult = internalClient.Query(queryReq); foreach (var item in queryResult.Items) { Document doc = SourceTable.FromAttributeMap(item); @@ -532,6 +544,17 @@ private int GetCount() } else { +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = SourceTable.DDBClient as AmazonDynamoDBClient; + if (internalClient == null) + { + throw new InvalidOperationException("Accessing the synchronous Count from .NET or .NET Core requires " + + "initializing the Table with an actual AmazonDynamoDBClient."); + } +#else + var internalClient = DDBClient; +#endif switch (SearchMethod) { case SearchType.Scan: @@ -558,7 +581,7 @@ private int GetCount() SourceTable.AddRequestHandler(scanReq, isAsync: false); - var scanResult = SourceTable.DDBClient.Scan(scanReq); + var scanResult = internalClient.Scan(scanReq); count = Matches.Count + scanResult.Count.GetValueOrDefault(); return count; case SearchType.Query: @@ -583,7 +606,7 @@ private int GetCount() SourceTable.AddRequestHandler(queryReq, isAsync: false); - var queryResult = SourceTable.DDBClient.Query(queryReq); + var queryResult = internalClient.Query(queryReq); count = Matches.Count + queryResult.Count.GetValueOrDefault(); return count; default: diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs index 275f2aef3c6e..fc6546ad1662 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs @@ -54,12 +54,7 @@ internal enum DynamoDBConsumer internal bool IsEmptyStringValueEnabled { get { return Config.IsEmptyStringValueEnabled; } } internal IEnumerable StoreAsEpoch { get { return Config.AttributesToStoreAsEpoch; } } internal IEnumerable KeyNames { get { return Keys.Keys; } } - -#if NETSTANDARD - internal AmazonDynamoDBClient DDBClient { get; private set; } -#else internal IAmazonDynamoDB DDBClient { get; private set; } -#endif #endregion @@ -366,7 +361,19 @@ private TableDescription DescribeTable(string tableName) }; this.AddRequestHandler(req, isAsync: false); - var info = this.DDBClient.DescribeTable(req); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var client = DDBClient as AmazonDynamoDBClient; + if (client == null) + { + throw new InvalidOperationException("Calling the synchronous LoadTable from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when creating a Table via TableBuilder instead."); + } +#else + var client = DDBClient; +#endif + + var info = client.DescribeTable(req); if (info.Table == null) { @@ -413,11 +420,7 @@ internal Table(IAmazonDynamoDB ddbClient, TableConfig config) if (ddbClient == null) throw new ArgumentNullException("ddbClient"); -#if NETSTANDARD - DDBClient = ddbClient as AmazonDynamoDBClient; -#else DDBClient = ddbClient; -#endif Config = config; } @@ -938,7 +941,19 @@ internal Document PutItemHelper(Document doc, PutItemOperationConfig config) currentConfig.ConditionalExpression.ApplyExpression(req, this); } - var resp = DDBClient.PutItem(req); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var client = DDBClient as AmazonDynamoDBClient; + if (client == null) + { + throw new InvalidOperationException("Calling the synchronous PutItem from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when creating a Table via PutItemAsync instead."); + } +#else + var client = DDBClient; +#endif + + var resp = client.PutItem(req); doc.CommitChanges(); Document ret = null; @@ -1013,10 +1028,22 @@ internal Document GetItemHelper(Key key, GetItemOperationConfig config) this.AddRequestHandler(request, isAsync: false); +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var client = DDBClient as AmazonDynamoDBClient; + if (client == null) + { + throw new InvalidOperationException("Calling the synchronous GetItem from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when creating a Table via GetItemAsync instead."); + } +#else + var client = DDBClient; +#endif + if (currentConfig.AttributesToGet != null) request.AttributesToGet = currentConfig.AttributesToGet; - var result = DDBClient.GetItem(request); + var result = client.GetItem(request); var attributeMap = result.Item; if (attributeMap == null || attributeMap.Count == 0) return null; @@ -1047,7 +1074,7 @@ internal async Task GetItemHelperAsync(Key key, GetItemOperationConfig } #endif - #endregion +#endregion #region UpdateItem @@ -1058,7 +1085,7 @@ internal Document UpdateHelper(Document doc, Primitive hashKey, Primitive rangeK return UpdateHelper(doc, key, config); } -#if AWS_ASYNC_API +#if AWS_ASYNC_API internal Task UpdateHelperAsync(Document doc, Primitive hashKey, Primitive rangeKey, UpdateItemOperationConfig config, CancellationToken cancellationToken) { Key key = (hashKey != null || rangeKey != null) ? MakeKey(hashKey, rangeKey) : MakeKey(doc); @@ -1132,8 +1159,19 @@ internal Document UpdateHelper(Document doc, Key key, UpdateItemOperationConfig req.ExpressionAttributeNames.Add(kvp.Key, kvp.Value); } } +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var client = DDBClient as AmazonDynamoDBClient; + if (client == null) + { + throw new InvalidOperationException("Calling the synchronous UpdateItem from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when creating a Table via UpdateItemAsync instead."); + } +#else + var client = DDBClient; +#endif - var resp = DDBClient.UpdateItem(req); + var resp = client.UpdateItem(req); var returnedAttributes = resp.Attributes; doc.CommitChanges(); @@ -1145,7 +1183,7 @@ internal Document UpdateHelper(Document doc, Key key, UpdateItemOperationConfig return ret; } -#if AWS_ASYNC_API +#if AWS_ASYNC_API internal async Task UpdateHelperAsync(Document doc, Key key, UpdateItemOperationConfig config, CancellationToken cancellationToken) { var currentConfig = config ?? new UpdateItemOperationConfig(); @@ -1275,7 +1313,19 @@ internal Document DeleteHelper(Key key, DeleteItemOperationConfig config) currentConfig.ConditionalExpression.ApplyExpression(req, this); } - var attributes = DDBClient.DeleteItem(req).Attributes; +#if AWS_ASYNC_API + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var client = DDBClient as AmazonDynamoDBClient; + if (client == null) + { + throw new InvalidOperationException("Calling the synchronous DeleteItem from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling DeleteItemAsync instead."); + } +#else + var client = DDBClient; +#endif + + var attributes = client.DeleteItem(req).Attributes; Document ret = null; if (currentConfig.ReturnValues == ReturnValues.AllOldAttributes) diff --git a/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs b/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs index a96fc902c6ef..e0b897b0ae12 100644 --- a/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs +++ b/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs @@ -1,5 +1,11 @@ using Xunit; using Amazon.DynamoDBv2.Model; +using System.Threading.Tasks; +using Moq; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DocumentModel; +using System.Threading; +using System; namespace AWSSDK_NetStandard.UnitTests { @@ -29,5 +35,178 @@ public void AttributeValueIsBOOLSetTest() Assert.False(boolAV.IsBOOLSet); Assert.False(boolAV.BOOL.HasValue); } + + #region MockingTests - these are similar to AWSSDK_DotNet.UnitTests.MockingTests, if we unify .NET Framework 4.5 and .NET tests they can be de-duplicated + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_PutItemAsync() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + // This calls the low-level PutItemAsync, which should be valid on the mocked client + await table.PutItemAsync(document); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_GetItemAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.GetItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetItemResponse { Item = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level GetItemAsync, which should be valid on the mocked client + await table.GetItemAsync("123"); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_UpdateItemAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.UpdateItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new UpdateItemResponse { Attributes = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + // This calls the low-level UpdateItemAsync, which should be valid on the mocked client + await table.UpdateItemAsync(document, "123"); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_DeleteItemAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.DeleteItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DeleteItemResponse { Attributes = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level DeleteItemAsync, which should be valid on the mocked client + await table.DeleteItemAsync("123"); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_QueryAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.QueryAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new QueryResponse { Items = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level QueryAsync, which should be valid on the mocked client + await table.Query("123", new QueryFilter()).GetNextSetAsync(); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_ScanAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.ScanAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ScanResponse { Items = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level ScanAsync, which should be valid on the mocked client + await table.Scan(new ScanOperationConfig()).GetNextSetAsync(); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_BatchGetAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.BatchGetItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new BatchGetItemResponse { Responses = new(), UnprocessedKeys = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var batchGet = table.CreateBatchGet(); + batchGet.AddKey("123"); + + // This calls the low-level BatchGetItemAsync, which should be valid on the mocked client + await batchGet.ExecuteAsync(); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_BatchWriteAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.BatchWriteItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new BatchWriteItemResponse { UnprocessedItems = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + + var document = new Document(); + document["Id"] = "123"; + + var batchWrite = table.CreateBatchWrite(); + batchWrite.AddDocumentToPut(document); + + // This calls the low-level BatchWriteItemAsync, which should be valid on the mocked client + await batchWrite.ExecuteAsync(); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_TransactGetAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.TransactGetItemsAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new TransactGetItemsResponse { Responses = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var transactGet = table.CreateTransactGet(); + transactGet.AddKey("123"); + + // This calls the low-level TransactGetItemsAsync, which should be valid on the mocked client + await transactGet.ExecuteAsync(); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public async Task TestMockingTableClient_TransactWriteAsync() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + var transactWrite = table.CreateTransactWrite(); + transactWrite.AddDocumentToPut(document); + + // This calls the low-level TransactWriteItemsAsync, which should be valid on the mocked client + await transactWrite.ExecuteAsync(); + } + + [Fact] + [Trait("Category", "DynamoDBv2")] + public void TestMockingTableClient_LoadTable_Throws() + { + var mockClient = new Mock(); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync LoadTable + // that this still relies on, so we expect it to fail + Assert.Throws(() => Table.LoadTable(mockClient.Object, "TestTable")); + } + #endregion } } diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBTests.cs b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBTests.cs index da8c44271835..adeec52799c8 100644 --- a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBTests.cs +++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/DynamoDBTests.cs @@ -425,41 +425,6 @@ public class Employee public string ManagerName { get; set; } } -#if ASYNC_AWAIT - [TestMethod] - [TestCategory("DynamoDBv2")] - public async Task TestMockingAsyncSeach() - { - var mockDBContext = new Mock(); - mockDBContext - .Setup(x => x.ScanAsync( - It.IsAny>(), - It.IsAny())) - .Returns( - new MockAsyncSearch() // Return mock version of AsyncSearch - ); - - var search = mockDBContext.Object.ScanAsync(new List()); - Assert.IsInstanceOfType(search, typeof(MockAsyncSearch)); - - var items = await search.GetNextSetAsync(); - Assert.AreEqual(0, items.Count()); - } - - public class DataItem - { - public string Id { get; set; } - } - - public class MockAsyncSearch : AsyncSearch - { - public override Task> GetNextSetAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - return Task.FromResult(new List()); - } - } -#endif - /// /// Asserts that our desired exception is thrown when attempting to make a query /// that relies on the hash key without correct table metadata diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs new file mode 100644 index 000000000000..b0c679c3e552 --- /dev/null +++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs @@ -0,0 +1,372 @@ +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DataModel; +using Amazon.DynamoDBv2.DocumentModel; +using Amazon.DynamoDBv2.Model; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AWSSDK_DotNet.UnitTests +{ + /// + /// Tests related to the mockabilty of the doucment and object mapper programming models + /// + [TestClass] + public class MockingTests + { + +#if ASYNC_AWAIT + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingAsyncSeach() + { + var mockDBContext = new Mock(); + mockDBContext + .Setup(x => x.ScanAsync( + It.IsAny>(), + It.IsAny())) + .Returns( + new MockAsyncSearch() // Return mock version of AsyncSearch + ); + + var search = mockDBContext.Object.ScanAsync(new List()); + Assert.IsInstanceOfType(search, typeof(MockAsyncSearch)); + + var items = await search.GetNextSetAsync(); + Assert.AreEqual(0, items.Count()); + } + + public class DataItem + { + public string Id { get; set; } + } + + public class MockAsyncSearch : AsyncSearch + { + public override Task> GetNextSetAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(new List()); + } + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_PutItemAsync() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + // This calls the low-level PutItemAsync, which should be valid on the mocked client + await table.PutItemAsync(document); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_GetItemAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.GetItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetItemResponse { Item = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level GetItemAsync, which should be valid on the mocked client + await table.GetItemAsync("123"); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_UpdateItemAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.UpdateItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new UpdateItemResponse { Attributes = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + // This calls the low-level UpdateItemAsync, which should be valid on the mocked client + await table.UpdateItemAsync(document, "123"); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_DeleteItemAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.DeleteItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new DeleteItemResponse { Attributes = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level DeleteItemAsync, which should be valid on the mocked client + await table.DeleteItemAsync("123"); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_QueryAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.QueryAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new QueryResponse { Items = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level QueryAsync, which should be valid on the mocked client + await table.Query("123", new QueryFilter()).GetNextSetAsync(); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_ScanAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.ScanAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ScanResponse { Items = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // This calls the low-level ScanAsync, which should be valid on the mocked client + await table.Scan(new ScanOperationConfig()).GetNextSetAsync(); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_BatchGetAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.BatchGetItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new BatchGetItemResponse { Responses = new(), UnprocessedKeys = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var batchGet = table.CreateBatchGet(); + batchGet.AddKey("123"); + + // This calls the low-level BatchGetItemAsync, which should be valid on the mocked client + await batchGet.ExecuteAsync(); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_BatchWriteAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.BatchWriteItemAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new BatchWriteItemResponse { UnprocessedItems = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + + var document = new Document(); + document["Id"] = "123"; + + var batchWrite = table.CreateBatchWrite(); + batchWrite.AddDocumentToPut(document); + + // This calls the low-level BatchWriteItemAsync, which should be valid on the mocked client + await batchWrite.ExecuteAsync(); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_TransactGetAsync() + { + var mockClient = new Mock(); + mockClient.Setup(x => x.TransactGetItemsAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new TransactGetItemsResponse { Responses = new() }); + + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var transactGet = table.CreateTransactGet(); + transactGet.AddKey("123"); + + // This calls the low-level TransactGetItemsAsync, which should be valid on the mocked client + await transactGet.ExecuteAsync(); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public async Task TestMockingTableClient_TransactWriteAsync() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + var transactWrite = table.CreateTransactWrite(); + transactWrite.AddDocumentToPut(document); + + // This calls the low-level TransactWriteItemsAsync, which should be valid on the mocked client + await transactWrite.ExecuteAsync(); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_LoadTable_Throws() + { + var mockClient = new Mock(); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync LoadTable + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => Table.LoadTable(mockClient.Object, "TestTable")); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_PutItem_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync PutItem + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => table.PutItem(document)); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_GetItem_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync GetItem + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => table.GetItem("123")); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_UpdateItem_Throwsc() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync UpdateItem + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => table.UpdateItem(document, "123")); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_DeleteItem_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync DeleteItem + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => table.DeleteItem("123")); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_Query_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync Query + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => table.Query("123", new QueryFilter()).GetNextSet()); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_Scan_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync Scan + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => table.Scan(new ScanOperationConfig()).GetNextSet()); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_BatchGet_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var batchGet = table.CreateBatchGet(); + batchGet.AddKey("123"); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync BatchGetItem + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => batchGet.Execute()); + + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_BatchWrite_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + var batchWrite = table.CreateBatchWrite(); + batchWrite.AddDocumentToPut(document); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync BatchWriteItem + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => batchWrite.Execute()); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_TransactGet_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var transactGet = table.CreateTransactGet(); + transactGet.AddKey("123"); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync TransactGetItems + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => transactGet.Execute()); + } + + [TestMethod] + [TestCategory("DynamoDBv2")] + public void TestMockingTableClient_TransactWrite_Throws() + { + var mockClient = new Mock(); + var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); + + var document = new Document(); + document["Id"] = "123"; + + var transactWrite = table.CreateTransactWrite(); + transactWrite.AddDocumentToPut(document); + + // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync TransactWriteItems + // that this still relies on, so we expect it to fail + Assert.ThrowsException(() => transactWrite.Execute()); + } +#endif + } +} From 2bed5ea2065dcfc9d7ed3b3c09585532eaa496d9 Mon Sep 17 00:00:00 2001 From: Alex Shovlin Date: Thu, 1 Aug 2024 21:19:32 -0400 Subject: [PATCH 2/4] Remove casts from loops and correct conditionals --- .../Custom/DocumentModel/DocumentBatchGet.cs | 20 +-- .../DocumentModel/DocumentBatchWrite.cs | 20 +-- .../DocumentModel/DocumentTransactGet.cs | 4 +- .../DocumentModel/DocumentTransactWrite.cs | 4 +- .../DynamoDBv2/Custom/DocumentModel/Search.cs | 6 +- .../DynamoDBv2/Custom/DocumentModel/Table.cs | 10 +- .../UnitTests/Custom/MockingTests.cs | 157 +----------------- 7 files changed, 33 insertions(+), 188 deletions(-) diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs index 696e5b51c4e1..bcec76d707f6 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs @@ -429,19 +429,19 @@ private Results GetAttributeItems() private static void CallUntilCompletion(IAmazonDynamoDB client, BatchGetItemRequest request, Results allResults) { - do +#if NETSTANDARD + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = client as AmazonDynamoDBClient; + if (internalClient == null) { -#if AWS_ASYNC_API - // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods - var internalClient = client as AmazonDynamoDBClient; - if (internalClient == null) - { - throw new InvalidOperationException("Calling the synchronous DocumentBatchGet.Execute() from .NET or .NET Core requires initializing the Table " + - "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); - } + throw new InvalidOperationException("Calling the synchronous DocumentBatchGet.Execute() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); + } #else - internalClient = DDBClient; + var internalClient = client; #endif + do + { var serviceResponse = internalClient.BatchGetItem(request); foreach (var kvp in serviceResponse.Responses) diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs index 1b2a1441ca7d..2d4a4e882928 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchWrite.cs @@ -471,19 +471,19 @@ private static Dictionary> GetNextWriteI private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, IAmazonDynamoDB client) { - do +#if NETSTANDARD + // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods + var internalClient = client as AmazonDynamoDBClient; + if (internalClient == null) { -#if AWS_ASYNC_API - // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods - var internalClient = client as AmazonDynamoDBClient; - if (internalClient == null) - { - throw new InvalidOperationException("Calling the synchronous DocumentBatchWrite.Execute() from .NET or .NET Core requires initializing the Table " + - "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); - } + throw new InvalidOperationException("Calling the synchronous DocumentBatchWrite.Execute() from .NET or .NET Core requires initializing the Table " + + "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); + } #else - internalClient = DDBClient; + var internalClient = client; #endif + do + { var result = internalClient.BatchWriteItem(request); request.RequestItems = result.UnprocessedItems; diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs index 6d4dfc1813f4..354b87c14a98 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactGet.cs @@ -307,7 +307,7 @@ private Dictionary> GetItemsHelper() if (Items == null || !Items.Any()) return new Dictionary>(); var request = ConstructRequest(isAsync: false); -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var internalClient = Items[0].TransactionPart.TargetTable.DDBClient as AmazonDynamoDBClient; if (internalClient == null) @@ -316,7 +316,7 @@ private Dictionary> GetItemsHelper() "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); } #else - internalClient = Items[0].TransactionPart.TargetTable.DDBClient; + var internalClient = Items[0].TransactionPart.TargetTable.DDBClient; #endif var response = internalClient.TransactGetItems(request); return GetDocuments(response.Responses); diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs index fb89d960cebe..cff407032037 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentTransactWrite.cs @@ -598,7 +598,7 @@ private void WriteItemsHelper() try { -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var internalClient = Items[0].TransactionPart.TargetTable.DDBClient as AmazonDynamoDBClient; if (internalClient == null) @@ -607,7 +607,7 @@ private void WriteItemsHelper() "with an actual AmazonDynamoDBClient. You can use a mocked or substitute IAmazonDynamoDB when calling ExecuteAsync instead."); } #else - internalClient = DDBClient; + var internalClient = Items[0].TransactionPart.TargetTable.DDBClient; #endif internalClient.TransactWriteItems(request); diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs index baf6de0db1c5..6eaef4a68e37 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Search.cs @@ -212,7 +212,7 @@ internal List GetNextSetHelper() { List ret = new List(); -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var internalClient = SourceTable.DDBClient as AmazonDynamoDBClient; if (internalClient == null) @@ -544,7 +544,7 @@ private int GetCount() } else { -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var internalClient = SourceTable.DDBClient as AmazonDynamoDBClient; if (internalClient == null) @@ -553,7 +553,7 @@ private int GetCount() "initializing the Table with an actual AmazonDynamoDBClient."); } #else - var internalClient = DDBClient; + var internalClient = SourceTable.DDBClient; #endif switch (SearchMethod) { diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs index fc6546ad1662..6a30637db5f6 100644 --- a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs +++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Table.cs @@ -361,7 +361,7 @@ private TableDescription DescribeTable(string tableName) }; this.AddRequestHandler(req, isAsync: false); -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var client = DDBClient as AmazonDynamoDBClient; if (client == null) @@ -941,7 +941,7 @@ internal Document PutItemHelper(Document doc, PutItemOperationConfig config) currentConfig.ConditionalExpression.ApplyExpression(req, this); } -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var client = DDBClient as AmazonDynamoDBClient; if (client == null) @@ -1028,7 +1028,7 @@ internal Document GetItemHelper(Key key, GetItemOperationConfig config) this.AddRequestHandler(request, isAsync: false); -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var client = DDBClient as AmazonDynamoDBClient; if (client == null) @@ -1159,7 +1159,7 @@ internal Document UpdateHelper(Document doc, Key key, UpdateItemOperationConfig req.ExpressionAttributeNames.Add(kvp.Key, kvp.Value); } } -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var client = DDBClient as AmazonDynamoDBClient; if (client == null) @@ -1313,7 +1313,7 @@ internal Document DeleteHelper(Key key, DeleteItemOperationConfig config) currentConfig.ConditionalExpression.ApplyExpression(req, this); } -#if AWS_ASYNC_API +#if NETSTANDARD // Cast the IAmazonDynamoDB to the concrete client instead, so we can access the internal sync-over-async methods var client = DDBClient as AmazonDynamoDBClient; if (client == null) diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs index b0c679c3e552..7fb590c82f08 100644 --- a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs +++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs @@ -52,6 +52,7 @@ public override Task> GetNextSetAsync(CancellationToken cancellationToke return Task.FromResult(new List()); } } +#endif [TestMethod] [TestCategory("DynamoDBv2")] @@ -212,161 +213,5 @@ public async Task TestMockingTableClient_TransactWriteAsync() await transactWrite.ExecuteAsync(); } - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_LoadTable_Throws() - { - var mockClient = new Mock(); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync LoadTable - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => Table.LoadTable(mockClient.Object, "TestTable")); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_PutItem_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - var document = new Document(); - document["Id"] = "123"; - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync PutItem - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => table.PutItem(document)); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_GetItem_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync GetItem - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => table.GetItem("123")); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_UpdateItem_Throwsc() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - var document = new Document(); - document["Id"] = "123"; - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync UpdateItem - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => table.UpdateItem(document, "123")); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_DeleteItem_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync DeleteItem - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => table.DeleteItem("123")); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_Query_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync Query - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => table.Query("123", new QueryFilter()).GetNextSet()); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_Scan_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync Scan - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => table.Scan(new ScanOperationConfig()).GetNextSet()); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_BatchGet_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - var batchGet = table.CreateBatchGet(); - batchGet.AddKey("123"); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync BatchGetItem - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => batchGet.Execute()); - - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_BatchWrite_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - var document = new Document(); - document["Id"] = "123"; - - var batchWrite = table.CreateBatchWrite(); - batchWrite.AddDocumentToPut(document); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync BatchWriteItem - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => batchWrite.Execute()); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_TransactGet_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - var transactGet = table.CreateTransactGet(); - transactGet.AddKey("123"); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync TransactGetItems - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => transactGet.Execute()); - } - - [TestMethod] - [TestCategory("DynamoDBv2")] - public void TestMockingTableClient_TransactWrite_Throws() - { - var mockClient = new Mock(); - var table = new TableBuilder(mockClient.Object, "TestTable").AddHashKey("Id", DynamoDBEntryType.String).Build(); - - var document = new Document(); - document["Id"] = "123"; - - var transactWrite = table.CreateTransactWrite(); - transactWrite.AddDocumentToPut(document); - - // Because we're in ASYNC_AWAIT, the mock client doesn't expose the internal sync TransactWriteItems - // that this still relies on, so we expect it to fail - Assert.ThrowsException(() => transactWrite.Execute()); - } -#endif } } From 9fa85ad48e80358c600e48810b33a4a3bbf40b33 Mon Sep 17 00:00:00 2001 From: Alex Shovlin Date: Thu, 1 Aug 2024 21:28:14 -0400 Subject: [PATCH 3/4] Clarify comment --- sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs b/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs index e0b897b0ae12..057d34d9e413 100644 --- a/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs +++ b/sdk/test/NetStandard/UnitTests/Services/DynamoDBTests.cs @@ -36,7 +36,7 @@ public void AttributeValueIsBOOLSetTest() Assert.False(boolAV.BOOL.HasValue); } - #region MockingTests - these are similar to AWSSDK_DotNet.UnitTests.MockingTests, if we unify .NET Framework 4.5 and .NET tests they can be de-duplicated + #region MockingTests [Fact] [Trait("Category", "DynamoDBv2")] From 2f8a80ea5900768e8fe24d18d513fed4f61a085e Mon Sep 17 00:00:00 2001 From: Alex Shovlin Date: Wed, 7 Aug 2024 15:35:46 -0400 Subject: [PATCH 4/4] test: Update LangVersion for UnitTests.NetStandard.csproj to match elsewhere --- .../UnitTests/UnitTests.NetStandard.csproj | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sdk/test/NetStandard/UnitTests/UnitTests.NetStandard.csproj b/sdk/test/NetStandard/UnitTests/UnitTests.NetStandard.csproj index 7839959e879c..6d5f79191292 100644 --- a/sdk/test/NetStandard/UnitTests/UnitTests.NetStandard.csproj +++ b/sdk/test/NetStandard/UnitTests/UnitTests.NetStandard.csproj @@ -18,10 +18,20 @@ false CS1591,CS0612,CS0618,xUnit1013,NU1701 - true - true - 8.0 + true + true + + + 11.0 + + + 9.0 + +