diff --git a/.github/workflows/workflow-ci.yml b/.github/workflows/workflow-ci.yml
index 3fbf196..36a5e86 100644
--- a/.github/workflows/workflow-ci.yml
+++ b/.github/workflows/workflow-ci.yml
@@ -34,7 +34,7 @@ jobs:
shell: bash
run: |
echo "🔍 Localizando Projetos de Teste..."
- TEST_PROJECTS=$(find . -name "*.csproj" | grep -E "\.UnitTests|\.Tests|\.ArchitectureTests" | grep -v "Assets")
+ TEST_PROJECTS=$(find . -name "*.csproj" | grep -E "\.ArchitectureTests|\.IntegrationTests|\.UnitTests" | grep -v "Assets" | sort)
if [ -z "$TEST_PROJECTS" ]; then
echo "⚠️ Nenhum projeto de teste encontrado."
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 024b8fd..288ffc3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,7 +11,6 @@
-
@@ -26,7 +25,7 @@
-
+
@@ -49,7 +48,7 @@
-
+
@@ -58,8 +57,10 @@
+
+
-
+
diff --git a/InvoiceReminder.API/AuthenticationSetup/BearerSecuritySchemeTransformer.cs b/InvoiceReminder.API/AuthenticationSetup/BearerSecuritySchemeTransformer.cs
index e3d1a10..2bcf61a 100644
--- a/InvoiceReminder.API/AuthenticationSetup/BearerSecuritySchemeTransformer.cs
+++ b/InvoiceReminder.API/AuthenticationSetup/BearerSecuritySchemeTransformer.cs
@@ -3,7 +3,7 @@
using Microsoft.OpenApi;
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("InvoiceReminder.API.UnitTests")]
+[assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.API")]
namespace InvoiceReminder.API.AuthenticationSetup;
diff --git a/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj b/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj
index e00d40d..70853db 100644
--- a/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj
+++ b/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj
@@ -1,10 +1,6 @@
-
true
Exe
false
diff --git a/InvoiceReminder.Data/Interfaces/IBaseRepository.cs b/InvoiceReminder.Data/Interfaces/IBaseRepository.cs
index 94e4f56..350bdbc 100644
--- a/InvoiceReminder.Data/Interfaces/IBaseRepository.cs
+++ b/InvoiceReminder.Data/Interfaces/IBaseRepository.cs
@@ -6,6 +6,8 @@ public interface IBaseRepository : IDisposable where TEntity : class
{
Task AddAsync(TEntity entity, CancellationToken cancellationToken = default);
Task BulkInsertAsync(ICollection entities, CancellationToken cancellationToken = default);
+ Task BulkRemoveAsync(ICollection entities, CancellationToken cancellationToken = default);
+ Task BulkUpdateAsync(ICollection entities, CancellationToken cancellationToken = default);
void Remove(TEntity entity);
Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
IEnumerable GetAll();
diff --git a/InvoiceReminder.Data/Interfaces/IUserRepository.cs b/InvoiceReminder.Data/Interfaces/IUserRepository.cs
index 1347d21..c381d5c 100644
--- a/InvoiceReminder.Data/Interfaces/IUserRepository.cs
+++ b/InvoiceReminder.Data/Interfaces/IUserRepository.cs
@@ -5,5 +5,4 @@ namespace InvoiceReminder.Data.Interfaces;
public interface IUserRepository : IBaseRepository
{
Task GetByEmailAsync(string value, CancellationToken cancellationToken = default);
- new Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
}
diff --git a/InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs b/InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs
index e6809e9..4ffba79 100644
--- a/InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs
+++ b/InvoiceReminder.Data/Migrations/20250930210104_Initial_Create.cs
@@ -3,217 +3,216 @@
#nullable disable
-namespace InvoiceReminder.Data.Migrations
+namespace InvoiceReminder.Data.Migrations;
+
+///
+[ExcludeFromCodeCoverage]
+public partial class Initial_Create : Migration
{
- [ExcludeFromCodeCoverage]
///
- public partial class Initial_Create : Migration
+ protected override void Up(MigrationBuilder migrationBuilder)
{
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- _ = migrationBuilder.EnsureSchema(
- name: "invoice_reminder");
-
- _ = migrationBuilder.CreateTable(
- name: "user",
- schema: "invoice_reminder",
- columns: table => new
- {
- id = table.Column(type: "uuid", nullable: false),
- telegram_chat_id = table.Column(type: "bigint", nullable: false, defaultValue: 0L),
- name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- email = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- password = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- created_at = table.Column(type: "timestamp with time zone", nullable: false),
- updated_at = table.Column(type: "timestamp with time zone", nullable: false)
- },
- constraints: table => _ = table.PrimaryKey("PK_user", x => x.id));
-
- _ = migrationBuilder.InsertData(
- table: "user",
- schema: "invoice_reminder",
- columns: ["id", "telegram_chat_id", "name", "email", "password", "created_at", "updated_at"],
- values: new object[,]
- {
- {
- "0d77a03d-ac35-480c-b409-a08133409c7c",
- 0,
- "John Doe",
- "john.doe@notmail.com",
- "8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92",
- new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc),
- new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc)
- },
- {
- "59918776-f6c6-4def-93b1-95d7a7717942",
- 0,
- "Jane Doe",
- "jane.doe@notmail.com",
- "8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92",
- new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc),
- new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc)
- }
- });
-
- _ = migrationBuilder.CreateTable(
- name: "email_auth_token",
- schema: "invoice_reminder",
- columns: table => new
- {
- id = table.Column(type: "uuid", nullable: false),
- user_id = table.Column(type: "uuid", nullable: false),
- access_token = table.Column(type: "character varying(512)", maxLength: 512, nullable: false),
- refresh_token = table.Column(type: "character varying(512)", maxLength: 512, nullable: false),
- nonce_value = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
- token_provider = table.Column(type: "character varying(25)", maxLength: 25, nullable: false),
- access_token_expiry = table.Column(type: "timestamp with time zone", nullable: false),
- created_at = table.Column(type: "timestamp with time zone", nullable: false),
- updated_at = table.Column(type: "timestamp with time zone", nullable: false)
- },
- constraints: table =>
- {
- _ = table.PrimaryKey("PK_email_auth_token", x => x.id);
- _ = table.ForeignKey(
- name: "FK_email_auth_token_user_user_id",
- column: x => x.user_id,
- principalSchema: "invoice_reminder",
- principalTable: "user",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- _ = migrationBuilder.CreateTable(
- name: "invoice",
- schema: "invoice_reminder",
- columns: table => new
- {
- id = table.Column(type: "uuid", nullable: false),
- user_id = table.Column(type: "uuid", nullable: false),
- bank = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- beneficiary = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- amount = table.Column(type: "numeric", nullable: false),
- barcode = table.Column(type: "text", nullable: false),
- due_date = table.Column(type: "timestamp with time zone", nullable: false),
- created_at = table.Column(type: "timestamp with time zone", nullable: false),
- updated_at = table.Column(type: "timestamp with time zone", nullable: false)
- },
- constraints: table =>
- {
- _ = table.PrimaryKey("PK_invoice", x => x.id);
- _ = table.ForeignKey(
- name: "FK_invoice_user_user_id",
- column: x => x.user_id,
- principalSchema: "invoice_reminder",
- principalTable: "user",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- _ = migrationBuilder.CreateTable(
- name: "job_schedule",
- schema: "invoice_reminder",
- columns: table => new
+ _ = migrationBuilder.EnsureSchema(
+ name: "invoice_reminder");
+
+ _ = migrationBuilder.CreateTable(
+ name: "user",
+ schema: "invoice_reminder",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ telegram_chat_id = table.Column(type: "bigint", nullable: false, defaultValue: 0L),
+ name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ email = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ password = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ created_at = table.Column(type: "timestamp with time zone", nullable: false),
+ updated_at = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table => _ = table.PrimaryKey("PK_user", x => x.id));
+
+ _ = migrationBuilder.InsertData(
+ table: "user",
+ schema: "invoice_reminder",
+ columns: ["id", "telegram_chat_id", "name", "email", "password", "created_at", "updated_at"],
+ values: new object[,]
+ {
{
- id = table.Column(type: "uuid", nullable: false),
- user_id = table.Column(type: "uuid", nullable: false),
- cron_expression = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- created_at = table.Column(type: "timestamp with time zone", nullable: false),
- updated_at = table.Column(type: "timestamp with time zone", nullable: false)
+ "0d77a03d-ac35-480c-b409-a08133409c7c",
+ 0,
+ "John Doe",
+ "john.doe@notmail.com",
+ "8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92",
+ new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc),
+ new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc)
},
- constraints: table =>
{
- _ = table.PrimaryKey("PK_job_schedule", x => x.id);
- _ = table.ForeignKey(
- name: "FK_job_schedule_user_user_id",
- column: x => x.user_id,
- principalSchema: "invoice_reminder",
- principalTable: "user",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- _ = migrationBuilder.CreateTable(
- name: "scan_email_definition",
- schema: "invoice_reminder",
- columns: table => new
- {
- id = table.Column(type: "uuid", nullable: false),
- user_id = table.Column(type: "uuid", nullable: false),
- invoice_type = table.Column(type: "integer", nullable: false),
- beneficiary = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- description = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- sender_email_address = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- attachment_filename = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
- created_at = table.Column(type: "timestamp with time zone", nullable: false),
- updated_at = table.Column(type: "timestamp with time zone", nullable: false)
- },
- constraints: table =>
- {
- _ = table.PrimaryKey("PK_scan_email_definition", x => x.id);
- _ = table.ForeignKey(
- name: "FK_scan_email_definition_user_user_id",
- column: x => x.user_id,
- principalSchema: "invoice_reminder",
- principalTable: "user",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- _ = migrationBuilder.CreateIndex(
- name: "IX_email_auth_token_user_id",
- schema: "invoice_reminder",
- table: "email_auth_token",
- column: "user_id");
-
- _ = migrationBuilder.CreateIndex(
- name: "IX_invoice_user_id",
- schema: "invoice_reminder",
- table: "invoice",
- column: "user_id");
-
- _ = migrationBuilder.CreateIndex(
- name: "IX_job_schedule_user_id",
- schema: "invoice_reminder",
- table: "job_schedule",
- column: "user_id");
-
- _ = migrationBuilder.CreateIndex(
- name: "IX_scan_email_definition_user_id",
- schema: "invoice_reminder",
- table: "scan_email_definition",
- column: "user_id");
-
- _ = migrationBuilder.CreateIndex(
- name: "idx_user_email",
- schema: "invoice_reminder",
- table: "user",
- column: "email",
- unique: true);
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- _ = migrationBuilder.DropTable(
- name: "email_auth_token",
- schema: "invoice_reminder");
-
- _ = migrationBuilder.DropTable(
- name: "invoice",
- schema: "invoice_reminder");
-
- _ = migrationBuilder.DropTable(
- name: "job_schedule",
- schema: "invoice_reminder");
-
- _ = migrationBuilder.DropTable(
- name: "scan_email_definition",
- schema: "invoice_reminder");
-
- _ = migrationBuilder.DropTable(
- name: "user",
- schema: "invoice_reminder");
- }
+ "59918776-f6c6-4def-93b1-95d7a7717942",
+ 0,
+ "Jane Doe",
+ "jane.doe@notmail.com",
+ "8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92",
+ new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc),
+ new DateTime(2025, 05, 06, 0, 0, 0, DateTimeKind.Utc)
+ }
+ });
+
+ _ = migrationBuilder.CreateTable(
+ name: "email_auth_token",
+ schema: "invoice_reminder",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ user_id = table.Column(type: "uuid", nullable: false),
+ access_token = table.Column(type: "character varying(512)", maxLength: 512, nullable: false),
+ refresh_token = table.Column(type: "character varying(512)", maxLength: 512, nullable: false),
+ nonce_value = table.Column(type: "character varying(64)", maxLength: 64, nullable: false),
+ token_provider = table.Column(type: "character varying(25)", maxLength: 25, nullable: false),
+ access_token_expiry = table.Column(type: "timestamp with time zone", nullable: false),
+ created_at = table.Column(type: "timestamp with time zone", nullable: false),
+ updated_at = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ _ = table.PrimaryKey("PK_email_auth_token", x => x.id);
+ _ = table.ForeignKey(
+ name: "FK_email_auth_token_user_user_id",
+ column: x => x.user_id,
+ principalSchema: "invoice_reminder",
+ principalTable: "user",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ _ = migrationBuilder.CreateTable(
+ name: "invoice",
+ schema: "invoice_reminder",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ user_id = table.Column(type: "uuid", nullable: false),
+ bank = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ beneficiary = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ amount = table.Column(type: "numeric", nullable: false),
+ barcode = table.Column(type: "text", nullable: false),
+ due_date = table.Column(type: "timestamp with time zone", nullable: false),
+ created_at = table.Column(type: "timestamp with time zone", nullable: false),
+ updated_at = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ _ = table.PrimaryKey("PK_invoice", x => x.id);
+ _ = table.ForeignKey(
+ name: "FK_invoice_user_user_id",
+ column: x => x.user_id,
+ principalSchema: "invoice_reminder",
+ principalTable: "user",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ _ = migrationBuilder.CreateTable(
+ name: "job_schedule",
+ schema: "invoice_reminder",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ user_id = table.Column(type: "uuid", nullable: false),
+ cron_expression = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ created_at = table.Column(type: "timestamp with time zone", nullable: false),
+ updated_at = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ _ = table.PrimaryKey("PK_job_schedule", x => x.id);
+ _ = table.ForeignKey(
+ name: "FK_job_schedule_user_user_id",
+ column: x => x.user_id,
+ principalSchema: "invoice_reminder",
+ principalTable: "user",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ _ = migrationBuilder.CreateTable(
+ name: "scan_email_definition",
+ schema: "invoice_reminder",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ user_id = table.Column(type: "uuid", nullable: false),
+ invoice_type = table.Column(type: "integer", nullable: false),
+ beneficiary = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ description = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ sender_email_address = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ attachment_filename = table.Column(type: "character varying(255)", maxLength: 255, nullable: false),
+ created_at = table.Column(type: "timestamp with time zone", nullable: false),
+ updated_at = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ _ = table.PrimaryKey("PK_scan_email_definition", x => x.id);
+ _ = table.ForeignKey(
+ name: "FK_scan_email_definition_user_user_id",
+ column: x => x.user_id,
+ principalSchema: "invoice_reminder",
+ principalTable: "user",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ _ = migrationBuilder.CreateIndex(
+ name: "IX_email_auth_token_user_id",
+ schema: "invoice_reminder",
+ table: "email_auth_token",
+ column: "user_id");
+
+ _ = migrationBuilder.CreateIndex(
+ name: "IX_invoice_user_id",
+ schema: "invoice_reminder",
+ table: "invoice",
+ column: "user_id");
+
+ _ = migrationBuilder.CreateIndex(
+ name: "IX_job_schedule_user_id",
+ schema: "invoice_reminder",
+ table: "job_schedule",
+ column: "user_id");
+
+ _ = migrationBuilder.CreateIndex(
+ name: "IX_scan_email_definition_user_id",
+ schema: "invoice_reminder",
+ table: "scan_email_definition",
+ column: "user_id");
+
+ _ = migrationBuilder.CreateIndex(
+ name: "idx_user_email",
+ schema: "invoice_reminder",
+ table: "user",
+ column: "email",
+ unique: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ _ = migrationBuilder.DropTable(
+ name: "email_auth_token",
+ schema: "invoice_reminder");
+
+ _ = migrationBuilder.DropTable(
+ name: "invoice",
+ schema: "invoice_reminder");
+
+ _ = migrationBuilder.DropTable(
+ name: "job_schedule",
+ schema: "invoice_reminder");
+
+ _ = migrationBuilder.DropTable(
+ name: "scan_email_definition",
+ schema: "invoice_reminder");
+
+ _ = migrationBuilder.DropTable(
+ name: "user",
+ schema: "invoice_reminder");
}
}
diff --git a/InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs b/InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs
index 306b6a8..a46456d 100644
--- a/InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs
+++ b/InvoiceReminder.Data/Persistence/EntitiesConfig/EmailAuthTokenConfig.cs
@@ -3,7 +3,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("InvoiceReminder.Infrastructure.UnitTests")]
+[assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.Infrastructure")]
namespace InvoiceReminder.Data.Persistence.EntitiesConfig;
diff --git a/InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs b/InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs
index 67c85e7..e958023 100644
--- a/InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs
+++ b/InvoiceReminder.Data/Persistence/EntitiesConfig/InvoiceConfig.cs
@@ -3,7 +3,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("InvoiceReminder.Infrastructure.UnitTests")]
+[assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.Infrastructure")]
namespace InvoiceReminder.Data.Persistence.EntitiesConfig;
diff --git a/InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs b/InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs
index 290c756..682ed70 100644
--- a/InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs
+++ b/InvoiceReminder.Data/Persistence/EntitiesConfig/JobScheduleConfig.cs
@@ -3,7 +3,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("InvoiceReminder.Infrastructure.UnitTests")]
+[assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.Infrastructure")]
namespace InvoiceReminder.Data.Persistence.EntitiesConfig;
@@ -17,11 +17,13 @@ public void Configure(EntityTypeBuilder builder)
_ = builder.Property(x => x.Id)
.HasColumnName("id")
+ .HasColumnType("uuid")
.ValueGeneratedOnAdd()
.IsRequired();
_ = builder.Property(x => x.UserId)
.HasColumnName("user_id")
+ .HasColumnType("uuid")
.IsRequired();
_ = builder.Property(x => x.CronExpression)
diff --git a/InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs b/InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs
index 4944e3d..e57c94b 100644
--- a/InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs
+++ b/InvoiceReminder.Data/Persistence/EntitiesConfig/ScanEmailDefinitionConfig.cs
@@ -3,7 +3,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("InvoiceReminder.Infrastructure.UnitTests")]
+[assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.Infrastructure")]
namespace InvoiceReminder.Data.Persistence.EntitiesConfig;
@@ -23,6 +23,7 @@ public void Configure(EntityTypeBuilder builder)
_ = builder.Property(x => x.UserId)
.HasColumnName("user_id")
+ .HasColumnType("uuid")
.IsRequired();
_ = builder.Property(x => x.InvoiceType)
diff --git a/InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs b/InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs
index 8db1858..d870d57 100644
--- a/InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs
+++ b/InvoiceReminder.Data/Persistence/EntitiesConfig/UserConfig.cs
@@ -3,7 +3,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("InvoiceReminder.Infrastructure.UnitTests")]
+[assembly: InternalsVisibleTo("InvoiceReminder.UnitTests.Infrastructure")]
namespace InvoiceReminder.Data.Persistence.EntitiesConfig;
diff --git a/InvoiceReminder.Data/Repository/BaseRepository.cs b/InvoiceReminder.Data/Repository/BaseRepository.cs
index 236d3da..e839a8d 100644
--- a/InvoiceReminder.Data/Repository/BaseRepository.cs
+++ b/InvoiceReminder.Data/Repository/BaseRepository.cs
@@ -39,6 +39,21 @@ public virtual async Task BulkInsertAsync(ICollection entities, Ca
return entities.Count;
}
+ public virtual async Task BulkRemoveAsync(ICollection entities, CancellationToken cancellationToken = default)
+ {
+ await _dbContext.BulkDeleteAsync(entities, cancellationToken: cancellationToken);
+ }
+
+ public virtual async Task BulkUpdateAsync(ICollection entities, CancellationToken cancellationToken = default)
+ {
+ foreach (var entity in entities)
+ {
+ entity.GetType().GetProperty("UpdatedAt")?.SetValue(entity, DateTime.UtcNow);
+ }
+
+ await _dbContext.BulkUpdateAsync(entities, cancellationToken: cancellationToken);
+ }
+
public virtual void Remove(TEntity entity)
{
if (_dbContext.Entry(entity).State == EntityState.Detached)
diff --git a/InvoiceReminder.Data/Repository/UnitOfWork.cs b/InvoiceReminder.Data/Repository/UnitOfWork.cs
index 3091e9f..56f1c50 100644
--- a/InvoiceReminder.Data/Repository/UnitOfWork.cs
+++ b/InvoiceReminder.Data/Repository/UnitOfWork.cs
@@ -2,6 +2,7 @@
using InvoiceReminder.Data.Interfaces;
using InvoiceReminder.Data.Persistence;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging;
using System.Data;
using System.Data.Common;
@@ -14,6 +15,7 @@ public class UnitOfWork : IUnitOfWork
private readonly CoreDbContext _context;
private readonly DbConnection _connection;
private readonly ILogger _logger;
+ private const string LogExceptionMessage = "{ContextualInfo} - Exception: {Message}";
public UnitOfWork(CoreDbContext context, ILogger logger)
{
@@ -24,29 +26,47 @@ public UnitOfWork(CoreDbContext context, ILogger logger)
public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
{
- await OpenConnection(cancellationToken);
-
- using var transaction = await _connection.BeginTransactionAsync(cancellationToken);
+ IDbContextTransaction transaction = default;
try
{
+ await OpenConnection(cancellationToken);
+
+ transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
+
_ = await _context.SaveChangesAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
}
+ catch (OperationCanceledException ex) when (cancellationToken.IsCancellationRequested)
+ {
+ var method = $"{nameof(UnitOfWork)}.{nameof(SaveChangesAsync)}";
+ var contextualInfo = $"Method {method} execution was interrupted by a CancellationToken Request...";
+
+ if (_logger.IsEnabled(LogLevel.Warning))
+ {
+ _logger.LogWarning(ex, LogExceptionMessage, contextualInfo, ex.Message);
+ }
+
+ throw new OperationCanceledException(contextualInfo, ex, cancellationToken);
+ }
catch (Exception ex)
{
+ var method = $"{nameof(UnitOfWork)}.{nameof(SaveChangesAsync)}";
+ var contextualInfo = $"Exception raised. Rolling back changes >> {method}(...)";
+
if (_logger.IsEnabled(LogLevel.Error))
{
- _logger.LogError(ex, "{Message}", ex.Message);
+ _logger.LogError(ex, LogExceptionMessage, contextualInfo, ex.Message);
}
- await transaction.RollbackAsync(cancellationToken);
+ await transaction?.RollbackAsync(CancellationToken.None);
- throw new DataLayerException($"Exception raised while saving changes: {ex.Message}", ex);
+ throw new DataLayerException(contextualInfo, ex);
}
finally
{
+ transaction?.Dispose();
await CloseConnection();
}
}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/InvoiceConfigTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/InvoiceConfigTests.cs
deleted file mode 100644
index 7842369..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/InvoiceConfigTests.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using InvoiceReminder.Data.Persistence.EntitiesConfig;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.EntitiesConfig;
-
-[TestClass]
-public sealed class InvoiceConfigTests
-{
- [TestMethod]
- public void InvoiceConfig_ShouldNotThrowErrorWhenInstantiated()
- {
- // Arrange && Act
- Action action = () => _ = new InvoiceConfig();
-
- // Assert
- action.ShouldNotThrow();
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/JobScheduleConfigTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/JobScheduleConfigTests.cs
deleted file mode 100644
index 000cd87..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/JobScheduleConfigTests.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using InvoiceReminder.Data.Persistence.EntitiesConfig;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.EntitiesConfig;
-
-[TestClass]
-public sealed class JobScheduleConfigTests
-{
- [TestMethod]
- public void JobScheduleConfig_ShouldNotThrowErrorWhenInstantiated()
- {
- // Arrange && Act
- Action action = () => _ = new JobScheduleConfig();
-
- // Assert
- action.ShouldNotThrow();
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
deleted file mode 100644
index 16b63a6..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/ScanEmailDefinitionConfigTests.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using InvoiceReminder.Data.Persistence.EntitiesConfig;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.EntitiesConfig;
-
-[TestClass]
-public sealed class ScanEmailDefinitionConfigTests
-{
- [TestMethod]
- public void ScanEmailDefinitionConfig_ShouldNotThrowErrorWhenInstantiated()
- {
- // Arrange && Act
- Action action = () => _ = new ScanEmailDefinitionConfig();
-
- // Assert
- action.ShouldNotThrow();
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/UserConfigTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/UserConfigTests.cs
deleted file mode 100644
index 43b0a44..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/EntitiesConfig/UserConfigTests.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using InvoiceReminder.Data.Persistence.EntitiesConfig;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.EntitiesConfig;
-
-[TestClass]
-public sealed class UserConfigTests
-{
- [TestMethod]
- public void UserConfig_ShouldNotThrowErrorWhenInstantiated()
- {
- // Arrange && Act
- Action action = () => _ = new UserConfig();
-
- // Assert
- action.ShouldNotThrow();
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs
deleted file mode 100644
index a9ca8c5..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs
+++ /dev/null
@@ -1,349 +0,0 @@
-using Bogus;
-using InvoiceReminder.Data.Repository;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using NSubstitute;
-using Shouldly;
-using System.Linq.Expressions;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.Repository;
-
-[TestClass]
-public sealed class BaseRepositoryTests
-{
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions _contextOptions;
- private Faker _testEntityFaker;
-
- public TestContext TestContext { get; set; }
-
- public BaseRepositoryTests()
- {
- _connection = new SqliteConnection("Filename=:memory:");
- _connection.Open();
-
- _contextOptions = new DbContextOptionsBuilder()
- .UseSqlite(_connection)
- .Options;
- }
-
- [TestInitialize]
- public async Task Setup()
- {
- InitializeFaker();
- using var context = new TestDbContext(_contextOptions);
- _ = await context.Database.EnsureCreatedAsync(TestContext.CancellationToken);
- }
-
- [TestCleanup]
- public void TearDown()
- {
- _connection.Dispose();
- }
-
- private void InitializeFaker()
- {
- _testEntityFaker = new Faker()
- .RuleFor(e => e.Id, _ => Guid.NewGuid())
- .RuleFor(e => e.Name, f => f.Lorem.Word());
- }
-
- private TestDbContext CreateContext()
- {
- return new(_contextOptions);
- }
-
- [TestMethod]
- public async Task AddAsync_Should_AddEntityToDatabase()
- {
- // Arrange
- var entity = _testEntityFaker.Generate();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- // Act
- var addedEntity = await repository.AddAsync(entity, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- // Assert
- addedEntity.ShouldBeSameAs(entity);
- context.TestEntities.ShouldContain(entity);
- }
-
- [TestMethod]
- public async Task BulkInsertAsync_Should_AddMultipleEntitiesToDatabaseWithTimestamps()
- {
- // Arrange
- var entities = _testEntityFaker.Generate(2);
-
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- // Act
- var count = await repository.BulkInsertAsync(entities, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
- var total = await context.TestEntities.CountAsync(TestContext.CancellationToken);
-
- // Assert
- count.ShouldBe(entities.Count);
-
- context.ShouldSatisfyAllConditions(() =>
- {
- total.ShouldBe(entities.Count);
- context.TestEntities.ShouldAllBe(e => e.CreatedAt.HasValue && e.UpdatedAt.HasValue);
- });
- }
-
- [TestMethod]
- public async Task Remove_Should_RemoveExistingEntityFromDatabase()
- {
- // Arrange
- var entity = _testEntityFaker.Generate();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
- _ = await repository.AddAsync(entity, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- // Act
- repository.Remove(entity);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- // Assert
- context.TestEntities.ShouldNotContain(entity);
- }
-
- [TestMethod]
- public async Task Remove_Should_AttachAndRemoveDetachedEntity()
- {
- // Arrange
- var entity = _testEntityFaker.Generate();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- _ = await repository.AddAsync(entity, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- _ = context.Attach(entity);
- context.Entry(entity).State = EntityState.Detached;
-
- // Act
- repository.Remove(entity);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- // Assert
- context.TestEntities.ShouldNotContain(entity);
- }
-
- [TestMethod]
- public async Task GetByIdAsync_Should_ReturnEntityById()
- {
- // Arrange
- var entity = _testEntityFaker.Generate();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- _ = await repository.AddAsync(entity, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- // Act
- var retrievedEntity = await repository.GetByIdAsync(entity.Id, TestContext.CancellationToken);
-
- // Assert
- retrievedEntity.ShouldSatisfyAllConditions(() =>
- {
- _ = retrievedEntity.ShouldNotBeNull();
- retrievedEntity.Id.ShouldBe(entity.Id);
- retrievedEntity.Name.ShouldBe(entity.Name);
- });
- }
-
- [TestMethod]
- public async Task GetByIdAsync_Should_ReturnNullWhenEntityNotFound()
- {
- // Arrange
- var nonExistingId = Guid.NewGuid();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- // Act
- var retrievedEntity = await repository.GetByIdAsync(nonExistingId, TestContext.CancellationToken);
-
- // Assert
- retrievedEntity.ShouldBeNull();
- }
-
- [TestMethod]
- public async Task GetAll_Should_ReturnAllEntities()
- {
- // Arrange
- var entities = _testEntityFaker.Generate(2);
-
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- _ = await repository.BulkInsertAsync(entities, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- // Act
- var allEntities = repository.GetAll().ToList();
-
- // Assert
- allEntities.ShouldSatisfyAllConditions(() =>
- {
- _ = allEntities.ShouldNotBeNull();
- allEntities.Count.ShouldBe(entities.Count);
- allEntities.ShouldBeEquivalentTo(entities);
- });
- }
-
- [TestMethod]
- public async Task Update_Should_UpdateExistingEntity()
- {
- // Arrange
- var entity = _testEntityFaker.Generate();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- _ = await repository.AddAsync(entity, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- entity.Name = _testEntityFaker.Generate().Name;
-
- // Act
- var updatedEntity = repository.Update(entity);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
- var retrievedEntity = await repository.GetByIdAsync(entity.Id, TestContext.CancellationToken);
-
- // Assert
- updatedEntity.ShouldBeSameAs(entity);
-
- retrievedEntity.ShouldSatisfyAllConditions(() =>
- {
- _ = retrievedEntity.ShouldNotBeNull();
- retrievedEntity.Name.ShouldBe(entity.Name);
- });
- }
-
- [TestMethod]
- public async Task Update_Should_AttachAndUpdateDetachedEntity()
- {
- // Arrange
- var entity = _testEntityFaker.Generate();
- using var context = CreateContext();
- var repository = new BaseRepository(context);
-
- _ = await repository.AddAsync(entity, TestContext.CancellationToken);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
-
- _ = context.Attach(entity);
- context.Entry(entity).State = EntityState.Detached;
-
- entity.Name = _testEntityFaker.Generate().Name;
-
- // Act
- var updatedEntity = repository.Update(entity);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
- var retrievedEntity = await repository.GetByIdAsync(entity.Id, TestContext.CancellationToken);
-
- // Assert
- updatedEntity.ShouldBeSameAs(entity);
-
- retrievedEntity.ShouldSatisfyAllConditions(() =>
- {
- _ = retrievedEntity.ShouldNotBeNull();
- retrievedEntity.Name.ShouldBe(entity.Name);
- });
- }
-
- [TestMethod]
- public async Task Where_Should_ReturnEntitiesMatchingPredicate()
- {
- // Arrange
- var entity1 = _testEntityFaker.Generate();
- entity1.Name = "Test1";
-
- var entity2 = _testEntityFaker.Generate();
- entity2.Name = "AnotherTest";
-
- var entity3 = _testEntityFaker.Generate();
- entity3.Name = "Test2";
-
- var entities = new List { entity1, entity2, entity3 };
-
- using var context = CreateContext();
- context.TestEntities.AddRange(entities);
- _ = await context.SaveChangesAsync(TestContext.CancellationToken);
- var repository = new BaseRepository(context);
-
- // Act
- Expression> predicate = e => e.Name.StartsWith("Test");
- var filteredEntities = repository.Where(predicate).ToList();
-
- // Assert
- filteredEntities.ShouldSatisfyAllConditions(() =>
- {
- filteredEntities.Count.ShouldBe(2);
- filteredEntities.ShouldContain(entities[0]);
- filteredEntities.ShouldNotContain(entities[1]);
- filteredEntities.ShouldContain(entities[2]);
- });
- }
-
- [TestMethod]
- public void Dispose_Should_DisposeDbContext()
- {
- // Arrange
- var mockConnection = Substitute.For();
- var options = new DbContextOptionsBuilder()
- .UseSqlite(mockConnection)
- .Options;
- var mockDbContext = Substitute.For(options);
- var repository = new BaseRepository(mockDbContext);
-
- // Act
- repository.Dispose();
-
- // Assert
- mockDbContext.Received(1).Dispose();
- }
-
- [TestMethod]
- public void Dispose_CalledMultipleTimes_Should_DisposeDbContextOnlyOnce()
- {
- // Arrange
- var mockConnection = Substitute.For();
- var options = new DbContextOptionsBuilder()
- .UseSqlite(mockConnection)
- .Options;
- var mockDbContext = Substitute.For(options);
- var repository = new BaseRepository(mockDbContext);
-
- // Act
- repository.Dispose();
- repository.Dispose();
-
- // Assert
- mockDbContext.Received(1).Dispose();
- }
-}
-
-public class TestDbContext : DbContext
-{
- public DbSet TestEntities { get; set; }
-
- public TestDbContext(DbContextOptions options) : base(options) { }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- _ = modelBuilder.Entity().HasKey(e => e.Id);
- base.OnModelCreating(modelBuilder);
- }
-}
-
-public class TestEntity
-{
- public Guid Id { get; set; }
- public string Name { get; set; }
- public DateTime? CreatedAt { get; set; }
- public DateTime? UpdatedAt { get; set; }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs
deleted file mode 100644
index 83ea47d..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using InvoiceReminder.Data.Interfaces;
-using InvoiceReminder.Data.Persistence;
-using InvoiceReminder.Data.Repository;
-using InvoiceReminder.Domain.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using NSubstitute;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.Repository;
-
-[TestClass]
-public sealed class InvoiceRepositoryTests
-{
- private readonly CoreDbContext _dbContext;
- private readonly ILogger _logger;
- private readonly IInvoiceRepository _repository;
-
- public TestContext TestContext { get; set; }
-
- public InvoiceRepositoryTests()
- {
- var options = new DbContextOptionsBuilder()
- .UseNpgsql(default)
- .Options;
-
- _dbContext = Substitute.ForPartsOf(options);
- _logger = Substitute.For>();
- _repository = Substitute.For();
- }
-
- [TestMethod]
- public void InvoiceRepository_ShouldBeAssignableToItsInterface_And_GenericInterface_And_GenericRepository()
- {
- // Arrange && Act
- var repository = new InvoiceRepository(_dbContext, _logger);
-
- // Assert
- repository.ShouldSatisfyAllConditions(() =>
- {
- _ = repository.ShouldBeAssignableTo();
- _ = repository.ShouldBeAssignableTo>();
- _ = repository.ShouldBeAssignableTo>();
-
- _ = repository.ShouldNotBeNull();
- _ = repository.ShouldBeOfType();
- });
- }
-
- [TestMethod]
- public async Task GetByBarcodeAsync_ShouldReturnInvoice_WhenInvoiceExists()
- {
- // Arrange
- var barcode = "12345678901234567890123456789012345678901234";
- var invoice = new Invoice { Barcode = barcode };
-
- _ = _repository.GetByBarcodeAsync(Arg.Any(), Arg.Any())
- .Returns(Task.FromResult(invoice));
-
- // Act
- var result = await _repository.GetByBarcodeAsync(barcode, TestContext.CancellationToken);
-
- // Assert
- result.ShouldSatisfyAllConditions(() =>
- {
- _ = result.ShouldBeAssignableTo();
- _ = result.ShouldBeOfType();
- _ = result.ShouldNotBeNull();
- result.Barcode.ShouldBe(barcode);
- });
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs
deleted file mode 100644
index d0d478f..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using InvoiceReminder.Data.Interfaces;
-using InvoiceReminder.Data.Persistence;
-using InvoiceReminder.Data.Repository;
-using InvoiceReminder.Domain.Entities;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using NSubstitute;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.Repository;
-
-[TestClass]
-public sealed class JobScheduleRepositoryTests
-{
- private readonly CoreDbContext _dbContext;
- private readonly ILogger _logger;
- private readonly IJobScheduleRepository _repository;
-
- public TestContext TestContext { get; set; }
-
- public JobScheduleRepositoryTests()
- {
- var options = new DbContextOptionsBuilder()
- .UseNpgsql(default)
- .Options;
-
- _dbContext = Substitute.ForPartsOf(options);
- _logger = Substitute.For>();
- _repository = Substitute.For();
- }
-
- [TestMethod]
- public void JobScheduleRepository_ShouldBeAssignableToItsInterface_And_GenericInterface_And_GenericRepository()
- {
- // Arrange && Act
- var repository = new JobScheduleRepository(_dbContext, _logger);
-
- // Assert
- repository.ShouldSatisfyAllConditions(() =>
- {
- _ = repository.ShouldBeAssignableTo();
- _ = repository.ShouldBeAssignableTo>();
- _ = repository.ShouldBeAssignableTo>();
-
- _ = repository.ShouldNotBeNull();
- _ = repository.ShouldBeOfType();
- });
- }
-
- [TestMethod]
- public async Task GetByUserIdAsync_ShouldReturnJobSchedule_WhenJobScheduleExists()
- {
- // Arrange
- var userId = Guid.NewGuid();
- var jobSchedule = new List { new() { UserId = userId } };
-
- _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any())
- .Returns(Task.FromResult>(jobSchedule));
-
- // Act
- var result = await _repository.GetByUserIdAsync(userId, TestContext.CancellationToken);
-
- // Assert
- result.ShouldSatisfyAllConditions(() =>
- {
- _ = result.ShouldNotBeNull();
- _ = result.ShouldBeOfType>();
- result.ShouldNotBeEmpty();
- result.ShouldAllBe(x => x.UserId == userId);
- });
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs
deleted file mode 100644
index 214f7b7..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-using Bogus;
-using InvoiceReminder.Data.Interfaces;
-using InvoiceReminder.Data.Persistence;
-using InvoiceReminder.Data.Repository;
-using InvoiceReminder.Domain.Entities;
-using InvoiceReminder.Domain.Enums;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using NSubstitute;
-using Shouldly;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.Repository;
-
-[TestClass]
-public sealed class ScanEmailDefinitionRepositoryTests
-{
- private readonly CoreDbContext _dbContext;
- private readonly ILogger _logger;
- private readonly IScanEmailDefinitionRepository _repository;
-
- public TestContext TestContext { get; set; }
-
- public ScanEmailDefinitionRepositoryTests()
- {
- var options = new DbContextOptionsBuilder()
- .UseNpgsql(default)
- .Options;
-
- _dbContext = Substitute.ForPartsOf(options);
- _logger = Substitute.For>();
- _repository = Substitute.For();
- }
-
- private static Faker CreateFaker(Action> configure = null)
- {
- var faker = new Faker()
- .RuleFor(s => s.Id, _ => Guid.NewGuid())
- .RuleFor(s => s.UserId, _ => Guid.NewGuid())
- .RuleFor(s => s.InvoiceType, f => f.PickRandom())
- .RuleFor(s => s.Beneficiary, f => f.Company.CompanyName())
- .RuleFor(s => s.Description, f => f.Lorem.Sentence())
- .RuleFor(s => s.SenderEmailAddress, f => f.Internet.Email())
- .RuleFor(s => s.AttachmentFileName, f => f.System.FileName());
-
- configure?.Invoke(faker);
-
- return faker;
- }
-
- [TestMethod]
- public void ScanEmailDefinitionRepository_ShouldBeAssignableToItsInterface_And_GenericInterface_And_GenericRepository()
- {
- // Arrange && Act
- var repository = new ScanEmailDefinitionRepository(_dbContext, _logger);
-
- // Assert
- repository.ShouldSatisfyAllConditions(() =>
- {
- _ = repository.ShouldBeAssignableTo();
- _ = repository.ShouldBeAssignableTo>();
- _ = repository.ShouldBeAssignableTo>();
-
- _ = repository.ShouldNotBeNull();
- _ = repository.ShouldBeOfType();
- });
- }
-
- [TestMethod]
- public async Task GetBySenderBeneficiaryAsync_ShouldReturnScanEmailDefinition_WhenScanEmailDefinitionExists()
- {
- // Arrange
- var userId = Guid.NewGuid();
- var beneficiary = new Faker().Company.CompanyName();
- var scanEmailDefinition = CreateFaker()
- .RuleFor(s => s.UserId, _ => userId)
- .RuleFor(s => s.Beneficiary, _ => beneficiary)
- .Generate();
-
- _ = _repository.GetBySenderBeneficiaryAsync(Arg.Any(), Arg.Any(), Arg.Any())
- .Returns(Task.FromResult(scanEmailDefinition));
-
- // Act
- var result = await _repository.GetBySenderBeneficiaryAsync(beneficiary, userId, TestContext.CancellationToken);
-
- // Assert
- result.ShouldSatisfyAllConditions(() =>
- {
- _ = result.ShouldNotBeNull();
- _ = result.ShouldBeOfType();
- result.UserId.ShouldBe(userId);
- result.Beneficiary.ShouldBe(beneficiary);
- });
- }
-
- [TestMethod]
- public async Task GetBySenderEmailAsync_ShouldReturnScanEmailDefinition_WhenScanEmailDefinitionExists()
- {
- // Arrange
- var userId = Guid.NewGuid();
- var senderEmail = new Faker().Internet.Email();
- var scanEmailDefinition = CreateFaker()
- .RuleFor(s => s.UserId, _ => userId)
- .RuleFor(s => s.SenderEmailAddress, _ => senderEmail)
- .Generate();
-
- _ = _repository.GetBySenderEmailAddressAsync(Arg.Any(), Arg.Any(), Arg.Any())
- .Returns(Task.FromResult(scanEmailDefinition));
-
- // Act
- var result = await _repository.GetBySenderEmailAddressAsync(senderEmail, userId, TestContext.CancellationToken);
-
- // Assert
- result.ShouldSatisfyAllConditions(() =>
- {
- _ = result.ShouldNotBeNull();
- _ = result.ShouldBeOfType();
- result.UserId.ShouldBe(userId);
- result.SenderEmailAddress.ShouldBe(senderEmail);
- });
- }
-
- [TestMethod]
- public async Task GetByUserIdAsync_ShouldReturnScanEmailDefinition_WhenScanEmailDefinitionExists()
- {
- // Arrange
- var userId = Guid.NewGuid();
- var collection = CreateFaker()
- .RuleFor(s => s.UserId, _ => userId)
- .Generate(2);
-
- _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any())
- .Returns(Task.FromResult>(collection));
-
- // Act
- var result = await _repository.GetByUserIdAsync(userId, TestContext.CancellationToken);
-
- // Assert
- result.ShouldSatisfyAllConditions(() =>
- {
- _ = result.ShouldNotBeNull();
- _ = result.ShouldBeOfType>();
- result.ShouldNotBeEmpty();
- result.ShouldContain(x => x.UserId == userId);
- result.Count().ShouldBe(2);
- });
- }
-}
diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UnitOfWorkTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UnitOfWorkTests.cs
deleted file mode 100644
index a74c583..0000000
--- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UnitOfWorkTests.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-using InvoiceReminder.Data.Exceptions;
-using InvoiceReminder.Data.Persistence;
-using InvoiceReminder.Data.Repository;
-using InvoiceReminder.Domain.Entities;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using NSubstitute;
-using Shouldly;
-using System.Data;
-
-namespace InvoiceReminder.Infrastructure.UnitTests.Data.Repository
-{
- [TestClass]
- public sealed class UnitOfWorkTests
- {
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions _contextOptions;
- private readonly ILogger _logger;
-
- public TestContext TestContext { get; set; }
-
- public UnitOfWorkTests()
- {
- _connection = new SqliteConnection("Filename=:memory:");
-
- _contextOptions = new DbContextOptionsBuilder()
- .UseSqlite(_connection)
- .Options;
-
- _logger = Substitute.For>();
- }
-
- [TestInitialize]
- public void Setup()
- {
- _connection.Open();
- }
-
- [TestCleanup]
- public void TearDown()
- {
- _connection.Dispose();
- }
-
- [TestMethod]
- public async Task SaveChangesAsync_Should_OpenConnection_BeginTransaction_SaveChanges_CommitTransaction_CloseConnection()
- {
- // Arrange
- using var context = CreateContext();
- _ = await context.Database.EnsureCreatedAsync(TestContext.CancellationToken);
- var unitOfWork = CreateUnitOfWork(context);
-
- // Act
- await unitOfWork.SaveChangesAsync(TestContext.CancellationToken);
-
- // Assert
- context.Database.GetDbConnection().State.ShouldBe(ConnectionState.Closed);
- }
-
- [TestMethod]
- public async Task SaveChangesAsync_Should_RollbackTransaction_LogError_AndThrowDataLayerException_OnException()
- {
- // Arrange
- using var context = CreateContext();
- _ = await context.Database.EnsureCreatedAsync(TestContext.CancellationToken);
- var unitOfWork = CreateUnitOfWork(context);
-
- _ = context.Users.Add(new User { Id = Guid.NewGuid() });
- _ = _logger.IsEnabled(Arg.Any()).Returns(true);
-
- // Act
- var dataLayerException = await Should.ThrowAsync(
- async () => await unitOfWork.SaveChangesAsync(TestContext.CancellationToken)
- );
-
- // Assert
- context.Database.GetDbConnection().State.ShouldBe(ConnectionState.Closed);
-
- _ = dataLayerException.ShouldNotBeNull();
- _ = dataLayerException.InnerException.ShouldBeOfType();
- dataLayerException.Message.ShouldContain("Exception raised while saving changes");
-
- var eventId = Arg.Any();
- var state = Arg.Any