From 55945fa3d899e4a87f13d34a61072202d09cb907 Mon Sep 17 00:00:00 2001 From: Renan Rezende | Stone Date: Sun, 21 Mar 2021 18:43:09 -0300 Subject: [PATCH 1/3] [@rengenesio] Change cache to be indexed by connection string and table name. --- src/StrangerData/DbDialect.cs | 8 +- src/StrangerData/Generator/TableGenerator.cs | 10 +-- src/StrangerData/IDbDialect.cs | 5 +- src/StrangerData/Utils/MemoryCache.cs | 20 ++--- .../Utils/MemoryCacheTests.cs | 75 +++++++++++++++++++ 5 files changed, 93 insertions(+), 25 deletions(-) create mode 100644 test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs diff --git a/src/StrangerData/DbDialect.cs b/src/StrangerData/DbDialect.cs index d4f5c35..1a80b21 100644 --- a/src/StrangerData/DbDialect.cs +++ b/src/StrangerData/DbDialect.cs @@ -1,15 +1,15 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace StrangerData { public abstract class DbDialect : IDbDialect { - public DbDialect(string connectionString) + public string ConnectionString { get; } + + protected DbDialect(string connectionString) { + this.ConnectionString = connectionString; } public virtual void DeleteAll(Stack recordIdentifiers) diff --git a/src/StrangerData/Generator/TableGenerator.cs b/src/StrangerData/Generator/TableGenerator.cs index d8c1b08..4d6d2af 100644 --- a/src/StrangerData/Generator/TableGenerator.cs +++ b/src/StrangerData/Generator/TableGenerator.cs @@ -1,10 +1,7 @@ -using StrangerData; -using StrangerData.Utils; +using StrangerData.Utils; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace StrangerData.Generator { @@ -24,10 +21,7 @@ private TableGenerator(IDbDialect dbDialect, string tableName, Stack>(tableName, () => - { - return _dbDialect.GetTableSchemaInfo(tableName); - }); + _tableColumnInfoList = MemoryCache.TryGetFromCache>(_dbDialect.ConnectionString, tableName, () => this._dbDialect.GetTableSchemaInfo(tableName)); } public TableGenerator(IDbDialect dbDialect, string tableName) diff --git a/src/StrangerData/IDbDialect.cs b/src/StrangerData/IDbDialect.cs index 6570a91..01fb2b2 100644 --- a/src/StrangerData/IDbDialect.cs +++ b/src/StrangerData/IDbDialect.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace StrangerData { public interface IDbDialect : IDisposable { + string ConnectionString { get; } + TableColumnInfo[] GetTableSchemaInfo(string tableName); IDictionary Insert(string tableName, IEnumerable tableSchemaInfo, IDictionary values); diff --git a/src/StrangerData/Utils/MemoryCache.cs b/src/StrangerData/Utils/MemoryCache.cs index f0f991f..78e1222 100644 --- a/src/StrangerData/Utils/MemoryCache.cs +++ b/src/StrangerData/Utils/MemoryCache.cs @@ -1,34 +1,34 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace StrangerData.Utils { public static class MemoryCache { - private static IDictionary _cache; + private static IDictionary, object> _cache; - private static IDictionary Cache + private static IDictionary, object> Cache { get { if (_cache == null) - _cache = new Dictionary(); + _cache = new ConcurrentDictionary, object>(); return _cache; } } - public static T TryGetFromCache(string key, Func getValue) + public static T TryGetFromCache(string primaryKey, string secondaryKey, Func getValue) where T : class { - if (!Cache.ContainsKey(key)) + Tuple compositeKey = new Tuple(primaryKey, secondaryKey); + + if (!Cache.ContainsKey(compositeKey)) { - Cache[key] = getValue(); + Cache[compositeKey] = getValue(); } - return Cache[key] as T; + return Cache[compositeKey] as T; } } } diff --git a/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs b/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs new file mode 100644 index 0000000..b380b75 --- /dev/null +++ b/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs @@ -0,0 +1,75 @@ +using FluentAssertions; +using Moq; +using StrangerData.Utils; +using System; +using Xunit; + +namespace StrangerData.UnitTests.Utils +{ + public class MemoryCacheTests + { + public MemoryCacheTests() + { + } + + [Fact] + public void TryGetFromCache_BothKeysNotExists_InvokeFuncAndReturnFuncValue() + { + // Arrange + string expected = Any.String(); + + Mock> funcMock = new Mock>(); + funcMock.Setup(f => f()).Returns(expected); + + string nonExistentPrimaryKey = Any.String(); + string nonExistentSecondaryKey = Any.String(); + + // Act + string actual = MemoryCache.TryGetFromCache(nonExistentPrimaryKey, nonExistentSecondaryKey, funcMock.Object); + + // Assert + actual.Should().Be(expected); + funcMock.Verify(f => f(), Times.Once); + } + + [Fact] + public void TryGetFromCache_SecondaryKeyNotExists_ReturnNewValue() + { + string expected = Any.String(); + + Mock> funcMock = new Mock>(); + funcMock.Setup(f => f()).Returns(expected); + + string primaryKey = Any.String(); + MemoryCache.TryGetFromCache(primaryKey, Any.String(), () => Any.String()); + + string nonExistentSecondaryKey = Any.String(); + + // Act + string actual = MemoryCache.TryGetFromCache(primaryKey, nonExistentSecondaryKey, funcMock.Object); + + // Assert + actual.Should().Be(expected); + funcMock.Verify(f => f(), Times.Once); + } + + [Fact] + public void TryGetFromCache_BothKeysExists_ReturnPreviousValue() + { + Mock> funcMock = new Mock>(); + funcMock.Setup(f => f()).Returns(Any.String()); + + string primaryKey = Any.String(); + string secondaryKey = Any.String(); + string expected = Any.String(); + MemoryCache.TryGetFromCache(primaryKey, secondaryKey, () => expected); + + // Act + string actual = MemoryCache.TryGetFromCache(primaryKey, secondaryKey, funcMock.Object); + + // Assert + actual.Should().Be(expected); + funcMock.Verify(f => f(), Times.Never); + } + } +} From 139a8f1807a640a5369f2e1e1c7416a61f94ca0a Mon Sep 17 00:00:00 2001 From: Renan Rezende | Stone Date: Sun, 21 Mar 2021 19:04:44 -0300 Subject: [PATCH 2/3] [@rengenesio] Improve unit tests. --- src/StrangerData/Generator/TableGenerator.cs | 2 ++ .../Generator/TableGeneratorTests.cs | 33 +++++++++++++++++++ .../Utils/MemoryCacheTests.cs | 10 +++--- 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs diff --git a/src/StrangerData/Generator/TableGenerator.cs b/src/StrangerData/Generator/TableGenerator.cs index 4d6d2af..525d17b 100644 --- a/src/StrangerData/Generator/TableGenerator.cs +++ b/src/StrangerData/Generator/TableGenerator.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("StrangerData.UnitTests")] namespace StrangerData.Generator { internal class TableGenerator diff --git a/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs b/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs new file mode 100644 index 0000000..96d259e --- /dev/null +++ b/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs @@ -0,0 +1,33 @@ +using Moq; +using StrangerData.Generator; +using Xunit; + +namespace StrangerData.UnitTests.Generator +{ + public class TableGeneratorTests + { + [Fact] + public void Constructor_TablesFromDifferentDatabasesWithSameName_GetTableSchemaInfoFromBothDatabases() + { + // Arrange + Mock databaseOneDialectMock = new Mock(); + databaseOneDialectMock.Setup(d => d.ConnectionString) + .Returns(Any.String()); + + Mock databaseTwoDialectMock = new Mock(); + databaseTwoDialectMock.Setup(d => d.ConnectionString) + .Returns(Any.String()); + + string tableName = Any.String(); + + TableGenerator databaseOneTableGenerator = new TableGenerator(databaseOneDialectMock.Object, tableName); + + // Act + TableGenerator databaseTwoTableGenerator = new TableGenerator(databaseTwoDialectMock.Object, tableName); + + // Assert + databaseOneDialectMock.Verify(d => d.GetTableSchemaInfo(tableName), Times.Once); + databaseTwoDialectMock.Verify(d => d.GetTableSchemaInfo(tableName), Times.Once); + } + } +} diff --git a/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs b/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs index b380b75..dcabd90 100644 --- a/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs +++ b/test/StrangerData.UnitTests/Utils/MemoryCacheTests.cs @@ -8,10 +8,6 @@ namespace StrangerData.UnitTests.Utils { public class MemoryCacheTests { - public MemoryCacheTests() - { - } - [Fact] public void TryGetFromCache_BothKeysNotExists_InvokeFuncAndReturnFuncValue() { @@ -33,8 +29,9 @@ public void TryGetFromCache_BothKeysNotExists_InvokeFuncAndReturnFuncValue() } [Fact] - public void TryGetFromCache_SecondaryKeyNotExists_ReturnNewValue() + public void TryGetFromCache_SecondaryKeyNotExists_InvokeFuncAndReturnFuncValue() { + // Arrange string expected = Any.String(); Mock> funcMock = new Mock>(); @@ -54,8 +51,9 @@ public void TryGetFromCache_SecondaryKeyNotExists_ReturnNewValue() } [Fact] - public void TryGetFromCache_BothKeysExists_ReturnPreviousValue() + public void TryGetFromCache_BothKeysExists_NotInvokeFuncAndReturnExistentValue() { + // Arrange Mock> funcMock = new Mock>(); funcMock.Setup(f => f()).Returns(Any.String()); From 716421272ef5d824811b829641284d916a569c7f Mon Sep 17 00:00:00 2001 From: Renan Rezende | Stone Date: Sun, 21 Mar 2021 19:07:44 -0300 Subject: [PATCH 3/3] [@rengenesio] Improve TableGeneratur unit tests. --- .../Generator/TableGeneratorTests.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs b/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs index 96d259e..60fd31d 100644 --- a/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs +++ b/test/StrangerData.UnitTests/Generator/TableGeneratorTests.cs @@ -7,7 +7,7 @@ namespace StrangerData.UnitTests.Generator public class TableGeneratorTests { [Fact] - public void Constructor_TablesFromDifferentDatabasesWithSameName_GetTableSchemaInfoFromBothDatabases() + public void Constructor_TablesFromDatabasesWithDifferentConnectionString_GetTableSchemaInfoFromBothDatabases() { // Arrange Mock databaseOneDialectMock = new Mock(); @@ -29,5 +29,30 @@ public void Constructor_TablesFromDifferentDatabasesWithSameName_GetTableSchemaI databaseOneDialectMock.Verify(d => d.GetTableSchemaInfo(tableName), Times.Once); databaseTwoDialectMock.Verify(d => d.GetTableSchemaInfo(tableName), Times.Once); } + + [Fact] + public void Constructor_TablesFromDifferentDialectsWithSameConnectionString_GetTableSchemaInfoFromCache() + { + // Arrange + string connectionString = Any.String(); + string tableName = Any.String(); + + Mock databaseOneDialectMock = new Mock(); + databaseOneDialectMock.Setup(d => d.ConnectionString) + .Returns(connectionString); + + Mock databaseTwoDialectMock = new Mock(); + databaseTwoDialectMock.Setup(d => d.ConnectionString) + .Returns(connectionString); + + TableGenerator databaseOneTableGenerator = new TableGenerator(databaseOneDialectMock.Object, tableName); + + // Act + TableGenerator databaseTwoTableGenerator = new TableGenerator(databaseTwoDialectMock.Object, tableName); + + // Assert + databaseOneDialectMock.Verify(d => d.GetTableSchemaInfo(tableName), Times.Once); + databaseTwoDialectMock.Verify(d => d.GetTableSchemaInfo(tableName), Times.Never); + } } }