From 2aefb20ccda2e8fa7821845fe914cb3831ece9fd Mon Sep 17 00:00:00 2001 From: data-miner00 Date: Sat, 21 Sep 2024 20:12:02 +0800 Subject: [PATCH 1/4] test: Add unit test for seeder --- .../ExecutorTests.cs | 50 +++++++++++++++++++ .../GlobalSuppressions.cs | 8 +++ .../Sway.Database.Seeder.UnitTests.csproj | 18 +++++++ src/Sway.Database.Seeder/AssemblyInfo.cs | 20 ++++++++ src/Sway.sln | 7 +++ 5 files changed, 103 insertions(+) create mode 100644 src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs create mode 100644 src/Sway.Database.Seeder.UnitTests/GlobalSuppressions.cs create mode 100644 src/Sway.Database.Seeder.UnitTests/Sway.Database.Seeder.UnitTests.csproj create mode 100644 src/Sway.Database.Seeder/AssemblyInfo.cs diff --git a/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs b/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs new file mode 100644 index 0000000..c6cb3a8 --- /dev/null +++ b/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs @@ -0,0 +1,50 @@ +namespace Sway.Database.Seeder.UnitTests; + +using Moq; +using Sway.Database.Seeder.Options; +using Sway.Database.Seeder.Sinks; + +public sealed class ExecutorTests +{ + private static Mock MockSink; + private static Mock MockSinkFactory; + private static IExecutor Executor; + private static SeedingOption SeedingOption; + + [Before(Test)] + public void Setup() + { + MockSink = new Mock(); + MockSinkFactory = new Mock(); + + MockSinkFactory + .Setup(x => x.CreateSink(It.IsAny(), It.IsAny())) + .Returns(MockSink.Object); + + SeedingOption = new SeedingOption + { + Count = 1, + Entity = SwayEntity.User, + Destination = SinkType.Database, + }; + Executor = new Executor(MockSinkFactory.Object, SeedingOption); + } + + [Test] + [Retry(3)] + public async Task ExecuteAsync_InvokedProvisionAsync() + { + await Executor.ExecuteAsync(default); + + MockSink.Verify( + x => x.ProvisionAsync(1, default), + Times.Once()); + } + + [Test] + [DependsOn(nameof(ExecuteAsync_InvokedProvisionAsync))] + public async Task AfterExecution_ObjectNotNull() + { + await Assert.That(Executor).IsNotNull(); + } +} diff --git a/src/Sway.Database.Seeder.UnitTests/GlobalSuppressions.cs b/src/Sway.Database.Seeder.UnitTests/GlobalSuppressions.cs new file mode 100644 index 0000000..a7aec32 --- /dev/null +++ b/src/Sway.Database.Seeder.UnitTests/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "", Scope = "type", Target = "~T:Sway.Database.Seeder.UnitTests.ExecutorTests")] diff --git a/src/Sway.Database.Seeder.UnitTests/Sway.Database.Seeder.UnitTests.csproj b/src/Sway.Database.Seeder.UnitTests/Sway.Database.Seeder.UnitTests.csproj new file mode 100644 index 0000000..0d18580 --- /dev/null +++ b/src/Sway.Database.Seeder.UnitTests/Sway.Database.Seeder.UnitTests.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/src/Sway.Database.Seeder/AssemblyInfo.cs b/src/Sway.Database.Seeder/AssemblyInfo.cs new file mode 100644 index 0000000..35cef7d --- /dev/null +++ b/src/Sway.Database.Seeder/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties + + +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid("90f0fb08-0432-46c1-b99a-d12110b71fe3")] +[assembly: InternalsVisibleTo("Sway.Database.Seeder.UnitTests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/src/Sway.sln b/src/Sway.sln index 8dbe458..b31c92d 100644 --- a/src/Sway.sln +++ b/src/Sway.sln @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sway.Web.Mvc", "Sway.Web.Mv EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sway.Database.Seeder", "Sway.Database.Seeder\Sway.Database.Seeder.csproj", "{1F473C6B-DE7B-4F91-A72E-E7E0F5732C01}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sway.Database.Seeder.UnitTests", "Sway.Database.Seeder.UnitTests\Sway.Database.Seeder.UnitTests.csproj", "{AA117586-82B8-4DB7-94BC-5E2272AC59AF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,12 +75,17 @@ Global {1F473C6B-DE7B-4F91-A72E-E7E0F5732C01}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F473C6B-DE7B-4F91-A72E-E7E0F5732C01}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F473C6B-DE7B-4F91-A72E-E7E0F5732C01}.Release|Any CPU.Build.0 = Release|Any CPU + {AA117586-82B8-4DB7-94BC-5E2272AC59AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA117586-82B8-4DB7-94BC-5E2272AC59AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA117586-82B8-4DB7-94BC-5E2272AC59AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA117586-82B8-4DB7-94BC-5E2272AC59AF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {5BAA2566-3B21-402B-8219-972AC0E2DEB6} = {988783AC-9ED0-4AEF-AFD6-FD4F3E7CA70F} + {AA117586-82B8-4DB7-94BC-5E2272AC59AF} = {988783AC-9ED0-4AEF-AFD6-FD4F3E7CA70F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4C1AEF2-0F1A-423E-870D-870F0483410A} From 62b4eb220235099fd1570e2306063e1fca3e2a7d Mon Sep 17 00:00:00 2001 From: data-miner00 Date: Sat, 21 Sep 2024 20:20:02 +0800 Subject: [PATCH 2/4] test: Change from static to class variable --- .../ExecutorTests.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs b/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs index c6cb3a8..3bf1872 100644 --- a/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs +++ b/src/Sway.Database.Seeder.UnitTests/ExecutorTests.cs @@ -6,37 +6,40 @@ public sealed class ExecutorTests { - private static Mock MockSink; - private static Mock MockSinkFactory; - private static IExecutor Executor; - private static SeedingOption SeedingOption; + private Mock mockSink; + private Mock mockSinkFactory; + private IExecutor executor; + private SeedingOption seedingOption; [Before(Test)] public void Setup() { - MockSink = new Mock(); - MockSinkFactory = new Mock(); + this.mockSink = new Mock(); + this.mockSinkFactory = new Mock(); - MockSinkFactory + this.mockSinkFactory .Setup(x => x.CreateSink(It.IsAny(), It.IsAny())) - .Returns(MockSink.Object); + .Returns(this.mockSink.Object); - SeedingOption = new SeedingOption + this.seedingOption = new SeedingOption { Count = 1, Entity = SwayEntity.User, Destination = SinkType.Database, }; - Executor = new Executor(MockSinkFactory.Object, SeedingOption); + + this.executor = new Executor( + this.mockSinkFactory.Object, + this.seedingOption); } [Test] [Retry(3)] public async Task ExecuteAsync_InvokedProvisionAsync() { - await Executor.ExecuteAsync(default); + await this.executor.ExecuteAsync(default); - MockSink.Verify( + this.mockSink.Verify( x => x.ProvisionAsync(1, default), Times.Once()); } @@ -45,6 +48,6 @@ public async Task ExecuteAsync_InvokedProvisionAsync() [DependsOn(nameof(ExecuteAsync_InvokedProvisionAsync))] public async Task AfterExecution_ObjectNotNull() { - await Assert.That(Executor).IsNotNull(); + await Assert.That(this.executor).IsNotNull(); } } From b3f59b489579b56eeb50521e271c0850c8455293 Mon Sep 17 00:00:00 2001 From: data-miner00 Date: Sat, 21 Sep 2024 20:42:20 +0800 Subject: [PATCH 3/4] docs: Add docstrings --- .../Generator/IGenerator.cs | 14 ++++++++-- .../Generator/PaymentMethodGenerator.cs | 20 +++++++++---- .../Generator/ProductRatingGenerator.cs | 10 ++++++- .../Generator/UserGenerator.cs | 7 +++++ src/Sway.Database.Seeder/NamingStrategy.cs | 12 ++++++++ .../Options/DatabaseOption.cs | 8 +++++- .../Options/SeedingOption.cs | 28 +++++++++++++++++++ .../Options/SqlSinkOption.cs | 11 +++++++- 8 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/Sway.Database.Seeder/Generator/IGenerator.cs b/src/Sway.Database.Seeder/Generator/IGenerator.cs index 7d8dd9c..6b2d10e 100644 --- a/src/Sway.Database.Seeder/Generator/IGenerator.cs +++ b/src/Sway.Database.Seeder/Generator/IGenerator.cs @@ -1,11 +1,19 @@ namespace Sway.Database.Seeder.Generator; -using System; + using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; +/// +/// The interface for entity generator. +/// +/// The entity to be generated. internal interface IGenerator { + /// + /// Generate a list of . + /// + /// Amount of entities to be generated. + /// The cancellation token. + /// The list of generated entities. Task> GenerateAsync(int count, CancellationToken cancellationToken); } diff --git a/src/Sway.Database.Seeder/Generator/PaymentMethodGenerator.cs b/src/Sway.Database.Seeder/Generator/PaymentMethodGenerator.cs index 4c46cff..de3335c 100644 --- a/src/Sway.Database.Seeder/Generator/PaymentMethodGenerator.cs +++ b/src/Sway.Database.Seeder/Generator/PaymentMethodGenerator.cs @@ -6,20 +6,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; +/// +/// The generator class for . +/// internal sealed class PaymentMethodGenerator : IGenerator { private readonly Guid existingUserId; private readonly IEnumerable paymentTypes; - private Faker cryptoFaker; - private Faker cardFaker; - private Faker onlineBankingFaker; - private Faker ewalletFaker; - private readonly Dictionary crytoProviders = new() { { "Bitcoin", "BTC" }, @@ -33,6 +30,16 @@ internal sealed class PaymentMethodGenerator : IGenerator private readonly List cardIssuingBanks = ["Public Bank", "OCBC Bank", "Hong Leong Bank", "Maybank", "CIMB Bank"]; private readonly List ewalletProviders = ["Touch N Go", "Boost", "Grab Pay", "Alipay", "Sway", "Razer Pay"]; + private Faker cryptoFaker; + private Faker cardFaker; + private Faker onlineBankingFaker; + private Faker ewalletFaker; + + /// + /// Initializes a new instance of the class. + /// + /// The existing user to be binded with the new payment methods. + /// The payment type variants. public PaymentMethodGenerator(Guid existingUserId, IEnumerable paymentTypes) { this.existingUserId = Guard.ThrowIfDefault(existingUserId); @@ -44,6 +51,7 @@ public PaymentMethodGenerator(Guid existingUserId, IEnumerable paym this.ConfigureEWalletPaymentMethodFaker(); } + /// public Task> GenerateAsync(int count, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Sway.Database.Seeder/Generator/ProductRatingGenerator.cs b/src/Sway.Database.Seeder/Generator/ProductRatingGenerator.cs index 76673e8..e583887 100644 --- a/src/Sway.Database.Seeder/Generator/ProductRatingGenerator.cs +++ b/src/Sway.Database.Seeder/Generator/ProductRatingGenerator.cs @@ -6,16 +6,23 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; +/// +/// The generator class for . +/// internal sealed class ProductRatingGenerator : IGenerator { private readonly Guid existingProductId; private readonly Guid existingUserId; private Faker faker; + /// + /// Initializes a new instance of the class. + /// + /// The existing product to be binded. + /// The existing user who make the rating. public ProductRatingGenerator(Guid existingProductId, Guid existingUserId) { this.existingProductId = Guard.ThrowIfDefault(existingProductId); @@ -24,6 +31,7 @@ public ProductRatingGenerator(Guid existingProductId, Guid existingUserId) this.ConfigureProductRatingFaker(); } + /// public Task> GenerateAsync(int count, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Sway.Database.Seeder/Generator/UserGenerator.cs b/src/Sway.Database.Seeder/Generator/UserGenerator.cs index 8a849fb..dd60085 100644 --- a/src/Sway.Database.Seeder/Generator/UserGenerator.cs +++ b/src/Sway.Database.Seeder/Generator/UserGenerator.cs @@ -7,15 +7,22 @@ using System.Linq; using System.Threading.Tasks; +/// +/// The generator class for . +/// internal sealed class UserGenerator : IGenerator { private Faker faker; + /// + /// Initializes a new instance of the class. + /// public UserGenerator() { this.ConfigureUserFaker(); } + /// public Task> GenerateAsync(int count, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Sway.Database.Seeder/NamingStrategy.cs b/src/Sway.Database.Seeder/NamingStrategy.cs index 85cd531..3829d2b 100644 --- a/src/Sway.Database.Seeder/NamingStrategy.cs +++ b/src/Sway.Database.Seeder/NamingStrategy.cs @@ -1,10 +1,22 @@ namespace Sway.Database.Seeder; +/// +/// The naming strategy for naming a file. +/// internal enum NamingStrategy { + /// + /// The default value. + /// None, + /// + /// Name by random . + /// Guid, + /// + /// Name by current timestamp. + /// Timestamp, } diff --git a/src/Sway.Database.Seeder/Options/DatabaseOption.cs b/src/Sway.Database.Seeder/Options/DatabaseOption.cs index 4cd599e..5e30d69 100644 --- a/src/Sway.Database.Seeder/Options/DatabaseOption.cs +++ b/src/Sway.Database.Seeder/Options/DatabaseOption.cs @@ -1,6 +1,12 @@ namespace Sway.Database.Seeder.Options; +/// +/// The configurations for database. +/// internal sealed class DatabaseOption { - public string ConnectionString { get; set; } + /// + /// Gets or sets the connection string of the database. + /// + public string ConnectionString { get; set; } = null!; } diff --git a/src/Sway.Database.Seeder/Options/SeedingOption.cs b/src/Sway.Database.Seeder/Options/SeedingOption.cs index 60ce01e..9adbc40 100644 --- a/src/Sway.Database.Seeder/Options/SeedingOption.cs +++ b/src/Sway.Database.Seeder/Options/SeedingOption.cs @@ -2,19 +2,47 @@ using Sway.Core.Models; +/// +/// The configurations for generating . +/// +/// The existing product Id. +/// The existing user Id. internal record ProductRatingOption(Guid ExistingProductId, Guid ExistingUserId); +/// +/// The configurations for generating . +/// +/// The existing user Id. +/// The payment types to be generated. internal record PaymentMethodOption(Guid ExistingUserId, IEnumerable PaymentTypes); +/// +/// The configurations pertaining to seeding. +/// internal sealed class SeedingOption { + /// + /// Gets or sets the destination which the generated entities should go to. + /// public SinkType Destination { get; set; } + /// + /// Gets or sets the number of entites should be generated. + /// public int Count { get; set; } + /// + /// Gets or sets the entity to be generated. + /// public SwayEntity Entity { get; set; } + /// + /// Gets or sets the configurations for product rating. + /// public ProductRatingOption ProductRatingOption { get; set; } + /// + /// Gets or sets the configurations for payment method. + /// public PaymentMethodOption PaymentMethodOption { get; set; } } diff --git a/src/Sway.Database.Seeder/Options/SqlSinkOption.cs b/src/Sway.Database.Seeder/Options/SqlSinkOption.cs index 2b35dd8..ab174c6 100644 --- a/src/Sway.Database.Seeder/Options/SqlSinkOption.cs +++ b/src/Sway.Database.Seeder/Options/SqlSinkOption.cs @@ -1,8 +1,17 @@ namespace Sway.Database.Seeder.Options; +/// +/// The configurations for SQL script sink. +/// internal sealed class SqlSinkOption { - public string OutputPath { get; set; } + /// + /// Gets or sets the output path for the SQL files generated. + /// + public string OutputPath { get; set; } = null!; + /// + /// Gets or sets the naming strategy of the files generated. + /// public NamingStrategy NamingStrategy { get; set; } } From 6e100a3067a2ee6489a92b7cfa2e42dd7c44c9a4 Mon Sep 17 00:00:00 2001 From: data-miner00 Date: Sat, 21 Sep 2024 20:56:45 +0800 Subject: [PATCH 4/4] ci: Try ignore seeder unit test for now --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 107ba71..b9c4ab8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -59,3 +59,7 @@ steps: inputs: platform: '$(buildPlatform)' configuration: '$(buildConfiguration)' + testSelector: 'testAssemblies' + testAssemblyVer2: | + **\*tests.dll + !**\*seeder.unittests.dll