diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/DocumentBatchGet.cs index 59eddee6e49b..bcec76d707f6 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; } + private static void CallUntilCompletion(IAmazonDynamoDB client, BatchGetItemRequest request, Results allResults) + { #if NETSTANDARD - private static void CallUntilCompletion(AmazonDynamoDBClient client, BatchGetItemRequest request, Results allResults) + // 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 - private static void CallUntilCompletion(IAmazonDynamoDB client, BatchGetItemRequest request, Results allResults) + var internalClient = client; #endif - { do { - var serviceResponse = client.BatchGetItem(request); + 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..2d4a4e882928 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; } + private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, IAmazonDynamoDB client) + { #if NETSTANDARD - private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, AmazonDynamoDBClient client) + // 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 - private void CallUntilCompletion(BatchWriteItemRequest request, Dictionary> documentMap, IAmazonDynamoDB client) + var internalClient = client; #endif - { do { - var result = client.BatchWriteItem(request); + 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..354b87c14a98 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 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) + { + 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 + 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 28d2f3e484a5..cff407032037 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 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) + { + 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 + var internalClient = Items[0].TransactionPart.TargetTable.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..6eaef4a68e37 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 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) + { + 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 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) + { + throw new InvalidOperationException("Accessing the synchronous Count from .NET or .NET Core requires " + + "initializing the Table with an actual AmazonDynamoDBClient."); + } +#else + var internalClient = SourceTable.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..6a30637db5f6 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 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) + { + 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 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) + { + 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 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) + { + 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 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) + { + 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 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) + { + 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..057d34d9e413 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 + + [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/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 + + 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..7fb590c82f08 --- /dev/null +++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockingTests.cs @@ -0,0 +1,217 @@ +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()); + } + } +#endif + + [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(); + } + + } +}