From 4e793f68ed748a30d29c2d8e54d3b0cf3a3653f2 Mon Sep 17 00:00:00 2001 From: Alexander Preibisch Date: Tue, 3 Sep 2024 16:38:55 +0200 Subject: [PATCH] Rework Unit Tests and Code cleanup after dropping Sqlite support #239 #246 --- .../workflows/docker-image-pre-release.yml | 17 + CHANGELOG.md | 4 + OpenBudgeteer.API/Program.cs | 7 +- .../OpenBudgeteer.Blazor.csproj | 2 - OpenBudgeteer.Blazor/Program.cs | 3 +- .../Shared/BucketSelectDialog.razor | 1 - .../Repositories/IBaseRepository.cs | 4 +- .../Repositories/IBucketRepository.cs | 6 +- .../IBudgetedTransactionRepository.cs | 3 +- .../AccountRepository.cs | 4 +- .../BankTransactionRepository.cs | 4 +- .../BucketGroupRepository.cs | 4 +- .../BucketMovementRepository.cs | 4 +- .../BucketRepository.cs | 8 +- .../BucketRuleSetRepository.cs | 4 +- .../BucketVersionRepository.cs | 4 +- .../BudgetedTransactionRepository.cs | 18 +- .../ImportProfileRepository.cs | 4 +- .../MappingRuleRepository.cs | 4 +- .../RecurringBankTransactionRepository.cs | 4 +- .../BankTransactionService.cs | 169 ----- .../BaseService.cs | 102 --- .../BucketGroupService.cs | 217 ------- .../BucketMovementService.cs | 61 -- .../BucketRuleSetService.cs | 139 ---- .../BucketService.cs | 455 ------------- .../BudgetedTransactionService.cs | 172 ----- .../EFCore/EFCoreAccountService.cs | 47 ++ .../EFCore/EFCoreBankTransactionService.cs | 99 +++ .../EFCore/EFCoreBaseService.cs | 115 ++++ .../EFCore/EFCoreBucketGroupService.cs | 93 +++ .../EFCore/EFCoreBucketMovementService.cs | 58 ++ .../EFCore/EFCoreBucketRuleSetService.cs | 113 ++++ .../EFCore/EFCoreBucketService.cs | 267 ++++++++ .../EFCoreBudgetedTransactionService.cs | 138 ++++ .../EFCore/EFCoreImportProfileService.cs | 23 + .../EFCoreRecurringBankTransactionService.cs | 96 +++ .../EFCore/EFCoreServiceManager.cs | 25 + .../Exceptions/EntityNotFoundException.cs | 7 + .../Exceptions/EntityUpdateException.cs | 7 + .../GenericAccountService.cs} | 45 +- .../Generic/GenericBankTransactionService.cs | 105 +++ .../Generic/GenericBaseService.cs | 49 ++ .../Generic/GenericBucketGroupService.cs | 136 ++++ .../Generic/GenericBucketMovementService.cs | 42 ++ .../Generic/GenericBucketRuleSetService.cs | 81 +++ .../Generic/GenericBucketService.cs | 313 +++++++++ .../GenericBudgetedTransactionService.cs | 107 +++ .../Generic/GenericImportProfileService.cs | 16 + .../GenericRecurringBankTransactionService.cs | 94 +++ .../ImportProfileService.cs | 15 - .../RecurringBankTransactionService.cs | 134 ---- .../ServiceManager.cs | 25 - .../ConfigurationKeyConstants.cs | 2 - .../DbContextOptionsFactory.cs | 27 - .../Initialization/NoOpDatabaseInitializer.cs | 2 +- .../OnlineChecker/NoopOnlineChecker.cs | 1 - .../OpenBudgeteer.Core.Data.csproj | 2 - OpenBudgeteer.Core.Data/README.md | 5 - .../DeleteAllExtension.cs | 3 +- .../Common/TestDataGenerator.cs | 262 ++++++++ .../Mocking/MockDatabase.cs | 35 + .../Repository/MockAccountRepository.cs | 87 +++ .../MockBankTransactionRepository.cs | 116 ++++ .../Repository/MockBucketGroupRepository.cs | 111 ++++ .../MockBucketMovementRepository.cs | 101 +++ .../Repository/MockBucketRepository.cs | 271 ++++++++ .../Repository/MockBucketRuleSetRepository.cs | 119 ++++ .../Repository/MockBucketVersionRepository.cs | 101 +++ .../MockBudgetedTransactionRepository.cs | 149 +++++ .../Repository/MockImportProfileRepository.cs | 101 +++ .../Repository/MockMappingRuleRepository.cs | 101 +++ .../MockRecurringBankTransactionRepository.cs | 101 +++ .../Mocking/Services/MockAccountService.cs | 14 + .../Services/MockBankTransactionService.cs | 14 + .../Services/MockBucketGroupService.cs | 12 + .../Services/MockBucketMovementService.cs | 12 + .../Services/MockBucketRuleSetService.cs | 14 + .../Mocking/Services/MockBucketService.cs | 17 + .../MockBudgetedTransactionService.cs | 12 + .../Services/MockImportProfileService.cs | 12 + .../MockRecurringBankTransactionService.cs | 14 + .../Mocking/Services/MockServiceManager.cs | 29 + .../OpenBudgeteer.Core.Test.csproj | 5 +- OpenBudgeteer.Core.Test/TestServiceManager.cs | 124 ---- .../Tests/Database/AccountDatabaseTest.cs | 100 +++ .../Database/BankTransactionDatabaseTest.cs | 139 ++++ .../Tests/Database/BaseDatabaseTest.cs | 53 ++ .../Tests/Database/BucketDatabaseTest.cs | 169 +++++ .../Tests/Database/BucketGroupDatabaseTest.cs | 100 +++ .../Database/BucketMovementDatabaseTest.cs | 148 +++++ .../Database/BucketRuleSetDatabaseTest.cs | 129 ++++ .../Database/BucketVersionDatabaseTest.cs | 144 ++++ .../BudgetedTransactionDatabaseTest.cs | 216 ++++++ .../Database/ImportProfileDatabaseTest.cs | 164 +++++ .../Tests/Database/MappingRuleDatabaseTest.cs | 168 +++++ .../RecurringBankTransactionDatabaseTest.cs | 143 ++++ .../AccountPageViewModelTest.cs} | 44 +- .../BucketPageViewModelTest.cs} | 466 +++++++------ .../PageViewModels/ImportPageViewModelTest.cs | 582 +++++++++++++++++ .../TransactionPageViewModelTest.cs} | 66 +- .../YearMonthSelectorViewModelTest.cs | 20 +- .../AccountViewModelIsolatedTest.cs | 43 -- .../ViewModelTest/BaseTest.cs | 20 - .../BucketViewModelIsolatedTest.cs | 128 ---- .../ViewModelTest/ImportDataViewModelTest.cs | 613 ------------------ OpenBudgeteer.Core.Test/xunit.runner.json | 4 + OpenBudgeteer.Core/OpenBudgeteer.Core.csproj | 1 - OpenBudgeteer.sln | 6 - 109 files changed, 6547 insertions(+), 2769 deletions(-) delete mode 100644 OpenBudgeteer.Core.Data.Services/BankTransactionService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/BaseService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/BucketGroupService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/BucketMovementService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/BucketRuleSetService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/BucketService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/BudgetedTransactionService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreAccountService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBankTransactionService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBaseService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketGroupService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketMovementService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketRuleSetService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBudgetedTransactionService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreImportProfileService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreRecurringBankTransactionService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/EFCore/EFCoreServiceManager.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Exceptions/EntityNotFoundException.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Exceptions/EntityUpdateException.cs rename OpenBudgeteer.Core.Data.Services/{AccountService.cs => Generic/GenericAccountService.cs} (51%) create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBankTransactionService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBaseService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBucketGroupService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBucketMovementService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBucketRuleSetService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBucketService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericBudgetedTransactionService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericImportProfileService.cs create mode 100644 OpenBudgeteer.Core.Data.Services/Generic/GenericRecurringBankTransactionService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/ImportProfileService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/RecurringBankTransactionService.cs delete mode 100644 OpenBudgeteer.Core.Data.Services/ServiceManager.cs rename OpenBudgeteer.Core.Test/{Extension => Common}/DeleteAllExtension.cs (82%) create mode 100644 OpenBudgeteer.Core.Test/Common/TestDataGenerator.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/MockDatabase.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockAccountRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBankTransactionRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketGroupRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketMovementRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRuleSetRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketVersionRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockBudgetedTransactionRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockImportProfileRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockMappingRuleRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Repository/MockRecurringBankTransactionRepository.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockAccountService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockBankTransactionService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockBucketGroupService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockBucketMovementService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockBucketRuleSetService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockBucketService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockBudgetedTransactionService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockImportProfileService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockRecurringBankTransactionService.cs create mode 100644 OpenBudgeteer.Core.Test/Mocking/Services/MockServiceManager.cs delete mode 100644 OpenBudgeteer.Core.Test/TestServiceManager.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/AccountDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BankTransactionDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BaseDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BucketDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BucketGroupDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BucketMovementDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BucketRuleSetDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BucketVersionDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/BudgetedTransactionDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/ImportProfileDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/MappingRuleDatabaseTest.cs create mode 100644 OpenBudgeteer.Core.Test/Tests/Database/RecurringBankTransactionDatabaseTest.cs rename OpenBudgeteer.Core.Test/{ViewModelTest/AccountViewModelTest.cs => Tests/PageViewModels/AccountPageViewModelTest.cs} (50%) rename OpenBudgeteer.Core.Test/{ViewModelTest/BucketViewModelTest.cs => Tests/PageViewModels/BucketPageViewModelTest.cs} (72%) create mode 100644 OpenBudgeteer.Core.Test/Tests/PageViewModels/ImportPageViewModelTest.cs rename OpenBudgeteer.Core.Test/{ViewModelTest/TransactionViewModelTest.cs => Tests/PageViewModels/TransactionPageViewModelTest.cs} (90%) rename OpenBudgeteer.Core.Test/{ViewModelTest => Tests/PageViewModels}/YearMonthSelectorViewModelTest.cs (84%) delete mode 100644 OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelIsolatedTest.cs delete mode 100644 OpenBudgeteer.Core.Test/ViewModelTest/BaseTest.cs delete mode 100644 OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelIsolatedTest.cs delete mode 100644 OpenBudgeteer.Core.Test/ViewModelTest/ImportDataViewModelTest.cs create mode 100644 OpenBudgeteer.Core.Test/xunit.runner.json diff --git a/.github/workflows/docker-image-pre-release.yml b/.github/workflows/docker-image-pre-release.yml index 2e982fc..9bde8bb 100644 --- a/.github/workflows/docker-image-pre-release.yml +++ b/.github/workflows/docker-image-pre-release.yml @@ -11,6 +11,17 @@ jobs: test: runs-on: ubuntu-latest name: Run Test Cases + + services: + mariadb: + image: mariadb:latest + ports: + - 3306:3306 + env: + MYSQL_DATABASE: openbudgeteer_test + MYSQL_USER: openbudgeteer_test + MYSQL_PASSWORD: openbudgeteer_test + steps: - name: Check out repo uses: actions/checkout@v3 @@ -27,6 +38,12 @@ jobs: run: dotnet build OpenBudgeteer.Blazor --configuration Release --no-restore - name: Run Core Test Cases + env: + CONNECTION_SERVER: localhost + CONNECTION_PORT: 3306 + CONNECTION_USER: openbudgeteer_test + CONNECTION_PASSWORD: openbudgeteer_test + CONNECTION_DATABASE: openbudgeteer_test run: dotnet test OpenBudgeteer.Core.Test deploy-docker-app: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 5115c58..0d16572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ * Migrated reports from ChartJs.Blazor to Blazor-ApexCharts * Bucket Movements will be now stored with the current date instead on first of the month [#240](https://github.com/TheAxelander/OpenBudgeteer/issues/240) Thanks [Lucaber](https://github.com/Lucaber) +### :hammer: Maintenance + +* Rework Unit Tests and Code cleanup after dropping Sqlite support [#239](https://github.com/TheAxelander/OpenBudgeteer/issues/239) [#246](https://github.com/TheAxelander/OpenBudgeteer/issues/246) + ## 1.8.3 (2024-05-20) ### :warning: Breaking Changes diff --git a/OpenBudgeteer.API/Program.cs b/OpenBudgeteer.API/Program.cs index 16520dd..cf79220 100644 --- a/OpenBudgeteer.API/Program.cs +++ b/OpenBudgeteer.API/Program.cs @@ -9,6 +9,7 @@ using OpenBudgeteer.Core.Data.Entities; using OpenBudgeteer.Core.Data.Entities.Models; using OpenBudgeteer.Core.Data.Services; +using OpenBudgeteer.Core.Data.Services.EFCore; using Swashbuckle.AspNetCore.SwaggerGen; var builder = WebApplication.CreateBuilder(args); @@ -30,8 +31,8 @@ options.GroupNameFormat = "'v'VVV"; }); builder.Services.AddDatabase(builder.Configuration); -builder.Services.AddScoped(x => - new ServiceManager(x.GetRequiredService>())); +builder.Services.AddScoped(x => + new EFCoreServiceManager(x.GetRequiredService>())); var app = builder.Build(); @@ -41,7 +42,7 @@ // "api-supported-versions" and "api-deprecated-versions" .ReportApiVersions() .Build(); -var serviceManager = new ServiceManager(app.Services.GetRequiredService>()); +var serviceManager = new EFCoreServiceManager(app.Services.GetRequiredService>()); #region AccountService diff --git a/OpenBudgeteer.Blazor/OpenBudgeteer.Blazor.csproj b/OpenBudgeteer.Blazor/OpenBudgeteer.Blazor.csproj index 351824a..050a7e0 100644 --- a/OpenBudgeteer.Blazor/OpenBudgeteer.Blazor.csproj +++ b/OpenBudgeteer.Blazor/OpenBudgeteer.Blazor.csproj @@ -15,7 +15,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -25,7 +24,6 @@ - diff --git a/OpenBudgeteer.Blazor/Program.cs b/OpenBudgeteer.Blazor/Program.cs index d11d58b..2193f68 100644 --- a/OpenBudgeteer.Blazor/Program.cs +++ b/OpenBudgeteer.Blazor/Program.cs @@ -11,6 +11,7 @@ using OpenBudgeteer.Core.Data.Contracts.Services; using OpenBudgeteer.Core.Data.Entities; using OpenBudgeteer.Core.Data.Services; +using OpenBudgeteer.Core.Data.Services.EFCore; using OpenBudgeteer.Core.ViewModels.Helper; using Tewr.Blazor.FileReader; @@ -27,7 +28,7 @@ builder.Services.AddFileReaderService(); builder.Services.AddHostedService(); builder.Services.AddDatabase(builder.Configuration); -builder.Services.AddScoped(x => new ServiceManager(x.GetRequiredService>())); +builder.Services.AddScoped(x => new EFCoreServiceManager(x.GetRequiredService>())); builder.Services.AddScoped(x => new YearMonthSelectorViewModel(x.GetRequiredService())); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Required to read ANSI Text files diff --git a/OpenBudgeteer.Blazor/Shared/BucketSelectDialog.razor b/OpenBudgeteer.Blazor/Shared/BucketSelectDialog.razor index 69d1519..4f11fbc 100644 --- a/OpenBudgeteer.Blazor/Shared/BucketSelectDialog.razor +++ b/OpenBudgeteer.Blazor/Shared/BucketSelectDialog.razor @@ -1,7 +1,6 @@ @using OpenBudgeteer.Core.ViewModels @using System.Drawing @using System.Globalization -@using OpenBudgeteer.Core.Data @using OpenBudgeteer.Core.Data.Entities.Models @using OpenBudgeteer.Core.ViewModels.EntityViewModels @using OpenBudgeteer.Core.ViewModels.Helper diff --git a/OpenBudgeteer.Core.Data.Contracts/Repositories/IBaseRepository.cs b/OpenBudgeteer.Core.Data.Contracts/Repositories/IBaseRepository.cs index 1e938b2..cc07b4b 100644 --- a/OpenBudgeteer.Core.Data.Contracts/Repositories/IBaseRepository.cs +++ b/OpenBudgeteer.Core.Data.Contracts/Repositories/IBaseRepository.cs @@ -5,8 +5,8 @@ namespace OpenBudgeteer.Core.Data.Contracts.Repositories; public interface IBaseRepository where T : IEntity { - IQueryable All(); - IQueryable AllWithIncludedEntities(); + IEnumerable All(); + IEnumerable AllWithIncludedEntities(); T? ById(Guid id); T? ByIdWithIncludedEntities(Guid id); int Create(T entity); diff --git a/OpenBudgeteer.Core.Data.Contracts/Repositories/IBucketRepository.cs b/OpenBudgeteer.Core.Data.Contracts/Repositories/IBucketRepository.cs index 1a1838d..01e26bf 100644 --- a/OpenBudgeteer.Core.Data.Contracts/Repositories/IBucketRepository.cs +++ b/OpenBudgeteer.Core.Data.Contracts/Repositories/IBucketRepository.cs @@ -4,5 +4,9 @@ namespace OpenBudgeteer.Core.Data.Contracts.Repositories; public interface IBucketRepository : IBaseRepository { - + public Bucket? ByIdWithVersions(Guid id); + public Bucket? ByIdWithMovements(Guid id); + public Bucket? ByIdWithTransactions(Guid id); + public IEnumerable AllWithVersions(); + public IEnumerable AllWithActivities(); } \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Contracts/Repositories/IBudgetedTransactionRepository.cs b/OpenBudgeteer.Core.Data.Contracts/Repositories/IBudgetedTransactionRepository.cs index 86e6116..323fcdc 100644 --- a/OpenBudgeteer.Core.Data.Contracts/Repositories/IBudgetedTransactionRepository.cs +++ b/OpenBudgeteer.Core.Data.Contracts/Repositories/IBudgetedTransactionRepository.cs @@ -4,5 +4,6 @@ namespace OpenBudgeteer.Core.Data.Contracts.Repositories; public interface IBudgetedTransactionRepository : IBaseRepository { - + public IEnumerable AllWithTransactions(); + public BudgetedTransaction? ByIdWithTransaction(Guid id); } \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Repository/AccountRepository.cs b/OpenBudgeteer.Core.Data.Repository/AccountRepository.cs index ead1826..6bed6c5 100644 --- a/OpenBudgeteer.Core.Data.Repository/AccountRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/AccountRepository.cs @@ -14,10 +14,10 @@ public AccountRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.Account + public IEnumerable All() => DatabaseContext.Account .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.Account + public IEnumerable AllWithIncludedEntities() => DatabaseContext.Account .AsNoTracking(); public Account? ById(Guid id) => DatabaseContext.Account diff --git a/OpenBudgeteer.Core.Data.Repository/BankTransactionRepository.cs b/OpenBudgeteer.Core.Data.Repository/BankTransactionRepository.cs index fcda13d..40bc6f0 100644 --- a/OpenBudgeteer.Core.Data.Repository/BankTransactionRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BankTransactionRepository.cs @@ -15,10 +15,10 @@ public BankTransactionRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.BankTransaction + public IEnumerable All() => DatabaseContext.BankTransaction .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.BankTransaction + public IEnumerable AllWithIncludedEntities() => DatabaseContext.BankTransaction .Include(i => i.Account) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/BucketGroupRepository.cs b/OpenBudgeteer.Core.Data.Repository/BucketGroupRepository.cs index 7993c4c..094829f 100644 --- a/OpenBudgeteer.Core.Data.Repository/BucketGroupRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BucketGroupRepository.cs @@ -14,10 +14,10 @@ public BucketGroupRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.BucketGroup + public IEnumerable All() => DatabaseContext.BucketGroup .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.BucketGroup + public IEnumerable AllWithIncludedEntities() => DatabaseContext.BucketGroup .Include(i => i.Buckets) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/BucketMovementRepository.cs b/OpenBudgeteer.Core.Data.Repository/BucketMovementRepository.cs index c59e1f6..39b7164 100644 --- a/OpenBudgeteer.Core.Data.Repository/BucketMovementRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BucketMovementRepository.cs @@ -14,10 +14,10 @@ public BucketMovementRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.BucketMovement + public IEnumerable All() => DatabaseContext.BucketMovement .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.BucketMovement + public IEnumerable AllWithIncludedEntities() => DatabaseContext.BucketMovement .Include(i => i.Bucket) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/BucketRepository.cs b/OpenBudgeteer.Core.Data.Repository/BucketRepository.cs index d4a273c..7c9f5c2 100644 --- a/OpenBudgeteer.Core.Data.Repository/BucketRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BucketRepository.cs @@ -14,19 +14,19 @@ public BucketRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.Bucket + public IEnumerable All() => DatabaseContext.Bucket .AsNoTracking(); - public IQueryable AllWithVersions() => DatabaseContext.Bucket + public IEnumerable AllWithVersions() => DatabaseContext.Bucket .Include(i => i.BucketVersions) .AsNoTracking(); - public IQueryable AllWithActivities() => DatabaseContext.Bucket + public IEnumerable AllWithActivities() => DatabaseContext.Bucket .Include(i => i.BucketMovements) .Include(i => i.BudgetedTransactions)!.ThenInclude(i => i.Transaction) .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.Bucket + public IEnumerable AllWithIncludedEntities() => DatabaseContext.Bucket .Include(i => i.BucketGroup) .Include(i => i.BucketMovements) .Include(i => i.BucketVersions) diff --git a/OpenBudgeteer.Core.Data.Repository/BucketRuleSetRepository.cs b/OpenBudgeteer.Core.Data.Repository/BucketRuleSetRepository.cs index 044c579..7c9729e 100644 --- a/OpenBudgeteer.Core.Data.Repository/BucketRuleSetRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BucketRuleSetRepository.cs @@ -15,10 +15,10 @@ public BucketRuleSetRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.BucketRuleSet + public IEnumerable All() => DatabaseContext.BucketRuleSet .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.BucketRuleSet + public IEnumerable AllWithIncludedEntities() => DatabaseContext.BucketRuleSet .Include(i => i.TargetBucket) .Include(i => i.MappingRules) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/BucketVersionRepository.cs b/OpenBudgeteer.Core.Data.Repository/BucketVersionRepository.cs index a407b35..1cfa25f 100644 --- a/OpenBudgeteer.Core.Data.Repository/BucketVersionRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BucketVersionRepository.cs @@ -14,10 +14,10 @@ public BucketVersionRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.BucketVersion + public IEnumerable All() => DatabaseContext.BucketVersion .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.BucketVersion + public IEnumerable AllWithIncludedEntities() => DatabaseContext.BucketVersion .Include(i => i.Bucket) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/BudgetedTransactionRepository.cs b/OpenBudgeteer.Core.Data.Repository/BudgetedTransactionRepository.cs index 35b77e3..6f8895d 100644 --- a/OpenBudgeteer.Core.Data.Repository/BudgetedTransactionRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/BudgetedTransactionRepository.cs @@ -15,15 +15,15 @@ public BudgetedTransactionRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.BudgetedTransaction + public IEnumerable All() => DatabaseContext.BudgetedTransaction .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.BudgetedTransaction + public IEnumerable AllWithIncludedEntities() => DatabaseContext.BudgetedTransaction .Include(i => i.Bucket) .Include(i => i.Transaction) .AsNoTracking(); - public IQueryable AllWithTransactions() => DatabaseContext.BudgetedTransaction + public IEnumerable AllWithTransactions() => DatabaseContext.BudgetedTransaction .Include(i => i.Transaction) .Include(i => i.Transaction.Account) .AsNoTracking(); @@ -55,14 +55,18 @@ public int CreateRange(IEnumerable entities) public int Update(BudgetedTransaction entity) { - DatabaseContext.BudgetedTransaction.Update(entity); - return DatabaseContext.SaveChanges(); + // DatabaseContext.BudgetedTransaction.Update(entity); + // return DatabaseContext.SaveChanges(); + throw new NotSupportedException( + $"{typeof(BudgetedTransaction)} should not be updated, instead delete and re-create"); } public int UpdateRange(IEnumerable entities) { - DatabaseContext.BudgetedTransaction.UpdateRange(entities); - return DatabaseContext.SaveChanges(); + // DatabaseContext.BudgetedTransaction.UpdateRange(entities); + // return DatabaseContext.SaveChanges(); + throw new NotSupportedException( + $"{typeof(BudgetedTransaction)} should not be updated, instead delete and re-create"); } public int Delete(Guid id) diff --git a/OpenBudgeteer.Core.Data.Repository/ImportProfileRepository.cs b/OpenBudgeteer.Core.Data.Repository/ImportProfileRepository.cs index 24e32d9..d7e56ed 100644 --- a/OpenBudgeteer.Core.Data.Repository/ImportProfileRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/ImportProfileRepository.cs @@ -15,10 +15,10 @@ public ImportProfileRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.ImportProfile + public IEnumerable All() => DatabaseContext.ImportProfile .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.ImportProfile + public IEnumerable AllWithIncludedEntities() => DatabaseContext.ImportProfile .Include(i => i.Account) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/MappingRuleRepository.cs b/OpenBudgeteer.Core.Data.Repository/MappingRuleRepository.cs index 17a3fde..d003b27 100644 --- a/OpenBudgeteer.Core.Data.Repository/MappingRuleRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/MappingRuleRepository.cs @@ -14,10 +14,10 @@ public MappingRuleRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.MappingRule + public IEnumerable All() => DatabaseContext.MappingRule .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.MappingRule + public IEnumerable AllWithIncludedEntities() => DatabaseContext.MappingRule .Include(i => i.BucketRuleSet) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Repository/RecurringBankTransactionRepository.cs b/OpenBudgeteer.Core.Data.Repository/RecurringBankTransactionRepository.cs index b69a7ec..75ac927 100644 --- a/OpenBudgeteer.Core.Data.Repository/RecurringBankTransactionRepository.cs +++ b/OpenBudgeteer.Core.Data.Repository/RecurringBankTransactionRepository.cs @@ -15,10 +15,10 @@ public RecurringBankTransactionRepository(DatabaseContext databaseContext) DatabaseContext = databaseContext; } - public IQueryable All() => DatabaseContext.RecurringBankTransaction + public IEnumerable All() => DatabaseContext.RecurringBankTransaction .AsNoTracking(); - public IQueryable AllWithIncludedEntities() => DatabaseContext.RecurringBankTransaction + public IEnumerable AllWithIncludedEntities() => DatabaseContext.RecurringBankTransaction .Include(i => i.Account) .AsNoTracking(); diff --git a/OpenBudgeteer.Core.Data.Services/BankTransactionService.cs b/OpenBudgeteer.Core.Data.Services/BankTransactionService.cs deleted file mode 100644 index 50e80a9..0000000 --- a/OpenBudgeteer.Core.Data.Services/BankTransactionService.cs +++ /dev/null @@ -1,169 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class BankTransactionService : BaseService, IBankTransactionService -{ - internal BankTransactionService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new BankTransactionRepository(new DatabaseContext(dbContextOptions))) - { - } - - public BankTransaction GetWithEntities(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BankTransactionRepository(dbContext); - - var result = repository.ByIdWithIncludedEntities(id); - if (result == null) throw new Exception($"{typeof(BankTransaction)} not found in database"); - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAll(DateTime? periodStart, DateTime? periodEnd, int limit = 0) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BankTransactionRepository(dbContext); - - var result = repository - .AllWithIncludedEntities() - .Where(i => - i.TransactionDate >= (periodStart ?? DateTime.MinValue) && - i.TransactionDate <= (periodEnd ?? DateTime.MaxValue)) - .OrderByDescending(i => i.TransactionDate) - .ToList(); - return limit > 0 - ? result.Take(limit) - : result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetFromAccount(Guid accountId, int limit = 0) - { - return GetFromAccount(accountId, null, null, limit); - } - - public IEnumerable GetFromAccount(Guid accountId, DateTime? periodStart, DateTime? periodEnd, int limit = 0) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BankTransactionRepository(dbContext); - - var result = repository - .AllWithIncludedEntities() - .Where(i => - i.TransactionDate >= (periodStart ?? DateTime.MinValue) && - i.TransactionDate <= (periodEnd ?? DateTime.MaxValue) && - i.AccountId == accountId) - .OrderByDescending(i => i.TransactionDate) - .ToList(); - return limit > 0 - ? result.Take(limit) - : result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable ImportTransactions(IEnumerable entities) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var repository = new BankTransactionRepository(dbContext); - - var newTransactions = entities.ToList(); - repository.CreateRange(newTransactions); - transaction.Commit(); - return newTransactions; - } - catch (Exception e) - { - transaction.Rollback(); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public override BankTransaction Update(BankTransaction entity) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var budgetedTransactionRepository = new BudgetedTransactionRepository(dbContext); - var bankTransactionRepository = new BankTransactionRepository(dbContext); - - if (entity.BudgetedTransactions != null && entity.BudgetedTransactions.Any()) - { - // Delete all existing bucket assignments, as they will be replaced by passed assignments - var deletedIds = - budgetedTransactionRepository.All() - .Where(i => i.TransactionId == entity.Id) - .Select(i => i.Id) - .ToList(); - - if (deletedIds.Count != 0) - { - var result = budgetedTransactionRepository.DeleteRange(deletedIds); - if (result != deletedIds.Count) - throw new Exception("Unable to delete old Bucket Assignments of that Transaction"); - } - - // Reset all Guid for re-creation - foreach (var budgetedTransaction in entity.BudgetedTransactions) - { - budgetedTransaction.Id = Guid.Empty; - } - } - - // Update BankTransaction including bucket assignments (if available) in DB - bankTransactionRepository.Update(entity); - - transaction.Commit(); - return entity; - } - catch (Exception e) - { - transaction.Rollback(); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public override void Delete(Guid id) - { - using var dbContext = new DatabaseContext(DbContextOptions); - try - { - var bankTransactionRepository = new BankTransactionRepository(dbContext); - var result = bankTransactionRepository.Delete(id); - if (result == 0) throw new Exception("Unable to delete Bank Transaction"); - } - catch (Exception e) - { - throw new Exception($"Errors during database update: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/BaseService.cs b/OpenBudgeteer.Core.Data.Services/BaseService.cs deleted file mode 100644 index a9a73e8..0000000 --- a/OpenBudgeteer.Core.Data.Services/BaseService.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Repositories; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; - -namespace OpenBudgeteer.Core.Data.Services; - -internal abstract class BaseService : IBaseService - where TEntity : class, IEntity -{ - protected readonly DbContextOptions DbContextOptions; - private readonly IBaseRepository _baseRepository; - - protected BaseService(DbContextOptions dbContextOptions, IBaseRepository baseRepository) - { - DbContextOptions = dbContextOptions; - _baseRepository = baseRepository; - } - - public virtual TEntity Get(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - - var result = _baseRepository.ById(id); - if (result == null) throw new Exception($"{typeof(TEntity)} not found in database"); - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public virtual IEnumerable GetAll() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - - return _baseRepository.All().ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public virtual TEntity Create(TEntity entity) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - - var result = _baseRepository.Create(entity); - if (result == 0) throw new Exception($"Unable to create {typeof(TEntity)} in database"); - return entity; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public virtual TEntity Update(TEntity entity) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - - var result = _baseRepository.Update(entity); - if (result == 0) throw new Exception($"Unable to update {typeof(TEntity)} in database"); - return entity; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public virtual void Delete(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - - var result = _baseRepository.Delete(id); - if (result == 0) throw new Exception($"Unable to delete {typeof(TEntity)} in database"); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/BucketGroupService.cs b/OpenBudgeteer.Core.Data.Services/BucketGroupService.cs deleted file mode 100644 index b63f527..0000000 --- a/OpenBudgeteer.Core.Data.Services/BucketGroupService.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System.Collections.ObjectModel; -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class BucketGroupService : BaseService, IBucketGroupService -{ - internal BucketGroupService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new BucketGroupRepository(new DatabaseContext(dbContextOptions))) - { - } - - public BucketGroup GetWithBuckets(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketGroupRepository(dbContext); - - var result = repository.ByIdWithIncludedEntities(id); - if (result == null) throw new Exception($"{typeof(BucketGroup)} not found in database"); - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public override IEnumerable GetAll() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketGroupRepository(dbContext); - return repository - .AllWithIncludedEntities() - .Where(i => i.Id != Guid.Parse("00000000-0000-0000-0000-000000000001")) - .OrderBy(i => i.Position) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllFull() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketGroupRepository(dbContext); - return repository - .AllWithIncludedEntities() - .OrderBy(i => i.Position) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetSystemBucketGroups() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketGroupRepository(dbContext); - return repository.AllWithIncludedEntities() - .Where(i => i.Id == Guid.Parse("00000000-0000-0000-0000-000000000001")) - .OrderBy(i => i.Position) //In case in future there are multiple groups - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public override BucketGroup Create(BucketGroup entity) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var repository = new BucketGroupRepository(dbContext); - - var allGroups = GetAll().ToList(); - var lastNewPosition = allGroups.Count + 1; - - if (entity.Position > 0) - { - // Update positions of existing BucketGroups based on requested position - // As GetAll excludes System Groups no check on 0 position required - foreach (var bucketGroup in allGroups.Where(i => i.Position >= entity.Position)) - { - bucketGroup.Position++; - repository.Update(bucketGroup); - } - - // Fix a potential too large position number - if (entity.Position > lastNewPosition) entity.Position = lastNewPosition; - - repository.Create(entity); - } - else - { - entity.Position = lastNewPosition; - repository.Create(entity); - } - - transaction.Commit(); - return entity; - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public override BucketGroup Update(BucketGroup entity) - { - //TODO Handle Position Update - return base.Update(entity); - } - - public override void Delete(Guid id) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var bucketGroupRepository = new BucketGroupRepository(dbContext); - - var entity = bucketGroupRepository.ByIdWithIncludedEntities(id); - if (entity == null) throw new Exception("BucketGroup not found"); - if (entity.Buckets != null && entity.Buckets.Any()) throw new Exception("BucketGroup with Buckets cannot be deleted"); - - var oldPosition = entity.Position; - bucketGroupRepository.Delete(id); - - // Update Positions of other Bucket Groups - foreach (var bucketGroup in GetAll().Where(i => i.Position > oldPosition)) - { - bucketGroup.Position--; - bucketGroupRepository.Update(bucketGroup); - } - - transaction.Commit(); - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public BucketGroup Move(Guid bucketGroupId, int positions) - { - var bucketGroup = Get(bucketGroupId); - if (positions == 0) return bucketGroup; - - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var repository = new BucketGroupRepository(dbContext); - - // Create in an interim List for later use - var existingBucketGroups = new ObservableCollection(); - foreach (var group in GetAll().ToList()) - { - existingBucketGroups.Add(group); - } - var bucketGroupCount = existingBucketGroups.Count(); - var targetPosition = bucketGroup.Position + positions; - if (targetPosition < 1) targetPosition = 1; - if (targetPosition > bucketGroupCount) targetPosition = bucketGroupCount; - if (targetPosition == bucketGroup.Position) return bucketGroup; // Group is already at the end or top. No further action - - // Move Group in interim List - existingBucketGroups.Move(bucketGroup.Position - 1, targetPosition - 1); - - // Update Position number - var newPosition = 1; - foreach (var group in existingBucketGroups) - { - group.Position = newPosition; - repository.Update(group); - if (group.Id == bucketGroupId) bucketGroup = group; // Use correct object reference for final return - newPosition++; - } - - transaction.Commit(); - return bucketGroup; - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/BucketMovementService.cs b/OpenBudgeteer.Core.Data.Services/BucketMovementService.cs deleted file mode 100644 index 3ff63c8..0000000 --- a/OpenBudgeteer.Core.Data.Services/BucketMovementService.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class BucketMovementService : BaseService, IBucketMovementService -{ - internal BucketMovementService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new BucketMovementRepository(new DatabaseContext(dbContextOptions))) - { - } - - public IEnumerable GetAll(DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketMovementRepository(dbContext); - return repository - .All() - .Where(i => - i.MovementDate >= periodStart && - i.MovementDate <= periodEnd) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllFromBucket(Guid bucketId) - { - return GetAllFromBucket(bucketId, DateTime.MinValue, DateTime.MaxValue); - } - - public IEnumerable GetAllFromBucket(Guid bucketId, DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketMovementRepository(dbContext); - return repository - .All() - .Where(i => - i.MovementDate >= periodStart && - i.MovementDate <= periodEnd && - i.BucketId == bucketId) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/BucketRuleSetService.cs b/OpenBudgeteer.Core.Data.Services/BucketRuleSetService.cs deleted file mode 100644 index 7f1a27c..0000000 --- a/OpenBudgeteer.Core.Data.Services/BucketRuleSetService.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class BucketRuleSetService : BaseService, IBucketRuleSetService -{ - internal BucketRuleSetService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new BucketRuleSetRepository(new DatabaseContext(dbContextOptions))) - { - } - - public override BucketRuleSet Get(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRuleSetRepository(dbContext); - - var result = repository.ByIdWithIncludedEntities(id); - if (result == null) throw new Exception($"{typeof(BucketRuleSet)} not found in database"); - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public override IEnumerable GetAll() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRuleSetRepository(dbContext); - return repository - .AllWithIncludedEntities() - .OrderBy(i => i.Priority) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetMappingRules(Guid bucketRuleSetId) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new MappingRuleRepository(dbContext); - return repository - .AllWithIncludedEntities() - .Where(i => i.BucketRuleSetId == bucketRuleSetId) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public override BucketRuleSet Update(BucketRuleSet entity) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var bucketRuleSetRepository = new BucketRuleSetRepository(dbContext); - var mappingRuleRepository = new MappingRuleRepository(dbContext); - - // Check if Mapping Rules need to be deleted - var deletedIds = - // Collect database entities - mappingRuleRepository.All() - .Where(i => i.BucketRuleSetId == entity.Id) - .ToList() - // Select which of the database IDs are no longer available in entity - .Where(i => entity.MappingRules != null && entity.MappingRules - .All(j => j.Id != i.Id)) - .Select(i => i.Id) - .ToList(); - if (deletedIds.Count != 0) - { - var result = mappingRuleRepository.DeleteRange(deletedIds); - if (result != deletedIds.Count) - throw new Exception("Unable to delete old MappingRules of that BucketRuleSet"); - } - - // Update BucketRuleSet including MappingRules - bucketRuleSetRepository.Update(entity); - - transaction.Commit(); - return entity; - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public override void Delete(Guid id) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var bucketRuleSetRepository = new BucketRuleSetRepository(dbContext); - var mappingRuleRepository = new MappingRuleRepository(dbContext); - - // Delete all existing Mapping Rules - mappingRuleRepository.DeleteRange(mappingRuleRepository - .All() - .Where(i => i.BucketRuleSetId == id) - .Select(i => i.Id) - .ToList()); - - // Delete BucketRuleSet - bucketRuleSetRepository.Delete(id); - - transaction.Commit(); - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/BucketService.cs b/OpenBudgeteer.Core.Data.Services/BucketService.cs deleted file mode 100644 index f7ed701..0000000 --- a/OpenBudgeteer.Core.Data.Services/BucketService.cs +++ /dev/null @@ -1,455 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class BucketService : BaseService, IBucketService -{ - internal BucketService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new BucketRepository(new DatabaseContext(dbContextOptions))) - { - } - - public Bucket GetWithLatestVersion(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - - var result = repository.ByIdWithVersions(id); - if (result == null) throw new Exception($"{typeof(Bucket)} not found in database"); - result.CurrentVersion = GetLatestVersion(id, DateTime.Now); - result.BucketVersions = result.BucketVersions!.OrderByDescending(i => i.Version).ToList(); - - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public override IEnumerable GetAll() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - - return repository - .All() - .Where(i => - i.Id != Guid.Parse("00000000-0000-0000-0000-000000000001") && - i.Id != Guid.Parse("00000000-0000-0000-0000-000000000002")) - .OrderBy(i => i.Name) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetSystemBuckets() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - - return repository - .All() - .Where(i => - i.Id == Guid.Parse("00000000-0000-0000-0000-000000000001") || - i.Id == Guid.Parse("00000000-0000-0000-0000-000000000002")) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetActiveBuckets(DateTime validFrom) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - - var result = repository - .AllWithVersions() - .Where(i => - // Only valid Buckets of current month - i.ValidFrom <= validFrom && - // Only active Buckets - (i.IsInactive == false || - // Alternative: Bucket is inactive as of today, but was valid in current selected month - (i.IsInactive && i.IsInactiveFrom > validFrom))) - .OrderBy(i => i.Name) - .ToList(); - foreach (var bucket in result) - { - bucket.CurrentVersion = bucket.BucketVersions! - .OrderByDescending(i => i.ValidFrom) - .ToList() - .First(i => i.ValidFrom <= validFrom); - } - - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public BucketVersion GetLatestVersion(Guid bucketId, DateTime yearMonth) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketVersionRepository(dbContext); - - var result = repository - .All() - .Where(i => i.BucketId == bucketId) - .OrderByDescending(i => i.ValidFrom) - .ToList() - .FirstOrDefault(i => i!.ValidFrom <= yearMonth, null); - if (result == null) throw new Exception("No Bucket Version found for the selected month"); - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public BucketFigures GetFigures(Guid bucketId, DateTime yearMonth) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - var bucketWithTransactions = repository.ByIdWithTransactions(bucketId) ?? throw new Exception("Bucket not found."); - var bucketWithMovements = repository.ByIdWithMovements(bucketId) ?? throw new Exception("Bucket not found."); - - decimal input = 0, output = 0; - - // Calculate Balance - var balance = bucketWithTransactions.BudgetedTransactions! - .Where(i => i.Transaction.TransactionDate < yearMonth.AddMonths(1)) - .ToList() - .Sum(i => i.Amount); - - balance += bucketWithMovements.BucketMovements! - .Where(i => i.MovementDate < yearMonth.AddMonths(1)) - .ToList() - .Sum(i => i.Amount); - - - // Calculate In & Out - var bucketTransactionsCurrentMonth = bucketWithTransactions.BudgetedTransactions! - .Where(i => - i.Transaction.TransactionDate.Year == yearMonth.Year && - i.Transaction.TransactionDate.Month == yearMonth.Month) - .ToList(); - - foreach (var bucketTransaction in bucketTransactionsCurrentMonth) - { - if (bucketTransaction.Amount < 0) - output += bucketTransaction.Amount; - else - input += bucketTransaction.Amount; - } - - var bucketMovementsCurrentMonth = bucketWithMovements.BucketMovements! - .Where(i => - i.MovementDate.Year == yearMonth.Year && - i.MovementDate.Month == yearMonth.Month) - .ToList(); - - foreach (var bucketMovement in bucketMovementsCurrentMonth) - { - if (bucketMovement.Amount < 0) - output += bucketMovement.Amount; - else - input += bucketMovement.Amount; - } - - return new BucketFigures{ Balance = balance, Input = input, Output = output }; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public decimal GetBalance(Guid bucketId, DateTime yearMonth) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - var bucketWithTransactions = repository.ByIdWithTransactions(bucketId) ?? throw new Exception("Bucket not found."); - var bucketWithMovements = repository.ByIdWithMovements(bucketId) ?? throw new Exception("Bucket not found."); - - var result = bucketWithTransactions.BudgetedTransactions! - .Where(i => i.Transaction.TransactionDate < yearMonth.AddMonths(1)) - .ToList() - .Sum(i => i.Amount); - - result += bucketWithMovements.BucketMovements! - .Where(i => i.MovementDate < yearMonth.AddMonths(1)) - .ToList() - .Sum(i => i.Amount); - - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public BucketFigures GetInAndOut(Guid bucketId, DateTime yearMonth) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketRepository(dbContext); - var bucketWithTransactions = repository.ByIdWithTransactions(bucketId) ?? throw new Exception("Bucket not found."); - var bucketWithMovements = repository.ByIdWithMovements(bucketId) ?? throw new Exception("Bucket not found."); - - decimal input = 0, output = 0; - - var bucketTransactionsCurrentMonth = bucketWithTransactions.BudgetedTransactions! - .Where(i => - i.Transaction.TransactionDate.Year == yearMonth.Year && - i.Transaction.TransactionDate.Month == yearMonth.Month) - .ToList(); - - foreach (var bucketTransaction in bucketTransactionsCurrentMonth) - { - if (bucketTransaction.Amount < 0) - output += bucketTransaction.Amount; - else - input += bucketTransaction.Amount; - } - - var bucketMovementsCurrentMonth = bucketWithMovements.BucketMovements! - .Where(i => - i.MovementDate.Year == yearMonth.Year && - i.MovementDate.Month == yearMonth.Month) - .ToList(); - - foreach (var bucketMovement in bucketMovementsCurrentMonth) - { - if (bucketMovement.Amount < 0) - output += bucketMovement.Amount; - else - input += bucketMovement.Amount; - } - - return new BucketFigures{ Balance = null, Input = input, Output = output }; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public override Bucket Create(Bucket entity) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var repository = new BucketRepository(dbContext); - if (entity.CurrentVersion == null) throw new Exception("No Bucket Version defined"); - - entity.CurrentVersion.Version = 1; - entity.BucketVersions = new List(); - entity.BucketVersions.Add(entity.CurrentVersion); - - repository.Create(entity); - - transaction.Commit(); - return entity; - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public override Bucket Update(Bucket entity) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var bucketRepository = new BucketRepository(dbContext); - - if (entity.CurrentVersion != null) - { - entity.BucketVersions = new List(); - if (entity.Id == Guid.Empty) - { - // New Bucket - Create new Version - var newVersion = entity.CurrentVersion; - newVersion.Id = Guid.Empty; - newVersion.Version = 1; - entity.BucketVersions.Add(newVersion); - } - else - { - var latestVersion = GetLatestVersion(entity.Id, DateTime.Now); - if (entity.CurrentVersion.ValidFrom == latestVersion.ValidFrom) - { - // Change in same month, overwrite latest Version - latestVersion.BucketType = entity.CurrentVersion.BucketType; - latestVersion.BucketTypeXParam = entity.CurrentVersion.BucketTypeXParam; - latestVersion.BucketTypeYParam = entity.CurrentVersion.BucketTypeYParam; - latestVersion.BucketTypeZParam = entity.CurrentVersion.BucketTypeZParam; - latestVersion.Notes = entity.CurrentVersion.Notes; - - entity.BucketVersions.Add(latestVersion); - } - else - { - // Create new Version - var newVersion = entity.CurrentVersion; - newVersion.Id = Guid.Empty; - newVersion.Version = latestVersion.Version + 1; - entity.BucketVersions.Add(newVersion); - } - } - } - - bucketRepository.Update(entity); - - transaction.Commit(); - return entity; - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public void Close(Guid id, DateTime yearMonth) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var bucketRepository = new BucketRepository(dbContext); - var budgetedTransactionRepository = new BudgetedTransactionRepository(dbContext); - var bucketMovementRepository = new BucketMovementRepository(dbContext); - - if (GetBalance(id, yearMonth) != 0) throw new Exception("Balance must be 0 to close a Bucket"); - - if (budgetedTransactionRepository.All().Any(i => i.BucketId == id) || - bucketMovementRepository.All().Any(i => i.BucketId == id)) - { - // Update: Bucket will be set to inactive for the next month - var entity = bucketRepository.ById(id); - if (entity == null) throw new Exception("Bucket not found"); - if (entity.IsInactive) throw new Exception("Bucket has been already set to inactive"); - entity.IsInactive = true; - entity.IsInactiveFrom = yearMonth.AddMonths(1); - bucketRepository.Update(entity); - } - else - { - // Delete: Bucket has no transactions & movements, so it can be directly deleted from the database - bucketRepository.Delete(id); - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public override void Delete(Guid id) - { - using var dbContext = new DatabaseContext(DbContextOptions); - using var transaction = dbContext.Database.BeginTransaction(); - try - { - var bucketRepository = new BucketRepository(dbContext); - var bucketRuleSetRepository = new BucketRuleSetRepository(dbContext); - var budgetedTransactionRepository = new BudgetedTransactionRepository(dbContext); - var bucketMovementRepository = new BucketMovementRepository(dbContext); - - if (budgetedTransactionRepository.All().Any(i => i.BucketId == id) || - bucketMovementRepository.All().Any(i => i.BucketId == id)) - { - throw new Exception("Cannot delete a Bucket with assigned Transactions or Bucket Movements"); - } - - // Delete Bucket - bucketRepository.Delete(id); - - // Delete all BucketRuleSet which refer to this Bucket - var bucketRuleSetIds = bucketRuleSetRepository - .All() - .Where(i => i.TargetBucketId == id) - .Select(i => i.Id) - .ToList(); - if (bucketRuleSetIds.Count != 0) bucketRuleSetRepository.DeleteRange(bucketRuleSetIds); - - transaction.Commit(); - } - catch (Exception e) - { - transaction.Rollback(); - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } - - public BucketMovement CreateMovement(Guid bucketId, decimal amount, DateTime movementDate) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BucketMovementRepository(dbContext); - - var newBucketMovement = new BucketMovement() - { - Id = Guid.Empty, - BucketId = bucketId, - Amount = amount, - MovementDate = movementDate - }; - var result = repository.Create(newBucketMovement); - if (result == 0) throw new Exception($"Unable to create {typeof(BucketMovement)} in database"); - return newBucketMovement; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/BudgetedTransactionService.cs b/OpenBudgeteer.Core.Data.Services/BudgetedTransactionService.cs deleted file mode 100644 index 434d346..0000000 --- a/OpenBudgeteer.Core.Data.Services/BudgetedTransactionService.cs +++ /dev/null @@ -1,172 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class BudgetedTransactionService : BaseService, IBudgetedTransactionService -{ - internal BudgetedTransactionService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new BudgetedTransactionRepository(new DatabaseContext(dbContextOptions))) - { - } - - public IEnumerable GetAll(DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BudgetedTransactionRepository(dbContext); - - return repository - .AllWithTransactions() - .Where(i => - i.Transaction.TransactionDate >= periodStart && - i.Transaction.TransactionDate <= periodEnd) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllFromTransaction(Guid transactionId) - { - return GetAllFromTransaction(transactionId, DateTime.MinValue, DateTime.MaxValue); - } - - public IEnumerable GetAllFromTransaction(Guid transactionId, DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BudgetedTransactionRepository(dbContext); - - return repository - .AllWithTransactions() - .Where(i => - i.Transaction.TransactionDate >= periodStart && - i.Transaction.TransactionDate <= periodEnd && - i.TransactionId == transactionId) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllFromBucket(Guid bucketId) - { - return GetAllFromBucket(bucketId, DateTime.MinValue, DateTime.MaxValue); - } - - public IEnumerable GetAllFromBucket(Guid bucketId, DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BudgetedTransactionRepository(dbContext); - - return repository - .AllWithTransactions() - .Where(i => - i.Transaction.TransactionDate >= periodStart && - i.Transaction.TransactionDate <= periodEnd && - i.BucketId == bucketId) - .OrderByDescending(i => i.Transaction.TransactionDate) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllNonTransfer() - { - return GetAllNonTransfer(DateTime.MinValue, DateTime.MaxValue); - } - - public IEnumerable GetAllNonTransfer(DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BudgetedTransactionRepository(dbContext); - - return repository - .AllWithTransactions() - .Where(i => - i.Transaction.TransactionDate >= periodStart && - i.Transaction.TransactionDate <= periodEnd && - i.BucketId != Guid.Parse("00000000-0000-0000-0000-000000000002")) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllTransfer() - { - return GetAllTransfer(DateTime.MinValue, DateTime.MaxValue); - } - - public IEnumerable GetAllTransfer(DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BudgetedTransactionRepository(dbContext); - - return repository - .AllWithTransactions() - .Where(i => - i.Transaction.TransactionDate >= periodStart && - i.Transaction.TransactionDate <= periodEnd && - i.BucketId == Guid.Parse("00000000-0000-0000-0000-000000000002")) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllIncome() - { - return GetAllIncome(DateTime.MinValue, DateTime.MaxValue); - } - - public IEnumerable GetAllIncome(DateTime periodStart, DateTime periodEnd) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BudgetedTransactionRepository(dbContext); - - return repository - .AllWithTransactions() - .Where(i => - i.Transaction.TransactionDate >= periodStart && - i.Transaction.TransactionDate <= periodEnd && - i.BucketId == Guid.Parse("00000000-0000-0000-0000-000000000001")) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreAccountService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreAccountService.cs new file mode 100644 index 0000000..b3ada18 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreAccountService.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreAccountService : EFCoreBaseService, IAccountService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreAccountService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericAccountService CreateBaseService(DatabaseContext dbContext) + { + return new GenericAccountService( + new AccountRepository(dbContext), + new BankTransactionRepository(dbContext)); + } + + public IEnumerable GetActiveAccounts() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var genericAccountService = CreateBaseService(dbContext); + return genericAccountService.GetActiveAccounts(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public Account CloseAccount(Guid id) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var genericAccountService = CreateBaseService(dbContext); + return genericAccountService.CloseAccount(id); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBankTransactionService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBankTransactionService.cs new file mode 100644 index 0000000..0dfbbe0 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBankTransactionService.cs @@ -0,0 +1,99 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Exceptions; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreBankTransactionService : EFCoreBaseService, IBankTransactionService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreBankTransactionService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericBankTransactionService CreateBaseService(DatabaseContext dbContext) + { + return new GenericBankTransactionService( + new BankTransactionRepository(dbContext), + new BudgetedTransactionRepository(dbContext)); + } + + public BankTransaction GetWithEntities(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var genericBankTransactionService = CreateBaseService(dbContext); + return genericBankTransactionService.GetWithEntities(id); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: Bank Transaction not found in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAll(DateTime? periodStart, DateTime? periodEnd, int limit = 0) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var genericBankTransactionService = CreateBaseService(dbContext); + return genericBankTransactionService.GetAll(periodStart, periodEnd, limit); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetFromAccount(Guid accountId, int limit = 0) + { + return GetFromAccount(accountId, null, null, limit); + } + + public IEnumerable GetFromAccount(Guid accountId, DateTime? periodStart, DateTime? periodEnd, int limit = 0) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var genericBankTransactionService = CreateBaseService(dbContext); + return genericBankTransactionService.GetFromAccount(accountId, periodStart, periodEnd, limit); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable ImportTransactions(IEnumerable entities) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var genericBankTransactionService = CreateBaseService(dbContext); + try + { + var results = genericBankTransactionService.ImportTransactions(entities); + transaction.Commit(); + return results; + } + catch (Exception e) + { + transaction.Rollback(); + throw new Exception($"Errors during database update: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBaseService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBaseService.cs new file mode 100644 index 0000000..0e5d3fe --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBaseService.cs @@ -0,0 +1,115 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public abstract class EFCoreBaseService : IBaseService + where TEntity : class, IEntity +{ + private readonly DbContextOptions _dbContextOptions; + + protected EFCoreBaseService(DbContextOptions dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected abstract IBaseService CreateBaseService(DatabaseContext dbContext); + + public virtual TEntity Get(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.Get(id); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {typeof(TEntity)} not found in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public virtual IEnumerable GetAll() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAll(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public virtual TEntity Create(TEntity entity) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.Create(entity); + } + catch (EntityUpdateException e) + { + Console.WriteLine(e); + throw new Exception($"Unable to create {typeof(TEntity)} in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public virtual TEntity Update(TEntity entity) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.Update(entity); + } + catch (EntityUpdateException e) + { + Console.WriteLine(e); + throw new Exception($"Unable to update {typeof(TEntity)} in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public virtual void Delete(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + baseService.Delete(id); + } + catch (EntityUpdateException e) + { + Console.WriteLine(e); + throw new Exception($"Unable to delete {typeof(TEntity)} in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketGroupService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketGroupService.cs new file mode 100644 index 0000000..8942e51 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketGroupService.cs @@ -0,0 +1,93 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Exceptions; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreBucketGroupService : EFCoreBaseService, IBucketGroupService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreBucketGroupService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericBucketGroupService CreateBaseService(DatabaseContext dbContext) + { + return new GenericBucketGroupService(new BucketGroupRepository(dbContext)); + } + + public BucketGroup GetWithBuckets(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetWithBuckets(id); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception($"{typeof(BucketGroup)} not found in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllFull() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllFull(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetSystemBucketGroups() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetSystemBucketGroups(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public BucketGroup Move(Guid bucketGroupId, int positions) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var baseService = CreateBaseService(dbContext); + try + { + var results = baseService.Move(bucketGroupId, positions); + transaction.Commit(); + return results; + } + catch (Exception e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception($"Error during database update: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketMovementService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketMovementService.cs new file mode 100644 index 0000000..2f73d02 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketMovementService.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreBucketMovementService : EFCoreBaseService, IBucketMovementService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreBucketMovementService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericBucketMovementService CreateBaseService(DatabaseContext dbContext) + { + return new GenericBucketMovementService(new BucketMovementRepository(dbContext)); + } + + public IEnumerable GetAll(DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAll(periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllFromBucket(Guid bucketId) + { + return GetAllFromBucket(bucketId, DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllFromBucket(Guid bucketId, DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllFromBucket(bucketId, periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketRuleSetService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketRuleSetService.cs new file mode 100644 index 0000000..78bbcdc --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketRuleSetService.cs @@ -0,0 +1,113 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Exceptions; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreBucketRuleSetService : EFCoreBaseService, IBucketRuleSetService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreBucketRuleSetService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericBucketRuleSetService CreateBaseService(DatabaseContext dbContext) + { + return new GenericBucketRuleSetService( + new BucketRuleSetRepository(dbContext), + new MappingRuleRepository(dbContext)); + } + + public override BucketRuleSet Get(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.Get(id); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception($"{typeof(BucketRuleSet)} not found in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public override IEnumerable GetAll() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAll(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetMappingRules(Guid bucketRuleSetId) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetMappingRules(bucketRuleSetId); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public override BucketRuleSet Update(BucketRuleSet entity) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var baseService = CreateBaseService(dbContext); + try + { + var results = baseService.Update(entity); + transaction.Commit(); + return results; + } + catch (Exception e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public override void Delete(Guid id) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var baseService = CreateBaseService(dbContext); + try + { + baseService.Delete(id); + transaction.Commit(); + } + catch (Exception e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketService.cs new file mode 100644 index 0000000..7a06cfd --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBucketService.cs @@ -0,0 +1,267 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Exceptions; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreBucketService : EFCoreBaseService, IBucketService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreBucketService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericBucketService CreateBaseService(DatabaseContext dbContext) + { + return new GenericBucketService( + new BucketRepository(dbContext), + new BucketVersionRepository(dbContext), + new BudgetedTransactionRepository(dbContext), + new BucketMovementRepository(dbContext), + new BucketRuleSetRepository(dbContext)); + } + + public Bucket GetWithLatestVersion(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetWithLatestVersion(id); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception($"{typeof(Bucket)} not found in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public override IEnumerable GetAll() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAll(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetSystemBuckets() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetSystemBuckets(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetActiveBuckets(DateTime validFrom) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetActiveBuckets(validFrom); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public BucketVersion GetLatestVersion(Guid bucketId, DateTime yearMonth) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetLatestVersion(bucketId, yearMonth); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception("No Bucket Version found for the selected month"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public BucketFigures GetFigures(Guid bucketId, DateTime yearMonth) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetFigures(bucketId, yearMonth); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public decimal GetBalance(Guid bucketId, DateTime yearMonth) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetBalance(bucketId, yearMonth); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public BucketFigures GetInAndOut(Guid bucketId, DateTime yearMonth) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetInAndOut(bucketId, yearMonth); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public override Bucket Create(Bucket entity) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var baseService = CreateBaseService(dbContext); + try + { + var result = baseService.Create(entity); + transaction.Commit(); + return result; + } + catch (EntityUpdateException e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception(e.Message); + } + catch (Exception e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public override Bucket Update(Bucket entity) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var baseService = CreateBaseService(dbContext); + try + { + var result = baseService.Update(entity); + transaction.Commit(); + return result; + } + catch (Exception e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public void Close(Guid id, DateTime yearMonth) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + baseService.Close(id, yearMonth); + } + catch (EntityUpdateException e) + { + Console.WriteLine(e); + throw new Exception(e.Message); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public override void Delete(Guid id) + { + using var dbContext = new DatabaseContext(_dbContextOptions); + using var transaction = dbContext.Database.BeginTransaction(); + var baseService = CreateBaseService(dbContext); + try + { + baseService.Delete(id); + transaction.Commit(); + } + catch (EntityUpdateException e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception(e.Message); + } + catch (Exception e) + { + transaction.Rollback(); + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } + + public BucketMovement CreateMovement(Guid bucketId, decimal amount, DateTime movementDate) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.CreateMovement(bucketId, amount, movementDate); + } + catch (EntityUpdateException e) + { + Console.WriteLine(e); + throw new Exception(e.Message); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBudgetedTransactionService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBudgetedTransactionService.cs new file mode 100644 index 0000000..89d881b --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreBudgetedTransactionService.cs @@ -0,0 +1,138 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreBudgetedTransactionService : EFCoreBaseService, IBudgetedTransactionService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreBudgetedTransactionService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericBudgetedTransactionService CreateBaseService(DatabaseContext dbContext) + { + return new GenericBudgetedTransactionService(new BudgetedTransactionRepository(dbContext)); + } + + public IEnumerable GetAll(DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAll(periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllFromTransaction(Guid transactionId) + { + return GetAllFromTransaction(transactionId, DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllFromTransaction(Guid transactionId, DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllFromTransaction(transactionId, periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllFromBucket(Guid bucketId) + { + return GetAllFromBucket(bucketId, DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllFromBucket(Guid bucketId, DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllFromBucket(bucketId, periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllNonTransfer() + { + return GetAllNonTransfer(DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllNonTransfer(DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllNonTransfer(periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllTransfer() + { + return GetAllTransfer(DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllTransfer(DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllTransfer(periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllIncome() + { + return GetAllIncome(DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllIncome(DateTime periodStart, DateTime periodEnd) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllIncome(periodStart, periodEnd); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreImportProfileService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreImportProfileService.cs new file mode 100644 index 0000000..75de512 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreImportProfileService.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreImportProfileService : EFCoreBaseService, IImportProfileService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreImportProfileService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericImportProfileService CreateBaseService(DatabaseContext dbContext) + { + return new GenericImportProfileService(new ImportProfileRepository(dbContext)); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreRecurringBankTransactionService.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreRecurringBankTransactionService.cs new file mode 100644 index 0000000..d595cac --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreRecurringBankTransactionService.cs @@ -0,0 +1,96 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Data.Services.Exceptions; +using OpenBudgeteer.Core.Data.Services.Generic; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreRecurringBankTransactionService : EFCoreBaseService, IRecurringBankTransactionService +{ + private readonly DbContextOptions _dbContextOptions; + + public EFCoreRecurringBankTransactionService(DbContextOptions dbContextOptions) : base(dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } + + protected override GenericRecurringBankTransactionService CreateBaseService(DatabaseContext dbContext) + { + return new GenericRecurringBankTransactionService( + new RecurringBankTransactionRepository(dbContext), + new BankTransactionRepository(dbContext)); + } + + public RecurringBankTransaction GetWithEntities(Guid id) + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetWithEntities(id); + } + catch (EntityNotFoundException e) + { + Console.WriteLine(e); + throw new Exception($"{typeof(RecurringBankTransaction)} not found in database"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public IEnumerable GetAllWithEntities() + { + try + { + using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return baseService.GetAllWithEntities(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public async Task> GetPendingBankTransactionAsync(DateTime yearMonth) + { + try + { + await using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return await baseService.GetPendingBankTransactionAsync(yearMonth); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Error on querying database: {e.Message}"); + } + } + + public async Task> CreatePendingBankTransactionAsync(DateTime yearMonth) + { + try + { + await using var dbContext = new DatabaseContext(_dbContextOptions); + var baseService = CreateBaseService(dbContext); + return await baseService.CreatePendingBankTransactionAsync(yearMonth); + } + catch (EntityUpdateException e) + { + Console.WriteLine(e); + throw new Exception(e.Message); + } + catch (Exception e) + { + Console.WriteLine(e); + throw new Exception($"Errors during database update: {e.Message}"); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreServiceManager.cs b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreServiceManager.cs new file mode 100644 index 0000000..f49728b --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/EFCore/EFCoreServiceManager.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities; + +namespace OpenBudgeteer.Core.Data.Services.EFCore; + +public class EFCoreServiceManager : IServiceManager +{ + public IAccountService AccountService => new EFCoreAccountService(_dbContextOptions); + public IBankTransactionService BankTransactionService => new EFCoreBankTransactionService(_dbContextOptions); + public IBucketGroupService BucketGroupService => new EFCoreBucketGroupService(_dbContextOptions); + public IBucketMovementService BucketMovementService => new EFCoreBucketMovementService(_dbContextOptions); + public IBucketService BucketService => new EFCoreBucketService(_dbContextOptions); + public IBucketRuleSetService BucketRuleSetService => new EFCoreBucketRuleSetService(_dbContextOptions); + public IBudgetedTransactionService BudgetedTransactionService => new EFCoreBudgetedTransactionService(_dbContextOptions); + public IImportProfileService ImportProfileService => new EFCoreImportProfileService(_dbContextOptions); + public IRecurringBankTransactionService RecurringBankTransactionService => new EFCoreRecurringBankTransactionService(_dbContextOptions); + + private readonly DbContextOptions _dbContextOptions; + + public EFCoreServiceManager(DbContextOptions dbContextOptions) + { + _dbContextOptions = dbContextOptions; + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Exceptions/EntityNotFoundException.cs b/OpenBudgeteer.Core.Data.Services/Exceptions/EntityNotFoundException.cs new file mode 100644 index 0000000..86b323b --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Exceptions/EntityNotFoundException.cs @@ -0,0 +1,7 @@ +namespace OpenBudgeteer.Core.Data.Services.Exceptions; + +public class EntityNotFoundException : Exception +{ + public EntityNotFoundException() { } + public EntityNotFoundException(string message) : base(message) { } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Exceptions/EntityUpdateException.cs b/OpenBudgeteer.Core.Data.Services/Exceptions/EntityUpdateException.cs new file mode 100644 index 0000000..23ed981 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Exceptions/EntityUpdateException.cs @@ -0,0 +1,7 @@ +namespace OpenBudgeteer.Core.Data.Services.Exceptions; + +public class EntityUpdateException : Exception +{ + public EntityUpdateException() { } + public EntityUpdateException(string message) : base(message) { } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/AccountService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericAccountService.cs similarity index 51% rename from OpenBudgeteer.Core.Data.Services/AccountService.cs rename to OpenBudgeteer.Core.Data.Services/Generic/GenericAccountService.cs index f75ae9f..d16a5f1 100644 --- a/OpenBudgeteer.Core.Data.Services/AccountService.cs +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericAccountService.cs @@ -1,16 +1,20 @@ -using Microsoft.EntityFrameworkCore; +using OpenBudgeteer.Core.Data.Contracts.Repositories; using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; -namespace OpenBudgeteer.Core.Data.Services; +namespace OpenBudgeteer.Core.Data.Services.Generic; -internal class AccountService : BaseService, IAccountService +public class GenericAccountService : GenericBaseService, IAccountService { - internal AccountService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new AccountRepository(new DatabaseContext(dbContextOptions))) + private readonly IAccountRepository _accountRepository; + private readonly IBankTransactionRepository _bankTransactionRepository; + + public GenericAccountService( + IAccountRepository accountRepository, + IBankTransactionRepository bankTransactionRepository) : base(accountRepository) { + _accountRepository = accountRepository; + _bankTransactionRepository = bankTransactionRepository; } public override Account Get(Guid id) @@ -33,20 +37,11 @@ public override IEnumerable GetAll() public IEnumerable GetActiveAccounts() { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new AccountRepository(dbContext); - return repository.All() - .Where(i => i.IsActive == 1) - .OrderBy(i => i.Name) - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } + return _accountRepository + .All() + .Where(i => i.IsActive == 1) + .OrderBy(i => i.Name) + .ToList(); } /// @@ -55,12 +50,12 @@ public IEnumerable GetActiveAccounts() /// Response containing details and success of the request public Account CloseAccount(Guid id) { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BankTransactionRepository(dbContext); - var balance = repository.All() - .Where(i => i.Id == id) + var balance = _bankTransactionRepository + .All() + .Where(i => i.AccountId == id) .ToList() .Sum(i => i.Amount); + if (balance != 0) throw new Exception("Balance must be 0 to close an Account"); var account = Get(id); diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBankTransactionService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBankTransactionService.cs new file mode 100644 index 0000000..84074dc --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBankTransactionService.cs @@ -0,0 +1,105 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericBankTransactionService : GenericBaseService, IBankTransactionService +{ + private readonly IBankTransactionRepository _bankTransactionRepository; + private readonly IBudgetedTransactionRepository _budgetedTransactionRepository; + + public GenericBankTransactionService( + IBankTransactionRepository bankTransactionRepository, + IBudgetedTransactionRepository budgetedTransactionRepository) : base(bankTransactionRepository) + { + _bankTransactionRepository = bankTransactionRepository; + _budgetedTransactionRepository = budgetedTransactionRepository; + } + + public BankTransaction GetWithEntities(Guid id) + { + var result = _bankTransactionRepository.ByIdWithIncludedEntities(id); + if (result == null) throw new EntityNotFoundException(); + return result; + } + + public IEnumerable GetAll(DateTime? periodStart, DateTime? periodEnd, int limit = 0) + { + var result = _bankTransactionRepository + .AllWithIncludedEntities() + .Where(i => + i.TransactionDate >= (periodStart ?? DateTime.MinValue) && + i.TransactionDate <= (periodEnd ?? DateTime.MaxValue)) + .OrderByDescending(i => i.TransactionDate) + .ToList(); + return limit > 0 + ? result.Take(limit) + : result; + } + + public IEnumerable GetFromAccount(Guid accountId, int limit = 0) + { + return GetFromAccount(accountId, null, null, limit); + } + + public IEnumerable GetFromAccount(Guid accountId, DateTime? periodStart, DateTime? periodEnd, int limit = 0) + { + var result = _bankTransactionRepository + .AllWithIncludedEntities() + .Where(i => + i.TransactionDate >= (periodStart ?? DateTime.MinValue) && + i.TransactionDate <= (periodEnd ?? DateTime.MaxValue) && + i.AccountId == accountId) + .OrderByDescending(i => i.TransactionDate) + .ToList(); + return limit > 0 + ? result.Take(limit) + : result; + } + + public IEnumerable ImportTransactions(IEnumerable entities) + { + var newTransactions = entities.ToList(); + _bankTransactionRepository.CreateRange(newTransactions); + return newTransactions; + } + + public override BankTransaction Update(BankTransaction entity) + { + if (entity.BudgetedTransactions != null && entity.BudgetedTransactions.Any()) + { + // Delete all existing bucket assignments, as they will be replaced by passed assignments + var deletedIds = + _budgetedTransactionRepository.All() + .Where(i => i.TransactionId == entity.Id) + .Select(i => i.Id) + .ToList(); + + if (deletedIds.Count != 0) + { + var result = _budgetedTransactionRepository.DeleteRange(deletedIds); + if (result != deletedIds.Count) + throw new EntityUpdateException("Unable to delete old Bucket Assignments of that Transaction"); + } + + // Reset all Guid for re-creation + foreach (var budgetedTransaction in entity.BudgetedTransactions) + { + budgetedTransaction.Id = Guid.Empty; + } + } + + // Update BankTransaction including bucket assignments (if available) in DB + _bankTransactionRepository.Update(entity); + + return entity; + } + + public override void Delete(Guid id) + { + var result = _bankTransactionRepository.Delete(id); + if (result == 0) throw new EntityUpdateException("Unable to delete Bank Transaction"); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBaseService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBaseService.cs new file mode 100644 index 0000000..ef5926a --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBaseService.cs @@ -0,0 +1,49 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public abstract class GenericBaseService : IBaseService + where TEntity : class, IEntity +{ + private readonly IBaseRepository _baseRepository; + + protected GenericBaseService(IBaseRepository baseRepository) + { + _baseRepository = baseRepository; + } + + public virtual TEntity Get(Guid id) + { + var result = _baseRepository.ById(id); + if (result == null) throw new EntityNotFoundException(); + return result; + } + + public virtual IEnumerable GetAll() + { + return _baseRepository.All().ToList(); + } + + public virtual TEntity Create(TEntity entity) + { + var result = _baseRepository.Create(entity); + if (result == 0) throw new EntityUpdateException(); + return entity; + } + + public virtual TEntity Update(TEntity entity) + { + var result = _baseRepository.Update(entity); + if (result == 0) throw new EntityUpdateException(); + return entity; + } + + public virtual void Delete(Guid id) + { + var result = _baseRepository.Delete(id); + if (result == 0) throw new EntityUpdateException(); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketGroupService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketGroupService.cs new file mode 100644 index 0000000..58e0165 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketGroupService.cs @@ -0,0 +1,136 @@ +using System.Collections.ObjectModel; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericBucketGroupService : GenericBaseService, IBucketGroupService +{ + private readonly IBucketGroupRepository _bucketGroupRepository; + + public GenericBucketGroupService( + IBucketGroupRepository bucketGroupRepository) : base(bucketGroupRepository) + { + _bucketGroupRepository = bucketGroupRepository; + } + + public BucketGroup GetWithBuckets(Guid id) + { + var result = _bucketGroupRepository.ByIdWithIncludedEntities(id); + if (result == null) throw new EntityNotFoundException(); + return result; + } + + public override IEnumerable GetAll() + { + return _bucketGroupRepository + .AllWithIncludedEntities() + .Where(i => i.Id != Guid.Parse("00000000-0000-0000-0000-000000000001")) + .OrderBy(i => i.Position) + .ToList(); + } + + public IEnumerable GetAllFull() + { + return _bucketGroupRepository + .AllWithIncludedEntities() + .OrderBy(i => i.Position) + .ToList(); + } + + public IEnumerable GetSystemBucketGroups() + { + return _bucketGroupRepository + .AllWithIncludedEntities() + .Where(i => i.Id == Guid.Parse("00000000-0000-0000-0000-000000000001")) + .OrderBy(i => i.Position) //In case in future there are multiple groups + .ToList(); + } + + public override BucketGroup Create(BucketGroup entity) + { + var allGroups = GetAll().ToList(); + var lastNewPosition = allGroups.Count + 1; + + if (entity.Position > 0) + { + // Update positions of existing BucketGroups based on requested position + // As GetAll excludes System Groups no check on 0 position required + foreach (var bucketGroup in allGroups.Where(i => i.Position >= entity.Position)) + { + bucketGroup.Position++; + _bucketGroupRepository.Update(bucketGroup); + } + + // Fix a potential too large position number + if (entity.Position > lastNewPosition) entity.Position = lastNewPosition; + + _bucketGroupRepository.Create(entity); + } + else + { + entity.Position = lastNewPosition; + _bucketGroupRepository.Create(entity); + } + + return entity; + } + + public override BucketGroup Update(BucketGroup entity) + { + //TODO Handle Position Update + return base.Update(entity); + } + + public override void Delete(Guid id) + { + var entity = _bucketGroupRepository.ByIdWithIncludedEntities(id); + if (entity == null) throw new Exception("BucketGroup not found"); + if (entity.Buckets != null && entity.Buckets.Any()) throw new Exception("BucketGroup with Buckets cannot be deleted"); + + var oldPosition = entity.Position; + _bucketGroupRepository.Delete(id); + + // Update Positions of other Bucket Groups + foreach (var bucketGroup in GetAll().Where(i => i.Position > oldPosition)) + { + bucketGroup.Position--; + _bucketGroupRepository.Update(bucketGroup); + } + } + + public BucketGroup Move(Guid bucketGroupId, int positions) + { + var bucketGroup = Get(bucketGroupId); + if (positions == 0) return bucketGroup; + + // Create in an interim List for later use + var existingBucketGroups = new ObservableCollection(); + foreach (var group in GetAll().ToList()) + { + existingBucketGroups.Add(group); + } + var bucketGroupCount = existingBucketGroups.Count(); + var targetPosition = bucketGroup.Position + positions; + if (targetPosition < 1) targetPosition = 1; + if (targetPosition > bucketGroupCount) targetPosition = bucketGroupCount; + if (targetPosition == bucketGroup.Position) return bucketGroup; // Group is already at the end or top. No further action + + // Move Group in interim List + existingBucketGroups.Move(bucketGroup.Position - 1, targetPosition - 1); + + // Update Position number + var newPosition = 1; + foreach (var group in existingBucketGroups) + { + group.Position = newPosition; + _bucketGroupRepository.Update(group); + if (group.Id == bucketGroupId) bucketGroup = group; // Use correct object reference for final return + newPosition++; + } + + return bucketGroup; + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketMovementService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketMovementService.cs new file mode 100644 index 0000000..c526824 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketMovementService.cs @@ -0,0 +1,42 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericBucketMovementService : GenericBaseService, IBucketMovementService +{ + private readonly IBucketMovementRepository _bucketMovementRepository; + + public GenericBucketMovementService( + IBucketMovementRepository bucketMovementRepository) : base(bucketMovementRepository) + { + _bucketMovementRepository = bucketMovementRepository; + } + + public IEnumerable GetAll(DateTime periodStart, DateTime periodEnd) + { + return _bucketMovementRepository + .All() + .Where(i => + i.MovementDate >= periodStart && + i.MovementDate <= periodEnd) + .ToList(); + } + + public IEnumerable GetAllFromBucket(Guid bucketId) + { + return GetAllFromBucket(bucketId, DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllFromBucket(Guid bucketId, DateTime periodStart, DateTime periodEnd) + { + return _bucketMovementRepository + .All() + .Where(i => + i.MovementDate >= periodStart && + i.MovementDate <= periodEnd && + i.BucketId == bucketId) + .ToList(); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketRuleSetService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketRuleSetService.cs new file mode 100644 index 0000000..1ce3562 --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketRuleSetService.cs @@ -0,0 +1,81 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericBucketRuleSetService : GenericBaseService, IBucketRuleSetService +{ + private readonly IBucketRuleSetRepository _bucketRuleSetRepository; + private readonly IMappingRuleRepository _mappingRuleRepository; + + public GenericBucketRuleSetService( + IBucketRuleSetRepository bucketRuleSetRepository, IMappingRuleRepository mappingRuleRepository) : base(bucketRuleSetRepository) + { + _bucketRuleSetRepository = bucketRuleSetRepository; + _mappingRuleRepository = mappingRuleRepository; + } + + public override BucketRuleSet Get(Guid id) + { + var result = _bucketRuleSetRepository.ByIdWithIncludedEntities(id); + if (result == null) throw new EntityNotFoundException(); + return result; + } + + public override IEnumerable GetAll() + { + return _bucketRuleSetRepository + .AllWithIncludedEntities() + .OrderBy(i => i.Priority) + .ToList(); + } + + public IEnumerable GetMappingRules(Guid bucketRuleSetId) + { + return _mappingRuleRepository + .AllWithIncludedEntities() + .Where(i => i.BucketRuleSetId == bucketRuleSetId) + .ToList(); + } + + public override BucketRuleSet Update(BucketRuleSet entity) + { + // Check if Mapping Rules need to be deleted + var deletedIds = + // Collect database entities + _mappingRuleRepository.All() + .Where(i => i.BucketRuleSetId == entity.Id) + .ToList() + // Select which of the database IDs are no longer available in entity + .Where(i => entity.MappingRules != null && entity.MappingRules + .All(j => j.Id != i.Id)) + .Select(i => i.Id) + .ToList(); + if (deletedIds.Count != 0) + { + var result = _mappingRuleRepository.DeleteRange(deletedIds); + if (result != deletedIds.Count) + throw new Exception("Unable to delete old MappingRules of that BucketRuleSet"); + } + + // Update BucketRuleSet including MappingRules + _bucketRuleSetRepository.Update(entity); + + return entity; + } + + public override void Delete(Guid id) + { + // Delete all existing Mapping Rules + _mappingRuleRepository.DeleteRange(_mappingRuleRepository + .All() + .Where(i => i.BucketRuleSetId == id) + .Select(i => i.Id) + .ToList()); + + // Delete BucketRuleSet + _bucketRuleSetRepository.Delete(id); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketService.cs new file mode 100644 index 0000000..6f25e9a --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBucketService.cs @@ -0,0 +1,313 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericBucketService : GenericBaseService, IBucketService +{ + private readonly IBucketRepository _bucketRepository; + private readonly IBucketVersionRepository _bucketVersionRepository; + private readonly IBudgetedTransactionRepository _budgetedTransactionRepository; + private readonly IBucketMovementRepository _bucketMovementRepository; + private readonly IBucketRuleSetRepository _bucketRuleSetRepository; + + public GenericBucketService( + IBucketRepository bucketRepository, + IBucketVersionRepository bucketVersionRepository, + IBudgetedTransactionRepository budgetedTransactionRepository, + IBucketMovementRepository bucketMovementRepository, + IBucketRuleSetRepository bucketRuleSetRepository) : base(bucketRepository) + { + _bucketRepository = bucketRepository; + _bucketVersionRepository = bucketVersionRepository; + _budgetedTransactionRepository = budgetedTransactionRepository; + _bucketMovementRepository = bucketMovementRepository; + _bucketRuleSetRepository = bucketRuleSetRepository; + } + + public Bucket GetWithLatestVersion(Guid id) + { + var result = _bucketRepository.ByIdWithVersions(id); + if (result == null) throw new EntityNotFoundException(); + result.CurrentVersion = GetLatestVersion(id, DateTime.Now); + result.BucketVersions = result.BucketVersions!.OrderByDescending(i => i.Version).ToList(); + + return result; + } + + public override IEnumerable GetAll() + { + return _bucketRepository + .All() + .Where(i => + i.Id != Guid.Parse("00000000-0000-0000-0000-000000000001") && + i.Id != Guid.Parse("00000000-0000-0000-0000-000000000002")) + .OrderBy(i => i.Name) + .ToList(); + } + + public IEnumerable GetSystemBuckets() + { + return _bucketRepository + .All() + .Where(i => + i.Id == Guid.Parse("00000000-0000-0000-0000-000000000001") || + i.Id == Guid.Parse("00000000-0000-0000-0000-000000000002")) + .ToList(); + } + + public IEnumerable GetActiveBuckets(DateTime validFrom) + { + var result = _bucketRepository + .AllWithVersions() + .Where(i => + // Only valid Buckets of current month + i.ValidFrom <= validFrom && + // Only active Buckets + (i.IsInactive == false || + // Alternative: Bucket is inactive as of today, but was valid in current selected month + (i.IsInactive && i.IsInactiveFrom > validFrom))) + .OrderBy(i => i.Name) + .ToList(); + foreach (var bucket in result) + { + bucket.CurrentVersion = bucket.BucketVersions! + .OrderByDescending(i => i.ValidFrom) + .ToList() + .First(i => i.ValidFrom <= validFrom); + } + + return result; + } + + public BucketVersion GetLatestVersion(Guid bucketId, DateTime yearMonth) + { + var result = _bucketVersionRepository + .All() + .Where(i => i.BucketId == bucketId) + .OrderByDescending(i => i.ValidFrom) + .ToList() + .FirstOrDefault(i => i!.ValidFrom <= yearMonth, null); + if (result == null) throw new EntityNotFoundException(); + return result; + } + + public BucketFigures GetFigures(Guid bucketId, DateTime yearMonth) + { + var bucketWithTransactions = _bucketRepository.ByIdWithTransactions(bucketId) ?? throw new Exception("Bucket not found."); + var bucketWithMovements = _bucketRepository.ByIdWithMovements(bucketId) ?? throw new Exception("Bucket not found."); + + decimal input = 0, output = 0; + + // Calculate Balance + var balance = bucketWithTransactions.BudgetedTransactions! + .Where(i => i.Transaction.TransactionDate < yearMonth.AddMonths(1)) + .ToList() + .Sum(i => i.Amount); + + balance += bucketWithMovements.BucketMovements! + .Where(i => i.MovementDate < yearMonth.AddMonths(1)) + .ToList() + .Sum(i => i.Amount); + + + // Calculate In & Out + var bucketTransactionsCurrentMonth = bucketWithTransactions.BudgetedTransactions! + .Where(i => + i.Transaction.TransactionDate.Year == yearMonth.Year && + i.Transaction.TransactionDate.Month == yearMonth.Month) + .ToList(); + + foreach (var bucketTransaction in bucketTransactionsCurrentMonth) + { + if (bucketTransaction.Amount < 0) + output += bucketTransaction.Amount; + else + input += bucketTransaction.Amount; + } + + var bucketMovementsCurrentMonth = bucketWithMovements.BucketMovements! + .Where(i => + i.MovementDate.Year == yearMonth.Year && + i.MovementDate.Month == yearMonth.Month) + .ToList(); + + foreach (var bucketMovement in bucketMovementsCurrentMonth) + { + if (bucketMovement.Amount < 0) + output += bucketMovement.Amount; + else + input += bucketMovement.Amount; + } + + return new BucketFigures{ Balance = balance, Input = input, Output = output }; + } + + public decimal GetBalance(Guid bucketId, DateTime yearMonth) + { + var bucketWithTransactions = _bucketRepository.ByIdWithTransactions(bucketId) ?? throw new Exception("Bucket not found."); + var bucketWithMovements = _bucketRepository.ByIdWithMovements(bucketId) ?? throw new Exception("Bucket not found."); + + var result = bucketWithTransactions.BudgetedTransactions! + .Where(i => i.Transaction.TransactionDate < yearMonth.AddMonths(1)) + .ToList() + .Sum(i => i.Amount); + + result += bucketWithMovements.BucketMovements! + .Where(i => i.MovementDate < yearMonth.AddMonths(1)) + .ToList() + .Sum(i => i.Amount); + + return result; + } + + public BucketFigures GetInAndOut(Guid bucketId, DateTime yearMonth) + { + var bucketWithTransactions = _bucketRepository.ByIdWithTransactions(bucketId) ?? throw new Exception("Bucket not found."); + var bucketWithMovements = _bucketRepository.ByIdWithMovements(bucketId) ?? throw new Exception("Bucket not found."); + + decimal input = 0, output = 0; + + var bucketTransactionsCurrentMonth = bucketWithTransactions.BudgetedTransactions! + .Where(i => + i.Transaction.TransactionDate.Year == yearMonth.Year && + i.Transaction.TransactionDate.Month == yearMonth.Month) + .ToList(); + + foreach (var bucketTransaction in bucketTransactionsCurrentMonth) + { + if (bucketTransaction.Amount < 0) + output += bucketTransaction.Amount; + else + input += bucketTransaction.Amount; + } + + var bucketMovementsCurrentMonth = bucketWithMovements.BucketMovements! + .Where(i => + i.MovementDate.Year == yearMonth.Year && + i.MovementDate.Month == yearMonth.Month) + .ToList(); + + foreach (var bucketMovement in bucketMovementsCurrentMonth) + { + if (bucketMovement.Amount < 0) + output += bucketMovement.Amount; + else + input += bucketMovement.Amount; + } + + return new BucketFigures{ Balance = null, Input = input, Output = output }; + } + + public override Bucket Create(Bucket entity) + { + if (entity.CurrentVersion == null) throw new EntityUpdateException("No Bucket Version defined"); + + entity.CurrentVersion.Version = 1; + entity.BucketVersions = new List(); + entity.BucketVersions.Add(entity.CurrentVersion); + + _bucketRepository.Create(entity); + return entity; + } + + public override Bucket Update(Bucket entity) + { + if (entity.CurrentVersion != null) + { + entity.BucketVersions = new List(); + if (entity.Id == Guid.Empty) + { + // New Bucket - Create new Version + var newVersion = entity.CurrentVersion; + newVersion.Id = Guid.Empty; + newVersion.Version = 1; + entity.BucketVersions.Add(newVersion); + } + else + { + var latestVersion = GetLatestVersion(entity.Id, DateTime.Now); + if (entity.CurrentVersion.ValidFrom == latestVersion.ValidFrom) + { + // Change in same month, overwrite latest Version + latestVersion.BucketType = entity.CurrentVersion.BucketType; + latestVersion.BucketTypeXParam = entity.CurrentVersion.BucketTypeXParam; + latestVersion.BucketTypeYParam = entity.CurrentVersion.BucketTypeYParam; + latestVersion.BucketTypeZParam = entity.CurrentVersion.BucketTypeZParam; + latestVersion.Notes = entity.CurrentVersion.Notes; + + entity.BucketVersions.Add(latestVersion); + } + else + { + // Create new Version + var newVersion = entity.CurrentVersion; + newVersion.Id = Guid.Empty; + newVersion.Version = latestVersion.Version + 1; + entity.BucketVersions.Add(newVersion); + } + } + } + + _bucketRepository.Update(entity); + return entity; + } + + public void Close(Guid id, DateTime yearMonth) + { + if (GetBalance(id, yearMonth) != 0) throw new EntityUpdateException("Balance must be 0 to close a Bucket"); + + if (_budgetedTransactionRepository.All().Any(i => i.BucketId == id) || + _bucketMovementRepository.All().Any(i => i.BucketId == id)) + { + // Update: Bucket will be set to inactive for the next month + var entity = _bucketRepository.ById(id); + if (entity == null) throw new EntityUpdateException("Bucket not found"); + if (entity.IsInactive) throw new EntityUpdateException("Bucket has been already set to inactive"); + entity.IsInactive = true; + entity.IsInactiveFrom = yearMonth.AddMonths(1); + _bucketRepository.Update(entity); + } + else + { + // Delete: Bucket has no transactions & movements, so it can be directly deleted from the database + _bucketRepository.Delete(id); + } + } + + public override void Delete(Guid id) + { + if (_budgetedTransactionRepository.All().Any(i => i.BucketId == id) || + _bucketMovementRepository.All().Any(i => i.BucketId == id)) + { + throw new EntityUpdateException("Cannot delete a Bucket with assigned Transactions or Bucket Movements"); + } + + // Delete Bucket + _bucketRepository.Delete(id); + + // Delete all BucketRuleSet which refer to this Bucket + var bucketRuleSetIds = _bucketRuleSetRepository + .All() + .Where(i => i.TargetBucketId == id) + .Select(i => i.Id) + .ToList(); + if (bucketRuleSetIds.Count != 0) _bucketRuleSetRepository.DeleteRange(bucketRuleSetIds); + } + + public BucketMovement CreateMovement(Guid bucketId, decimal amount, DateTime movementDate) + { + var newBucketMovement = new BucketMovement() + { + Id = Guid.Empty, + BucketId = bucketId, + Amount = amount, + MovementDate = movementDate + }; + var result = _bucketMovementRepository.Create(newBucketMovement); + if (result == 0) throw new EntityUpdateException($"Unable to create {typeof(BucketMovement)} in database"); + return newBucketMovement; + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericBudgetedTransactionService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericBudgetedTransactionService.cs new file mode 100644 index 0000000..8c862da --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericBudgetedTransactionService.cs @@ -0,0 +1,107 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericBudgetedTransactionService : GenericBaseService, IBudgetedTransactionService +{ + private readonly IBudgetedTransactionRepository _budgetedTransactionRepository; + + public GenericBudgetedTransactionService( + IBudgetedTransactionRepository budgetedTransactionRepository) : base(budgetedTransactionRepository) + { + _budgetedTransactionRepository = budgetedTransactionRepository; + } + + public IEnumerable GetAll(DateTime periodStart, DateTime periodEnd) + { + return _budgetedTransactionRepository + .AllWithTransactions() + .Where(i => + i.Transaction.TransactionDate >= periodStart && + i.Transaction.TransactionDate <= periodEnd) + .ToList(); + } + + public IEnumerable GetAllFromTransaction(Guid transactionId) + { + return GetAllFromTransaction(transactionId, DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllFromTransaction(Guid transactionId, DateTime periodStart, DateTime periodEnd) + { + return _budgetedTransactionRepository + .AllWithTransactions() + .Where(i => + i.Transaction.TransactionDate >= periodStart && + i.Transaction.TransactionDate <= periodEnd && + i.TransactionId == transactionId) + .ToList(); + } + + public IEnumerable GetAllFromBucket(Guid bucketId) + { + return GetAllFromBucket(bucketId, DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllFromBucket(Guid bucketId, DateTime periodStart, DateTime periodEnd) + { + return _budgetedTransactionRepository + .AllWithTransactions() + .Where(i => + i.Transaction.TransactionDate >= periodStart && + i.Transaction.TransactionDate <= periodEnd && + i.BucketId == bucketId) + .OrderByDescending(i => i.Transaction.TransactionDate) + .ToList(); + } + + public IEnumerable GetAllNonTransfer() + { + return GetAllNonTransfer(DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllNonTransfer(DateTime periodStart, DateTime periodEnd) + { + return _budgetedTransactionRepository + .AllWithTransactions() + .Where(i => + i.Transaction.TransactionDate >= periodStart && + i.Transaction.TransactionDate <= periodEnd && + i.BucketId != Guid.Parse("00000000-0000-0000-0000-000000000002")) + .ToList(); + } + + public IEnumerable GetAllTransfer() + { + return GetAllTransfer(DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllTransfer(DateTime periodStart, DateTime periodEnd) + { + return _budgetedTransactionRepository + .AllWithTransactions() + .Where(i => + i.Transaction.TransactionDate >= periodStart && + i.Transaction.TransactionDate <= periodEnd && + i.BucketId == Guid.Parse("00000000-0000-0000-0000-000000000002")) + .ToList(); + } + + public IEnumerable GetAllIncome() + { + return GetAllIncome(DateTime.MinValue, DateTime.MaxValue); + } + + public IEnumerable GetAllIncome(DateTime periodStart, DateTime periodEnd) + { + return _budgetedTransactionRepository + .AllWithTransactions() + .Where(i => + i.Transaction.TransactionDate >= periodStart && + i.Transaction.TransactionDate <= periodEnd && + i.BucketId == Guid.Parse("00000000-0000-0000-0000-000000000001")) + .ToList(); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericImportProfileService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericImportProfileService.cs new file mode 100644 index 0000000..065935a --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericImportProfileService.cs @@ -0,0 +1,16 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericImportProfileService : GenericBaseService, IImportProfileService +{ + private readonly IImportProfileRepository _importProfileRepository; + + public GenericImportProfileService( + IImportProfileRepository importProfileRepository) : base(importProfileRepository) + { + _importProfileRepository = importProfileRepository; + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/Generic/GenericRecurringBankTransactionService.cs b/OpenBudgeteer.Core.Data.Services/Generic/GenericRecurringBankTransactionService.cs new file mode 100644 index 0000000..10dd7cd --- /dev/null +++ b/OpenBudgeteer.Core.Data.Services/Generic/GenericRecurringBankTransactionService.cs @@ -0,0 +1,94 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Contracts.Services; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Services.Exceptions; + +namespace OpenBudgeteer.Core.Data.Services.Generic; + +public class GenericRecurringBankTransactionService : GenericBaseService, IRecurringBankTransactionService +{ + private readonly IRecurringBankTransactionRepository _recurringBankTransactionRepository; + private readonly IBankTransactionRepository _bankTransactionRepository; + + public GenericRecurringBankTransactionService( + IRecurringBankTransactionRepository recurringBankTransactionRepository, + IBankTransactionRepository bankTransactionRepository) : base(recurringBankTransactionRepository) + { + _recurringBankTransactionRepository = recurringBankTransactionRepository; + _bankTransactionRepository = bankTransactionRepository; + } + + public RecurringBankTransaction GetWithEntities(Guid id) + { + var result = _recurringBankTransactionRepository.ByIdWithIncludedEntities(id); + if (result == null) throw new EntityNotFoundException(); + return result; + } + + public IEnumerable GetAllWithEntities() + { + return _recurringBankTransactionRepository + .AllWithIncludedEntities() + .ToList(); + } + + public async Task> GetPendingBankTransactionAsync(DateTime yearMonth) + { + var recurringBankTransactionTasks = new List>>(); + var recurringBankTransactions = _recurringBankTransactionRepository.AllWithIncludedEntities().ToList(); + foreach (var recurringBankTransaction in recurringBankTransactions) + { + recurringBankTransactionTasks.Add(Task.Run(() => + { + // Check if RecurringBankTransaction need to be created in current month + + // Iterate until Occurrence Date is no longer in the past + var newOccurrenceDate = recurringBankTransaction.FirstOccurrenceDate; + while (newOccurrenceDate < yearMonth) + { + newOccurrenceDate = recurringBankTransaction.GetNextIterationDate(newOccurrenceDate); + } + + // Check if Occurrence Date is in current month and if yes how often it may occur + // Otherwise RecurringBankTransaction not relevant for current month + var transactionsToBeCreated = new List(); + while (newOccurrenceDate.Month == yearMonth.Month && + newOccurrenceDate.Year == yearMonth.Year) + { + // Collect new BankTransactions + transactionsToBeCreated.Add(recurringBankTransaction.GetAsBankTransaction(newOccurrenceDate)); + + // Move to next iteration + newOccurrenceDate = recurringBankTransaction.GetNextIterationDate(newOccurrenceDate); + } + + return transactionsToBeCreated; + })); + } + + List result = []; + foreach (var taskResult in await Task.WhenAll(recurringBankTransactionTasks)) + { + result.AddRange(taskResult); + } + + return result; + } + + public async Task> CreatePendingBankTransactionAsync(DateTime yearMonth) + { + var transactions = (await GetPendingBankTransactionAsync(yearMonth)).ToList(); + if (transactions.Any(i => i.Account?.IsActive == 0)) + throw new EntityUpdateException("Identified Transactions which would be assigned to an inactive Account"); + + // Ensure Account is not assigned to prevent double creation of Accounts + foreach (var transaction in transactions) + { + transaction.Account = new Account(); + } + + _bankTransactionRepository.CreateRange(transactions); + + return transactions; + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/ImportProfileService.cs b/OpenBudgeteer.Core.Data.Services/ImportProfileService.cs deleted file mode 100644 index 1607db4..0000000 --- a/OpenBudgeteer.Core.Data.Services/ImportProfileService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class ImportProfileService : BaseService, IImportProfileService -{ - internal ImportProfileService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new ImportProfileRepository(new DatabaseContext(dbContextOptions))) - { - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/RecurringBankTransactionService.cs b/OpenBudgeteer.Core.Data.Services/RecurringBankTransactionService.cs deleted file mode 100644 index 73c9202..0000000 --- a/OpenBudgeteer.Core.Data.Services/RecurringBankTransactionService.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; - -namespace OpenBudgeteer.Core.Data.Services; - -internal class RecurringBankTransactionService : BaseService, IRecurringBankTransactionService -{ - internal RecurringBankTransactionService(DbContextOptions dbContextOptions) - : base(dbContextOptions, new RecurringBankTransactionRepository(new DatabaseContext(dbContextOptions))) - { - } - - public RecurringBankTransaction GetWithEntities(Guid id) - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new RecurringBankTransactionRepository(dbContext); - - var result = repository.ByIdWithIncludedEntities(id); - if (result == null) throw new Exception($"{typeof(RecurringBankTransaction)} not found in database"); - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public IEnumerable GetAllWithEntities() - { - try - { - using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new RecurringBankTransactionRepository(dbContext); - - return repository - .AllWithIncludedEntities() - .ToList(); - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public async Task> GetPendingBankTransactionAsync(DateTime yearMonth) - { - try - { - await using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new RecurringBankTransactionRepository(dbContext); - - var recurringBankTransactionTasks = new List>>(); - var recurringBankTransactions = repository.AllWithIncludedEntities().ToList(); - foreach (var recurringBankTransaction in recurringBankTransactions) - { - recurringBankTransactionTasks.Add(Task.Run(() => - { - // Check if RecurringBankTransaction need to be created in current month - - // Iterate until Occurrence Date is no longer in the past - var newOccurrenceDate = recurringBankTransaction.FirstOccurrenceDate; - while (newOccurrenceDate < yearMonth) - { - newOccurrenceDate = recurringBankTransaction.GetNextIterationDate(newOccurrenceDate); - } - - // Check if Occurrence Date is in current month and if yes how often it may occur - // Otherwise RecurringBankTransaction not relevant for current month - var transactionsToBeCreated = new List(); - while (newOccurrenceDate.Month == yearMonth.Month && - newOccurrenceDate.Year == yearMonth.Year) - { - // Collect new BankTransactions - transactionsToBeCreated.Add(recurringBankTransaction.GetAsBankTransaction(newOccurrenceDate)); - - // Move to next iteration - newOccurrenceDate = recurringBankTransaction.GetNextIterationDate(newOccurrenceDate); - } - - return transactionsToBeCreated; - })); - } - - List result = []; - foreach (var taskResult in await Task.WhenAll(recurringBankTransactionTasks)) - { - result.AddRange(taskResult); - } - - return result; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Error on querying database: {e.Message}"); - } - } - - public async Task> CreatePendingBankTransactionAsync(DateTime yearMonth) - { - try - { - await using var dbContext = new DatabaseContext(DbContextOptions); - var repository = new BankTransactionRepository(dbContext); - - var transactions = (await GetPendingBankTransactionAsync(yearMonth)).ToList(); - if (transactions.Any(i => i.Account?.IsActive == 0)) - throw new Exception("Identified Transactions which would be assigned to an inactive Account"); - - // Ensure Account is not assigned to prevent double creation of Accounts - foreach (var transaction in transactions) - { - transaction.Account = new Account(); - } - - repository.CreateRange(transactions); - await dbContext.SaveChangesAsync(); - - return transactions; - } - catch (Exception e) - { - Console.WriteLine(e); - throw new Exception($"Errors during database update: {e.Message}"); - } - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data.Services/ServiceManager.cs b/OpenBudgeteer.Core.Data.Services/ServiceManager.cs deleted file mode 100644 index a39c9a3..0000000 --- a/OpenBudgeteer.Core.Data.Services/ServiceManager.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; - -namespace OpenBudgeteer.Core.Data.Services; - -public class ServiceManager : IServiceManager -{ - public IAccountService AccountService => new AccountService(_dbContextOptions); - public IBankTransactionService BankTransactionService => new BankTransactionService(_dbContextOptions); - public IBucketGroupService BucketGroupService => new BucketGroupService(_dbContextOptions); - public IBucketMovementService BucketMovementService => new BucketMovementService(_dbContextOptions); - public IBucketService BucketService => new BucketService(_dbContextOptions); - public IBucketRuleSetService BucketRuleSetService => new BucketRuleSetService(_dbContextOptions); - public IBudgetedTransactionService BudgetedTransactionService => new BudgetedTransactionService(_dbContextOptions); - public IImportProfileService ImportProfileService => new ImportProfileService(_dbContextOptions); - public IRecurringBankTransactionService RecurringBankTransactionService => new RecurringBankTransactionService(_dbContextOptions); - - private readonly DbContextOptions _dbContextOptions; - - public ServiceManager(DbContextOptions dbContextOptions) - { - _dbContextOptions = dbContextOptions; - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data/ConfigurationKeyConstants.cs b/OpenBudgeteer.Core.Data/ConfigurationKeyConstants.cs index 4e51d84..176c669 100644 --- a/OpenBudgeteer.Core.Data/ConfigurationKeyConstants.cs +++ b/OpenBudgeteer.Core.Data/ConfigurationKeyConstants.cs @@ -14,6 +14,4 @@ public static class ConfigurationKeyConstants public const string PROVIDER_MARIADB = "MARIADB"; public const string PROVIDER_POSTGRES = "POSTGRES"; public const string PROVIDER_POSTGRESQL = "POSTGRESQL"; - public const string PROVIDER_SQLITE = "SQLITE"; - public const string PROVIDER_TEMPDB = "TEMPDB"; } \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data/DbContextOptionsFactory.cs b/OpenBudgeteer.Core.Data/DbContextOptionsFactory.cs index a427bd2..eb01e77 100644 --- a/OpenBudgeteer.Core.Data/DbContextOptionsFactory.cs +++ b/OpenBudgeteer.Core.Data/DbContextOptionsFactory.cs @@ -19,8 +19,6 @@ public static partial class DbContextOptionsFactory { private static readonly Dictionary> OptionsFactoryLookup = new(StringComparer.OrdinalIgnoreCase) { - [ConfigurationKeyConstants.PROVIDER_TEMPDB] = SetupSqliteTempDbConnection, - [ConfigurationKeyConstants.PROVIDER_SQLITE] = SetupSqliteConnection, [ConfigurationKeyConstants.PROVIDER_MYSQL] = SetupMariaDbConnection, [ConfigurationKeyConstants.PROVIDER_MARIADB] = SetupMariaDbConnection, [ConfigurationKeyConstants.PROVIDER_POSTGRES] = SetupPostgresConnection, @@ -48,31 +46,6 @@ public static DbContextOptions GetContextOptions(IConfiguration return optionsBuilder.Options; } - private static void SetupSqliteTempDbConnection(DbContextOptionsBuilder optionsBuilder, IConfiguration configuration) - { - var dbFilePath = Path.GetTempFileName(); - optionsBuilder.UseSqlite( - $"Data Source={dbFilePath}", - b => b.MigrationsAssembly("OpenBudgeteer.Core.Data.Sqlite.Migrations")); - } - - private static void SetupSqliteConnection(DbContextOptionsBuilder optionsBuilder, IConfiguration configuration) - { - var dbFilePath = configuration.GetValue(ConfigurationKeyConstants.CONNECTION_DATABASE); - dbFilePath = string.IsNullOrWhiteSpace(dbFilePath) - ? Path.Combine(Directory.GetCurrentDirectory(), "database", "openbudgeteer.db") - : Path.GetFullPath(dbFilePath); - - var directory = Path.GetDirectoryName(dbFilePath); - if (string.IsNullOrEmpty(directory)) throw new Exception("Unable to operate on provided directory"); - if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); - - var connectionString = $"Data Source={dbFilePath}"; - optionsBuilder.UseSqlite( - connectionString, - b => b.MigrationsAssembly("OpenBudgeteer.Core.Data.Sqlite.Migrations")); - } - private static void SetupMariaDbConnection(DbContextOptionsBuilder optionsBuilder, IConfiguration configuration) { var databaseName = configuration.GetValue(ConfigurationKeyConstants.CONNECTION_DATABASE, "openbudgeteer"); diff --git a/OpenBudgeteer.Core.Data/Initialization/NoOpDatabaseInitializer.cs b/OpenBudgeteer.Core.Data/Initialization/NoOpDatabaseInitializer.cs index f0e2be5..e36e1e4 100644 --- a/OpenBudgeteer.Core.Data/Initialization/NoOpDatabaseInitializer.cs +++ b/OpenBudgeteer.Core.Data/Initialization/NoOpDatabaseInitializer.cs @@ -7,6 +7,6 @@ public class NoOpDatabaseInitializer : IDatabaseInitializer { public void InitializeDatabase(IConfiguration configuration) { - // Do nothing. - SQLite automatically creates the database if not exists + // Do nothing } } \ No newline at end of file diff --git a/OpenBudgeteer.Core.Data/OnlineChecker/NoopOnlineChecker.cs b/OpenBudgeteer.Core.Data/OnlineChecker/NoopOnlineChecker.cs index 3d04210..817792f 100644 --- a/OpenBudgeteer.Core.Data/OnlineChecker/NoopOnlineChecker.cs +++ b/OpenBudgeteer.Core.Data/OnlineChecker/NoopOnlineChecker.cs @@ -2,7 +2,6 @@ namespace OpenBudgeteer.Core.Data.OnlineChecker; -// SQLite will come on line with the container :) public class NoopOnlineChecker : IDatabaseOnlineChecker { public bool IsDbOnline(IConfiguration configuration) diff --git a/OpenBudgeteer.Core.Data/OpenBudgeteer.Core.Data.csproj b/OpenBudgeteer.Core.Data/OpenBudgeteer.Core.Data.csproj index 249cf2c..ac3e0a1 100644 --- a/OpenBudgeteer.Core.Data/OpenBudgeteer.Core.Data.csproj +++ b/OpenBudgeteer.Core.Data/OpenBudgeteer.Core.Data.csproj @@ -12,7 +12,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -38,7 +37,6 @@ - diff --git a/OpenBudgeteer.Core.Data/README.md b/OpenBudgeteer.Core.Data/README.md index 6234920..921dae0 100644 --- a/OpenBudgeteer.Core.Data/README.md +++ b/OpenBudgeteer.Core.Data/README.md @@ -5,11 +5,6 @@ - Update `appsettings.json` to connect to a running Db instance - Run below command -For Sqlite: -```shell -dotnet ef migrations add __MigrationName__ --project ../OpenBudgeteer.Core.Data.Sqlite.Migrations/OpenBudgeteer.Core.Data.Sqlite.Migrations.csproj -``` - For MySql: ```shell dotnet ef migrations add __MigrationName__ --project ../OpenBudgeteer.Core.Data.MySql.Migrations/OpenBudgeteer.Core.Data.MySql.Migrations.csproj diff --git a/OpenBudgeteer.Core.Test/Extension/DeleteAllExtension.cs b/OpenBudgeteer.Core.Test/Common/DeleteAllExtension.cs similarity index 82% rename from OpenBudgeteer.Core.Test/Extension/DeleteAllExtension.cs rename to OpenBudgeteer.Core.Test/Common/DeleteAllExtension.cs index 4fee4ce..89f909d 100644 --- a/OpenBudgeteer.Core.Test/Extension/DeleteAllExtension.cs +++ b/OpenBudgeteer.Core.Test/Common/DeleteAllExtension.cs @@ -1,9 +1,8 @@ using System.Linq; using OpenBudgeteer.Core.Data.Contracts.Repositories; -using OpenBudgeteer.Core.Data.Contracts.Services; using OpenBudgeteer.Core.Data.Entities.Models; -namespace OpenBudgeteer.Core.Test.Extension; +namespace OpenBudgeteer.Core.Test.Common; public static class DeleteAllExtension where TRepository : IBaseRepository diff --git a/OpenBudgeteer.Core.Test/Common/TestDataGenerator.cs b/OpenBudgeteer.Core.Test/Common/TestDataGenerator.cs new file mode 100644 index 0000000..91c4074 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Common/TestDataGenerator.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Common; + +public class TestDataGenerator +{ + public static TestDataGenerator Current => new(); + + protected TestDataGenerator() { } + + private int GenerateRandomInt(Random random, int min = 0, int max = 100) + { + return random.Next(min, max); + } + + private string GenerateRandomString(Random random, int length = 20) + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + return new string(Enumerable + .Repeat(chars, length) + .Select(s => s[random.Next(s.Length)]) + .ToArray()); + } + + private decimal GenerateRandomDecimal(Random random, int min = 0, int max = 100) + { + var randomDouble = random.NextDouble(); + var randomDecimal = (decimal)randomDouble + GenerateRandomInt(random, min, max); + return Math.Round(randomDecimal, 2); + } + + #region Account + + public Account GenerateAccount() => GenerateAccount(null); + + public Account GenerateAccount(int? seed) + { + var random = seed != null ? new Random((int)seed) : new Random(); + return new Account() + { + Name = GenerateRandomString(random), + IsActive = GenerateRandomInt(random, 0, 1) + }; + } + + #endregion + + #region BankTransaction + + public BankTransaction GenerateBankTransaction(Account account) => GenerateBankTransaction(null, account); + + public BankTransaction GenerateBankTransaction(int? seed, Account account) + { + var random = seed != null ? new Random((int)seed) : new Random(); + var date = DateTime.Now.AddDays(GenerateRandomInt(random, 1, 30)); + return new BankTransaction() + { + AccountId = account.Id, + TransactionDate = new DateTime(date.Year, date.Month, date.Day), + Payee = GenerateRandomString(random), + Memo = GenerateRandomString(random), + Amount = GenerateRandomDecimal(random, 0, 50) + }; + } + + #endregion + + #region Bucket + + public Bucket GenerateBucket(BucketGroup bucketGroup) + => GenerateBucket(bucketGroup, GenerateBucketVersion()); + public Bucket GenerateBucket(BucketGroup bucketGroup, BucketVersion bucketVersion) + => GenerateBucket(null, bucketGroup, bucketVersion); + + public Bucket GenerateBucket(int? seed, BucketGroup bucketGroup, BucketVersion bucketVersion) + { + var random = seed != null ? new Random((int)seed) : new Random(); + var date = DateTime.Now.AddDays(GenerateRandomInt(random, 1, 30)); + var isInactive = Convert.ToBoolean(GenerateRandomInt(random, 0, 1)); + return new Bucket() + { + Name = GenerateRandomString(random), + BucketGroupId = bucketGroup.Id, + ColorCode = GenerateRandomString(random), + TextColorCode = GenerateRandomString(random), + ValidFrom = new DateTime(date.Year, date.Month, date.Day), + IsInactive = isInactive, + IsInactiveFrom = isInactive ? new DateTime(date.Year, date.Month, date.Day) : DateTime.MinValue, + BucketVersions = new List() { bucketVersion } + }; + } + + #endregion + + #region BucketGroup + + public BucketGroup GenerateBucketGroup() => GenerateBucketGroup(null); + + public BucketGroup GenerateBucketGroup(int? seed) + { + var random = seed != null ? new Random((int)seed) : new Random(); + return new BucketGroup() + { + Name = GenerateRandomString(random), + Position = GenerateRandomInt(random, 0, 10) + }; + } + + #endregion + + #region BucketMovement + + public BucketMovement GenerateBucketMovement(Bucket bucket) => GenerateBucketMovement(null, bucket); + + public BucketMovement GenerateBucketMovement(int? seed, Bucket bucket) + { + var random = seed != null ? new Random((int)seed) : new Random(); + var date = DateTime.Now.AddDays(GenerateRandomInt(random, 1, 30)); + return new BucketMovement() + { + BucketId = bucket.Id, + Amount = GenerateRandomDecimal(random, 0, 50), + MovementDate = new DateTime(date.Year, date.Month, date.Day) + }; + } + + #endregion + + #region BucketRuleSet + + public BucketRuleSet GenerateBucketRuleSet(Bucket bucket) => GenerateBucketRuleSet(null, bucket); + + public BucketRuleSet GenerateBucketRuleSet(int? seed, Bucket bucket) + { + var random = seed != null ? new Random((int)seed) : new Random(); + return new BucketRuleSet() + { + Priority = GenerateRandomInt(random, 1, 100), + Name = GenerateRandomString(random), + TargetBucketId = bucket.Id + }; + } + + #endregion + + #region BucketVersion + + // No active assignment to a Bucket to prevent deadlock with Bucket generation + + public BucketVersion GenerateBucketVersion() => GenerateBucketVersion(null); + + public BucketVersion GenerateBucketVersion(int? seed) + { + var random = seed != null ? new Random((int)seed) : new Random(); + var date = DateTime.Now.AddDays(GenerateRandomInt(random, 1, 30)); + return new BucketVersion() + { + Version = GenerateRandomInt(random, 1, 20), + BucketType = GenerateRandomInt(random, 1, 4), + BucketTypeXParam = GenerateRandomInt(random, 1, 50), + BucketTypeYParam = GenerateRandomDecimal(random, 0, 50), + BucketTypeZParam = new DateTime(date.Year, date.Month, date.Day), + Notes = GenerateRandomString(random), + ValidFrom = new DateTime(date.Year, date.Month, date.Day) + }; + } + + #endregion + + #region BudgetedTransaction + + public BudgetedTransaction GenerateBudgetedTransaction(Bucket bucket, BankTransaction bankTransaction) + => GenerateBudgetedTransaction(null, bucket, bankTransaction); + + public BudgetedTransaction GenerateBudgetedTransaction(int? seed, Bucket bucket, BankTransaction bankTransaction) + { + var random = seed != null ? new Random((int)seed) : new Random(); + return new BudgetedTransaction() + { + BucketId = bucket.Id, + TransactionId = bankTransaction.Id, + Amount = GenerateRandomDecimal(random, 0, 50) + }; + } + + #endregion + + #region ImportProfile + + public ImportProfile GenerateImportProfile(Account account) => GenerateImportProfile(null, account); + + public ImportProfile GenerateImportProfile(int? seed, Account account) + { + var random = seed != null ? new Random((int)seed) : new Random(); + return new ImportProfile() + { + ProfileName = GenerateRandomString(random), + AccountId = account.Id, + HeaderRow = GenerateRandomInt(random, 0, 4), + Delimiter = '|', + TextQualifier = '"', + DateFormat = GenerateRandomString(random, 5), + NumberFormat = GenerateRandomString(random, 5), + TransactionDateColumnName = GenerateRandomString(random), + PayeeColumnName = GenerateRandomString(random), + MemoColumnName = GenerateRandomString(random), + AmountColumnName = GenerateRandomString(random), + AdditionalSettingCreditValue = GenerateRandomInt(random, 0, 4), + CreditColumnName = GenerateRandomString(random), + CreditColumnIdentifierColumnName = GenerateRandomString(random), + CreditColumnIdentifierValue = GenerateRandomString(random), + AdditionalSettingAmountCleanup = Convert.ToBoolean(GenerateRandomInt(random, 0, 1)), + AdditionalSettingAmountCleanupValue = GenerateRandomString(random) + }; + } + + #endregion + + #region MappingRule + + public MappingRule GenerateMappingRule(BucketRuleSet bucketRuleSet) => GenerateMappingRule(null, bucketRuleSet); + + public MappingRule GenerateMappingRule(int? seed, BucketRuleSet bucketRuleSet) + { + var random = seed != null ? new Random((int)seed) : new Random(); + return new MappingRule() + { + BucketRuleSetId = bucketRuleSet.Id, + ComparisonField = GenerateRandomInt(random, 1, 4), + ComparisonType = GenerateRandomInt(random, 1, 4), + ComparisonValue = GenerateRandomString(random) + }; + } + + #endregion + + #region RecurringBankTransaction + + public RecurringBankTransaction GenerateRecurringBankTransaction(Account account) + => GenerateRecurringBankTransaction(null, account); + + public RecurringBankTransaction GenerateRecurringBankTransaction(int? seed, Account account) + { + var random = seed != null ? new Random((int)seed) : new Random(); + var date = DateTime.Now.AddDays(GenerateRandomInt(random, 1, 30)); + return new RecurringBankTransaction() + { + AccountId = account.Id, + RecurrenceType = GenerateRandomInt(random, 1, 4), + RecurrenceAmount = GenerateRandomInt(random, 1, 10), + FirstOccurrenceDate = new DateTime(date.Year, date.Month, date.Day), + Payee = GenerateRandomString(random), + Memo = GenerateRandomString(random), + Amount = GenerateRandomDecimal(random, 0, 50) + }; + } + + #endregion +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/MockDatabase.cs b/OpenBudgeteer.Core.Test/Mocking/MockDatabase.cs new file mode 100644 index 0000000..6e3c068 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/MockDatabase.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking; + +public class MockDatabase +{ + public Dictionary Accounts { get; } = new(); + public Dictionary BankTransactions { get; } = new(); + public Dictionary Buckets { get; } = new(); + public Dictionary BucketGroups { get; } = new(); + public Dictionary BucketMovements { get; } = new(); + public Dictionary BucketRuleSets { get; } = new(); + public Dictionary BucketVersions { get; } = new(); + public Dictionary BudgetedTransactions { get; } = new(); + public Dictionary ImportProfiles { get; } = new(); + public Dictionary MappingRules { get; } = new(); + public Dictionary RecurringBankTransactions { get; } = new(); + + public void Cleanup() + { + Accounts.Clear(); + BankTransactions.Clear(); + Buckets.Clear(); + BucketGroups.Clear(); + BucketMovements.Clear(); + BucketRuleSets.Clear(); + BucketVersions.Clear(); + BudgetedTransactions.Clear(); + ImportProfiles.Clear(); + MappingRules.Clear(); + RecurringBankTransactions.Clear(); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockAccountRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockAccountRepository.cs new file mode 100644 index 0000000..0e8464b --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockAccountRepository.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockAccountRepository : IAccountRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockAccountRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.Accounts.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + return All(); + } + + public Account? ById(Guid id) + { + _mockDatabase.Accounts.TryGetValue(id, out var result); + return result; + } + + public Account? ByIdWithIncludedEntities(Guid id) + { + return ById(id); + } + + public int Create(Account entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.Accounts[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(Account entity) + { + try + { + _mockDatabase.Accounts[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.Accounts.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBankTransactionRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBankTransactionRepository.cs new file mode 100644 index 0000000..bc18d14 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBankTransactionRepository.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBankTransactionRepository : IBankTransactionRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBankTransactionRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.BankTransactions.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var mockBudgetedTransactionRepository = new MockBudgetedTransactionRepository(_mockDatabase); + var transactions = All().ToList(); + foreach (var transaction in transactions) + { + transaction.Account = mockAccountRepository.ById(transaction.AccountId) + ?? throw new Exception("Account doesn't exist"); + transaction.BudgetedTransactions = new List(); + foreach (var budgetedTransaction in mockBudgetedTransactionRepository + .All() + .Where(i => i.TransactionId == transaction.Id)) + { + transaction.BudgetedTransactions.Add(budgetedTransaction); + } + } + + return transactions; + } + + public BankTransaction? ById(Guid id) + { + _mockDatabase.BankTransactions.TryGetValue(id, out var result); + return result; + } + + public BankTransaction? ByIdWithIncludedEntities(Guid id) + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var mockBudgetedTransactionRepository = new MockBudgetedTransactionRepository(_mockDatabase); + var transaction = ById(id); + if (transaction == null) return transaction; + transaction.Account = mockAccountRepository.ById(transaction.AccountId) + ?? throw new Exception("Account doesn't exist"); + transaction.BudgetedTransactions = new List(); + foreach (var budgetedTransaction in mockBudgetedTransactionRepository + .All() + .Where(i => i.TransactionId == transaction.Id)) + { + transaction.BudgetedTransactions.Add(budgetedTransaction); + } + return transaction; + } + + public int Create(BankTransaction entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.BankTransactions[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(BankTransaction entity) + { + try + { + _mockDatabase.BankTransactions[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.BankTransactions.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketGroupRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketGroupRepository.cs new file mode 100644 index 0000000..57ef7d8 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketGroupRepository.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBucketGroupRepository : IBucketGroupRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBucketGroupRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.BucketGroups.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var bucketGroups = All().ToList(); + foreach (var bucketGroup in bucketGroups) + { + bucketGroup.Buckets = new List(); + foreach (var bucket in mockBucketRepository + .All() + .Where(i => i.BucketGroupId == bucketGroup.Id)) + { + bucketGroup.Buckets.Add(bucket); + } + } + + return bucketGroups; + } + + public BucketGroup? ById(Guid id) + { + _mockDatabase.BucketGroups.TryGetValue(id, out var result); + return result; + } + + public BucketGroup? ByIdWithIncludedEntities(Guid id) + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var bucketGroup = ById(id); + if (bucketGroup == null) return bucketGroup; + bucketGroup.Buckets = new List(); + foreach (var bucket in mockBucketRepository + .All() + .Where(i => i.BucketGroupId == bucketGroup.Id)) + { + bucketGroup.Buckets.Add(bucket); + } + + return bucketGroup; + } + + public int Create(BucketGroup entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.BucketGroups[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(BucketGroup entity) + { + try + { + _mockDatabase.BucketGroups[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.BucketGroups.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketMovementRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketMovementRepository.cs new file mode 100644 index 0000000..2744f4b --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketMovementRepository.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBucketMovementRepository : IBucketMovementRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBucketMovementRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.BucketMovements.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var bucketMovements = All().ToList(); + foreach (var bucketMovement in bucketMovements) + { + bucketMovement.Bucket = mockBucketRepository.ById(bucketMovement.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + } + + return bucketMovements; + } + + public BucketMovement? ById(Guid id) + { + _mockDatabase.BucketMovements.TryGetValue(id, out var result); + return result; + } + + public BucketMovement? ByIdWithIncludedEntities(Guid id) + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var bucketMovement = ById(id); + if (bucketMovement == null) return bucketMovement; + bucketMovement.Bucket = mockBucketRepository.ById(bucketMovement.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + + return bucketMovement; + } + + public int Create(BucketMovement entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.BucketMovements[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(BucketMovement entity) + { + try + { + _mockDatabase.BucketMovements[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.BucketMovements.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRepository.cs new file mode 100644 index 0000000..587ea47 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRepository.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBucketRepository : IBucketRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBucketRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.Buckets.Values; + } + + public IEnumerable AllWithVersions() + { + var mockBucketVersionRepository = new MockBucketVersionRepository(_mockDatabase); + var buckets = All().ToList(); + foreach (var bucket in buckets) + { + bucket.BucketVersions = new List(); + foreach (var bucketVersion in mockBucketVersionRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketVersions.Add(bucketVersion); + } + } + + return buckets; + } + + public IEnumerable AllWithActivities() + { + var mockBucketMovementRepository = new MockBucketMovementRepository(_mockDatabase); + var mockBudgetedTransactionRepository = new MockBudgetedTransactionRepository(_mockDatabase); + var buckets = All().ToList(); + foreach (var bucket in buckets) + { + bucket.BucketMovements = new List(); + foreach (var bucketMovement in mockBucketMovementRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketMovements.Add(bucketMovement); + } + + bucket.BudgetedTransactions = new List(); + foreach (var budgetedTransaction in mockBudgetedTransactionRepository + .AllWithTransactions() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BudgetedTransactions.Add(budgetedTransaction); + } + } + + return buckets; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketGroupRepository = new MockBucketGroupRepository(_mockDatabase); + var mockBucketMovementRepository = new MockBucketMovementRepository(_mockDatabase); + var mockBucketVersionRepository = new MockBucketVersionRepository(_mockDatabase); + var mockBudgetedTransactionRepository = new MockBudgetedTransactionRepository(_mockDatabase); + var buckets = All().ToList(); + foreach (var bucket in buckets) + { + bucket.BucketGroup = mockBucketGroupRepository.ById(bucket.BucketGroupId) + ?? throw new Exception("BucketGroup doesn't exist"); + + bucket.BucketMovements = new List(); + foreach (var bucketMovement in mockBucketMovementRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketMovements.Add(bucketMovement); + } + + bucket.BucketVersions = new List(); + foreach (var bucketVersion in mockBucketVersionRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketVersions.Add(bucketVersion); + } + + bucket.BudgetedTransactions = new List(); + foreach (var budgetedTransaction in mockBudgetedTransactionRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BudgetedTransactions.Add(budgetedTransaction); + } + } + + return buckets; + } + + public Bucket? ById(Guid id) + { + _mockDatabase.Buckets.TryGetValue(id, out var result); + return result; + } + + public Bucket? ByIdWithVersions(Guid id) + { + var mockBucketVersionRepository = new MockBucketVersionRepository(_mockDatabase); + var bucket = ById(id); + if (bucket == null) return bucket; + bucket.BucketVersions = new List(); + foreach (var bucketVersion in mockBucketVersionRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketVersions.Add(bucketVersion); + } + + return bucket; + } + + public Bucket? ByIdWithMovements(Guid id) + { + var mockBucketMovementRepository = new MockBucketMovementRepository(_mockDatabase); + var bucket = ById(id); + if (bucket == null) return bucket; + + bucket.BucketMovements = new List(); + foreach (var bucketMovement in mockBucketMovementRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketMovements.Add(bucketMovement); + } + + return bucket; + } + + public Bucket? ByIdWithTransactions(Guid id) + { + var mockBudgetedTransactionRepository = new MockBudgetedTransactionRepository(_mockDatabase); + var bucket = ById(id); + if (bucket == null) return bucket; + + bucket.BudgetedTransactions = new List(); + foreach (var budgetedTransaction in mockBudgetedTransactionRepository + .AllWithTransactions() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BudgetedTransactions.Add(budgetedTransaction); + } + + return bucket; + } + + public Bucket? ByIdWithIncludedEntities(Guid id) + { + var mockBucketGroupRepository = new MockBucketGroupRepository(_mockDatabase); + var mockBucketMovementRepository = new MockBucketMovementRepository(_mockDatabase); + var mockBucketVersionRepository = new MockBucketVersionRepository(_mockDatabase); + var mockBudgetedTransactionRepository = new MockBudgetedTransactionRepository(_mockDatabase); + var bucket = ById(id); + if (bucket == null) return bucket; + bucket.BucketGroup = mockBucketGroupRepository.ById(bucket.BucketGroupId) + ?? throw new Exception("BucketGroup doesn't exist"); + + bucket.BucketMovements = new List(); + foreach (var bucketMovement in mockBucketMovementRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketMovements.Add(bucketMovement); + } + + bucket.BucketVersions = new List(); + foreach (var bucketVersion in mockBucketVersionRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BucketVersions.Add(bucketVersion); + } + + bucket.BudgetedTransactions = new List(); + foreach (var budgetedTransaction in mockBudgetedTransactionRepository + .All() + .Where(i => i.BucketId == bucket.Id)) + { + bucket.BudgetedTransactions.Add(budgetedTransaction); + } + + return bucket; + } + + public int Create(Bucket entity) + { + entity.Id = Guid.NewGuid(); + var version = entity.BucketVersions!.First(); + version.Id = Guid.NewGuid(); + version.BucketId = entity.Id; + _mockDatabase.Buckets[entity.Id] = entity; + _mockDatabase.BucketVersions[version.Id] = version; + return 2; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(Bucket entity) + { + try + { + var result = 1; + _mockDatabase.Buckets[entity.Id] = entity; + if (entity.BucketVersions == null) return result; + foreach (var bucketVersion in entity.BucketVersions) + { + if (bucketVersion.Id == Guid.Empty) + { + bucketVersion.Id = Guid.NewGuid(); + result++; + } + _mockDatabase.BucketVersions[bucketVersion.Id] = bucketVersion; + } + return result; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + var result = _mockDatabase.BucketVersions + .Where(i => i.Value.BucketId == id) + .Select(i => i.Key) + .Count(bucketVersionId => _mockDatabase.BucketVersions.Remove(bucketVersionId)); + if (_mockDatabase.Buckets.Remove(id)) result++; + + return result; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRuleSetRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRuleSetRepository.cs new file mode 100644 index 0000000..7805206 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketRuleSetRepository.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBucketRuleSetRepository : IBucketRuleSetRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBucketRuleSetRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.BucketRuleSets.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var mockMappingRuleRepository = new MockMappingRuleRepository(_mockDatabase); + var bucketRuleSets = All().ToList(); + foreach (var bucketRuleSet in bucketRuleSets) + { + bucketRuleSet.TargetBucket = mockBucketRepository.ById(bucketRuleSet.TargetBucketId) + ?? throw new Exception("Bucket doesn't exist"); + + bucketRuleSet.MappingRules = new List(); + foreach (var mappingRule in mockMappingRuleRepository + .All() + .Where(i => i.BucketRuleSetId == bucketRuleSet.Id)) + { + bucketRuleSet.MappingRules.Add(mappingRule); + } + } + + return bucketRuleSets; + } + + public BucketRuleSet? ById(Guid id) + { + _mockDatabase.BucketRuleSets.TryGetValue(id, out var result); + return result; + } + + public BucketRuleSet? ByIdWithIncludedEntities(Guid id) + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var mockMappingRuleRepository = new MockMappingRuleRepository(_mockDatabase); + var bucketRuleSet = ById(id); + if (bucketRuleSet == null) return bucketRuleSet; + bucketRuleSet.TargetBucket = mockBucketRepository.ById(bucketRuleSet.TargetBucketId) + ?? throw new Exception("Bucket doesn't exist"); + + bucketRuleSet.MappingRules = new List(); + foreach (var mappingRule in mockMappingRuleRepository + .All() + .Where(i => i.BucketRuleSetId == bucketRuleSet.Id)) + { + bucketRuleSet.MappingRules.Add(mappingRule); + } + + return bucketRuleSet; + } + + public int Create(BucketRuleSet entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.BucketRuleSets[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(BucketRuleSet entity) + { + try + { + _mockDatabase.BucketRuleSets[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.BucketRuleSets.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketVersionRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketVersionRepository.cs new file mode 100644 index 0000000..f771433 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBucketVersionRepository.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBucketVersionRepository : IBucketVersionRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBucketVersionRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.BucketVersions.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var bucketVersions = All().ToList(); + foreach (var bucketVersion in bucketVersions) + { + bucketVersion.Bucket = mockBucketRepository.ById(bucketVersion.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + } + + return bucketVersions; + } + + public BucketVersion? ById(Guid id) + { + _mockDatabase.BucketVersions.TryGetValue(id, out var result); + return result; + } + + public BucketVersion? ByIdWithIncludedEntities(Guid id) + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var bucketVersion = ById(id); + if (bucketVersion == null) return bucketVersion; + bucketVersion.Bucket = mockBucketRepository.ById(bucketVersion.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + + return bucketVersion; + } + + public int Create(BucketVersion entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.BucketVersions[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(BucketVersion entity) + { + try + { + _mockDatabase.BucketVersions[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.BucketVersions.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockBudgetedTransactionRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBudgetedTransactionRepository.cs new file mode 100644 index 0000000..a01e417 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockBudgetedTransactionRepository.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockBudgetedTransactionRepository : IBudgetedTransactionRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockBudgetedTransactionRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.BudgetedTransactions.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var mockBankTransactionRepository = new MockBankTransactionRepository(_mockDatabase); + var budgetedTransactions = All().ToList(); + foreach (var budgetedTransaction in budgetedTransactions) + { + budgetedTransaction.Bucket = mockBucketRepository.ById(budgetedTransaction.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + budgetedTransaction.Transaction = mockBankTransactionRepository.ById(budgetedTransaction.TransactionId) + ?? throw new Exception("BankTransaction doesn't exist"); + } + + return budgetedTransactions; + } + + public IEnumerable AllWithTransactions() + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var mockBankTransactionRepository = new MockBankTransactionRepository(_mockDatabase); + var budgetedTransactions = All().ToList(); + foreach (var budgetedTransaction in budgetedTransactions) + { + budgetedTransaction.Bucket = mockBucketRepository.ById(budgetedTransaction.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + budgetedTransaction.Transaction = mockBankTransactionRepository.ById(budgetedTransaction.TransactionId) + ?? throw new Exception("BankTransaction doesn't exist"); + budgetedTransaction.Transaction.Account = mockAccountRepository.ById(budgetedTransaction.Transaction.AccountId) + ?? throw new Exception("Account doesn't exist"); + } + + return budgetedTransactions; + } + + public BudgetedTransaction? ById(Guid id) + { + _mockDatabase.BudgetedTransactions.TryGetValue(id, out var result); + return result; + } + + public BudgetedTransaction? ByIdWithIncludedEntities(Guid id) + { + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var mockBankTransactionRepository = new MockBankTransactionRepository(_mockDatabase); + var budgetedTransaction = ById(id); + if (budgetedTransaction == null) return budgetedTransaction; + budgetedTransaction.Bucket = mockBucketRepository.ById(budgetedTransaction.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + budgetedTransaction.Transaction = mockBankTransactionRepository.ById(budgetedTransaction.TransactionId) + ?? throw new Exception("BankTransaction doesn't exist"); + + return budgetedTransaction; + } + + public BudgetedTransaction? ByIdWithTransaction(Guid id) + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var mockBucketRepository = new MockBucketRepository(_mockDatabase); + var mockBankTransactionRepository = new MockBankTransactionRepository(_mockDatabase); + var budgetedTransaction = ById(id); + if (budgetedTransaction == null) return budgetedTransaction; + budgetedTransaction.Bucket = mockBucketRepository.ById(budgetedTransaction.BucketId) + ?? throw new Exception("Bucket doesn't exist"); + budgetedTransaction.Transaction = mockBankTransactionRepository.ById(budgetedTransaction.TransactionId) + ?? throw new Exception("BankTransaction doesn't exist"); + budgetedTransaction.Transaction.Account = mockAccountRepository.ById(budgetedTransaction.Transaction.AccountId) + ?? throw new Exception("Account doesn't exist"); + + return budgetedTransaction; + } + + public int Create(BudgetedTransaction entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.BudgetedTransactions[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + [Obsolete("{BudgetedTransaction should not be updated, instead delete and re-create", false)] + public int Update(BudgetedTransaction entity) + { + // try + // { + // _mockDatabase.BudgetedTransactions[entity.Id] = entity; + // return 1; + // } + // catch (Exception e) + // { + // Console.WriteLine(e); + // return 0; + // } + throw new NotSupportedException( + $"{typeof(BudgetedTransaction)} should not be updated, instead delete and re-create"); + } + + [Obsolete("{BudgetedTransaction should not be updated, instead delete and re-create", false)] + public int UpdateRange(IEnumerable entities) + { + // return entities.Sum(Update); + throw new NotSupportedException( + $"{typeof(BudgetedTransaction)} should not be updated, instead delete and re-create"); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.BudgetedTransactions.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockImportProfileRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockImportProfileRepository.cs new file mode 100644 index 0000000..80a6335 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockImportProfileRepository.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockImportProfileRepository : IImportProfileRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockImportProfileRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.ImportProfiles.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var importProfiles = All().ToList(); + foreach (var importProfile in importProfiles) + { + importProfile.Account = mockAccountRepository.ById(importProfile.AccountId) + ?? throw new Exception("Account doesn't exist"); + } + + return importProfiles; + } + + public ImportProfile? ById(Guid id) + { + _mockDatabase.ImportProfiles.TryGetValue(id, out var result); + return result; + } + + public ImportProfile? ByIdWithIncludedEntities(Guid id) + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var importProfile = ById(id); + if (importProfile == null) return importProfile; + importProfile.Account = mockAccountRepository.ById(importProfile.AccountId) + ?? throw new Exception("Account doesn't exist"); + + return importProfile; + } + + public int Create(ImportProfile entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.ImportProfiles[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(ImportProfile entity) + { + try + { + _mockDatabase.ImportProfiles[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.ImportProfiles.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockMappingRuleRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockMappingRuleRepository.cs new file mode 100644 index 0000000..113362c --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockMappingRuleRepository.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockMappingRuleRepository : IMappingRuleRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockMappingRuleRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.MappingRules.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockBucketRuleSetRepository = new MockBucketRuleSetRepository(_mockDatabase); + var mappingRules = All().ToList(); + foreach (var mappingRule in mappingRules) + { + mappingRule.BucketRuleSet = mockBucketRuleSetRepository.ById(mappingRule.BucketRuleSetId) + ?? throw new Exception("BucketRuleSet doesn't exist"); + } + + return mappingRules; + } + + public MappingRule? ById(Guid id) + { + _mockDatabase.MappingRules.TryGetValue(id, out var result); + return result; + } + + public MappingRule? ByIdWithIncludedEntities(Guid id) + { + var mockBucketRuleSetRepository = new MockBucketRuleSetRepository(_mockDatabase); + var mappingRule = ById(id); + if (mappingRule == null) return mappingRule; + mappingRule.BucketRuleSet = mockBucketRuleSetRepository.ById(mappingRule.BucketRuleSetId) + ?? throw new Exception("BucketRuleSet doesn't exist"); + + return mappingRule; + } + + public int Create(MappingRule entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.MappingRules[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(MappingRule entity) + { + try + { + _mockDatabase.MappingRules[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.MappingRules.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Repository/MockRecurringBankTransactionRepository.cs b/OpenBudgeteer.Core.Test/Mocking/Repository/MockRecurringBankTransactionRepository.cs new file mode 100644 index 0000000..7b0c7b0 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Repository/MockRecurringBankTransactionRepository.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities.Models; + +namespace OpenBudgeteer.Core.Test.Mocking.Repository; + +public class MockRecurringBankTransactionRepository : IRecurringBankTransactionRepository +{ + private readonly MockDatabase _mockDatabase; + + public MockRecurringBankTransactionRepository(MockDatabase mockDatabase) + { + _mockDatabase = mockDatabase; + } + + public IEnumerable All() + { + return _mockDatabase.RecurringBankTransactions.Values; + } + + public IEnumerable AllWithIncludedEntities() + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var recurringBankTransactions = All().ToList(); + foreach (var recurringBankTransaction in recurringBankTransactions) + { + recurringBankTransaction.Account = mockAccountRepository.ById(recurringBankTransaction.AccountId) + ?? throw new Exception("Account doesn't exist"); + } + + return recurringBankTransactions; + } + + public RecurringBankTransaction? ById(Guid id) + { + _mockDatabase.RecurringBankTransactions.TryGetValue(id, out var result); + return result; + } + + public RecurringBankTransaction? ByIdWithIncludedEntities(Guid id) + { + var mockAccountRepository = new MockAccountRepository(_mockDatabase); + var recurringBankTransaction = ById(id); + if (recurringBankTransaction == null) return recurringBankTransaction; + recurringBankTransaction.Account = mockAccountRepository.ById(recurringBankTransaction.AccountId) + ?? throw new Exception("Account doesn't exist"); + + return recurringBankTransaction; + } + + public int Create(RecurringBankTransaction entity) + { + entity.Id = Guid.NewGuid(); + _mockDatabase.RecurringBankTransactions[entity.Id] = entity; + return 1; + } + + public int CreateRange(IEnumerable entities) + { + return entities.Sum(Create); + } + + public int Update(RecurringBankTransaction entity) + { + try + { + _mockDatabase.RecurringBankTransactions[entity.Id] = entity; + return 1; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int UpdateRange(IEnumerable entities) + { + return entities.Sum(Update); + } + + public int Delete(Guid id) + { + try + { + return _mockDatabase.RecurringBankTransactions.Remove(id) ? 1 : 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 0; + } + } + + public int DeleteRange(IEnumerable ids) + { + return ids.Sum(Delete); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockAccountService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockAccountService.cs new file mode 100644 index 0000000..12de84b --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockAccountService.cs @@ -0,0 +1,14 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockAccountService : GenericAccountService +{ + public MockAccountService(MockDatabase mockDatabase) : base( + new MockAccountRepository(mockDatabase), + new MockBankTransactionRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockBankTransactionService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockBankTransactionService.cs new file mode 100644 index 0000000..8f679f5 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockBankTransactionService.cs @@ -0,0 +1,14 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockBankTransactionService : GenericBankTransactionService +{ + public MockBankTransactionService(MockDatabase mockDatabase) : base( + new MockBankTransactionRepository(mockDatabase), + new MockBudgetedTransactionRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketGroupService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketGroupService.cs new file mode 100644 index 0000000..3db7fa5 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketGroupService.cs @@ -0,0 +1,12 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockBucketGroupService : GenericBucketGroupService +{ + public MockBucketGroupService(MockDatabase mockDatabase) : base(new MockBucketGroupRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketMovementService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketMovementService.cs new file mode 100644 index 0000000..9211313 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketMovementService.cs @@ -0,0 +1,12 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockBucketMovementService : GenericBucketMovementService +{ + public MockBucketMovementService(MockDatabase mockDatabase) : base(new MockBucketMovementRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketRuleSetService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketRuleSetService.cs new file mode 100644 index 0000000..712b347 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketRuleSetService.cs @@ -0,0 +1,14 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockBucketRuleSetService : GenericBucketRuleSetService +{ + public MockBucketRuleSetService(MockDatabase mockDatabase) : base( + new MockBucketRuleSetRepository(mockDatabase), + new MockMappingRuleRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketService.cs new file mode 100644 index 0000000..fc52ac6 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockBucketService.cs @@ -0,0 +1,17 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockBucketService : GenericBucketService +{ + public MockBucketService(MockDatabase mockDatabase) : base( + new MockBucketRepository(mockDatabase), + new MockBucketVersionRepository(mockDatabase), + new MockBudgetedTransactionRepository(mockDatabase), + new MockBucketMovementRepository(mockDatabase), + new MockBucketRuleSetRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockBudgetedTransactionService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockBudgetedTransactionService.cs new file mode 100644 index 0000000..3fb1c1c --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockBudgetedTransactionService.cs @@ -0,0 +1,12 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockBudgetedTransactionService : GenericBudgetedTransactionService +{ + public MockBudgetedTransactionService(MockDatabase mockDatabase) : base(new MockBudgetedTransactionRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockImportProfileService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockImportProfileService.cs new file mode 100644 index 0000000..62a2840 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockImportProfileService.cs @@ -0,0 +1,12 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockImportProfileService : GenericImportProfileService +{ + public MockImportProfileService(MockDatabase mockDatabase) : base(new MockImportProfileRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockRecurringBankTransactionService.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockRecurringBankTransactionService.cs new file mode 100644 index 0000000..fafbae0 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockRecurringBankTransactionService.cs @@ -0,0 +1,14 @@ +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Services.Generic; +using OpenBudgeteer.Core.Test.Mocking.Repository; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockRecurringBankTransactionService : GenericRecurringBankTransactionService +{ + public MockRecurringBankTransactionService(MockDatabase mockDatabase) : base( + new MockRecurringBankTransactionRepository(mockDatabase), + new MockBankTransactionRepository(mockDatabase)) + { + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Mocking/Services/MockServiceManager.cs b/OpenBudgeteer.Core.Test/Mocking/Services/MockServiceManager.cs new file mode 100644 index 0000000..1ea5e4f --- /dev/null +++ b/OpenBudgeteer.Core.Test/Mocking/Services/MockServiceManager.cs @@ -0,0 +1,29 @@ +using OpenBudgeteer.Core.Data.Contracts.Services; + +namespace OpenBudgeteer.Core.Test.Mocking.Services; + +public class MockServiceManager : IServiceManager +{ + public IAccountService AccountService { get; } + public IBankTransactionService BankTransactionService { get; } + public IBucketGroupService BucketGroupService { get; } + public IBucketMovementService BucketMovementService { get; } + public IBucketService BucketService { get; } + public IBucketRuleSetService BucketRuleSetService { get; } + public IBudgetedTransactionService BudgetedTransactionService { get; } + public IImportProfileService ImportProfileService { get; } + public IRecurringBankTransactionService RecurringBankTransactionService { get; } + + public MockServiceManager(MockDatabase mockDatabase) + { + AccountService = new MockAccountService(mockDatabase); + BankTransactionService = new MockBankTransactionService(mockDatabase); + BucketGroupService = new MockBucketGroupService(mockDatabase); + BucketMovementService = new MockBucketMovementService(mockDatabase); + BucketService = new MockBucketService(mockDatabase); + BucketRuleSetService = new MockBucketRuleSetService(mockDatabase); + BudgetedTransactionService = new MockBudgetedTransactionService(mockDatabase); + ImportProfileService = new MockImportProfileService(mockDatabase); + RecurringBankTransactionService = new MockRecurringBankTransactionService(mockDatabase); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/OpenBudgeteer.Core.Test.csproj b/OpenBudgeteer.Core.Test/OpenBudgeteer.Core.Test.csproj index f69313d..a0bea3b 100644 --- a/OpenBudgeteer.Core.Test/OpenBudgeteer.Core.Test.csproj +++ b/OpenBudgeteer.Core.Test/OpenBudgeteer.Core.Test.csproj @@ -31,10 +31,12 @@ PreserveNewest + + PreserveNewest + - @@ -51,7 +53,6 @@ - diff --git a/OpenBudgeteer.Core.Test/TestServiceManager.cs b/OpenBudgeteer.Core.Test/TestServiceManager.cs deleted file mode 100644 index 6396f9f..0000000 --- a/OpenBudgeteer.Core.Test/TestServiceManager.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using OpenBudgeteer.Core.Data; -using OpenBudgeteer.Core.Data.Contracts.Repositories; -using OpenBudgeteer.Core.Data.Contracts.Services; -using OpenBudgeteer.Core.Data.Entities; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.Data.Repository; -using OpenBudgeteer.Core.Data.Services; -using OpenBudgeteer.Core.Test.Extension; - -namespace OpenBudgeteer.Core.Test; - -public class TestServiceManager : IServiceManager -{ - public IAccountService AccountService => _lazyAccountService.Value; - public IBankTransactionService BankTransactionService => _lazyBankTransactionService.Value; - public IBucketGroupService BucketGroupService => _lazyBucketGroupService.Value; - public IBucketMovementService BucketMovementService => _lazyBucketMovementService.Value; - public IBucketService BucketService => _lazyBucketService.Value; - public IBucketRuleSetService BucketRuleSetService => _lazyBucketRuleSetService.Value; - public IBudgetedTransactionService BudgetedTransactionService => _lazyBudgetedTransactionService.Value; - public IImportProfileService ImportProfileService => _lazyImportProfileService.Value; - public IRecurringBankTransactionService RecurringBankTransactionService => _lazyRecurringBankTransactionService.Value; - - private readonly Lazy _lazyAccountService; - private readonly Lazy _lazyBankTransactionService; - private readonly Lazy _lazyBucketGroupService; - private readonly Lazy _lazyBucketMovementService; - private readonly Lazy _lazyBucketService; - private readonly Lazy _lazyBucketRuleSetService; - private readonly Lazy _lazyBudgetedTransactionService; - private readonly Lazy _lazyImportProfileService; - private readonly Lazy _lazyRecurringBankTransactionService; - - private readonly DbContextOptions _dbContextOptions; - - private TestServiceManager(DbContextOptions dbContextOptions) - { - _dbContextOptions = dbContextOptions; - var coreServiceManager = new ServiceManager(dbContextOptions); - _lazyAccountService = new Lazy(() => coreServiceManager.AccountService); - _lazyBankTransactionService = new Lazy(() => coreServiceManager.BankTransactionService); - _lazyBucketGroupService = new Lazy(() => coreServiceManager.BucketGroupService); - _lazyBucketMovementService = new Lazy(() => coreServiceManager.BucketMovementService); - _lazyBucketService = new Lazy(() => coreServiceManager.BucketService); - _lazyBucketRuleSetService = new Lazy(() => coreServiceManager.BucketRuleSetService); - _lazyBudgetedTransactionService = new Lazy(() => coreServiceManager.BudgetedTransactionService); - _lazyImportProfileService = new Lazy(() => coreServiceManager.ImportProfileService); - _lazyRecurringBankTransactionService = new Lazy(() => coreServiceManager.RecurringBankTransactionService); - } - - public static TestServiceManager CreateUsingSqlite(string dbName) - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - [ConfigurationKeyConstants.CONNECTION_PROVIDER] = ConfigurationKeyConstants.PROVIDER_SQLITE, - [ConfigurationKeyConstants.CONNECTION_DATABASE] = $"{dbName}.db" - }!) - .Build(); - - return GenerateTestServiceManager(configuration); - } - - public static TestServiceManager CreateUsingMySql() - { - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - [ConfigurationKeyConstants.CONNECTION_PROVIDER] = ConfigurationKeyConstants.PROVIDER_MYSQL, - [ConfigurationKeyConstants.CONNECTION_SERVER] = "mariadb-dev.home.lab", - [ConfigurationKeyConstants.CONNECTION_PORT] = "3306", - [ConfigurationKeyConstants.CONNECTION_USER] = "openbudgeteer_unit_test", - [ConfigurationKeyConstants.CONNECTION_PASSWORD] = "openbudgeteer_unit_test", - [ConfigurationKeyConstants.CONNECTION_DATABASE] = "openbudgeteer_unit_test" - }!) - .Build(); - - return GenerateTestServiceManager(configuration); - } - - private static TestServiceManager GenerateTestServiceManager(IConfiguration configuration) - { - var contextOptions = DbContextOptionsFactory.GetContextOptions(configuration); - var dbContext = new DatabaseContext(contextOptions); - dbContext.Database.Migrate(); - - return new TestServiceManager(contextOptions); - } - - public void CleanupDatabase() - { - using var dbContext = new DatabaseContext(_dbContextOptions); - - // Independent objects - DeleteAllExtension.DeleteAll(new ImportProfileRepository(dbContext)); - dbContext.SaveChanges(); - DeleteAllExtension.DeleteAll(new RecurringBankTransactionRepository(dbContext)); - dbContext.SaveChanges(); - DeleteAllExtension.DeleteAll(new BucketRuleSetRepository(dbContext)); - dbContext.SaveChanges(); - DeleteAllExtension.DeleteAll(new BudgetedTransactionRepository(dbContext)); - dbContext.SaveChanges(); - DeleteAllExtension.DeleteAll(new BucketMovementRepository(dbContext)); - dbContext.SaveChanges(); - DeleteAllExtension.DeleteAll(new BankTransactionRepository(dbContext)); - dbContext.SaveChanges(); - - // Depends on BudgetedTransaction & BucketMovement - DeleteAllExtension.DeleteAll(new BucketRepository(dbContext)); - dbContext.SaveChanges(); - - // Depends on Bucket - DeleteAllExtension.DeleteAll(new BucketGroupRepository(dbContext)); - dbContext.SaveChanges(); - - // Depends on BankTransaction - DeleteAllExtension.DeleteAll(new AccountRepository(dbContext)); - dbContext.SaveChanges(); - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/AccountDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/AccountDatabaseTest.cs new file mode 100644 index 0000000..a274ff5 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/AccountDatabaseTest.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class AccountDatabaseTest : BaseDatabaseTest +{ + protected override void CompareEntities(Account expected, Account actual) + { + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.IsActive, actual.IsActive); + } + + public static IEnumerable TestData_Repository + { + get + { + return new[] + { + new object[] { new MockAccountRepository(new MockDatabase()) }, + new object[] { new AccountRepository(new DatabaseContext(MariaDbContextOptions)) } + }; + } + } + + private List SetupTestData(IAccountRepository accountRepository) + { + DeleteAllExtension.DeleteAll(accountRepository); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var account = TestDataGenerator.Current.GenerateAccount(); + result.Add(account); + var repositoryResult = accountRepository.Create(account); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, account.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create(IAccountRepository baseRepository) + { + var accounts = SetupTestData(baseRepository); + RunChecks(baseRepository, accounts); + + DeleteAllExtension.DeleteAll(baseRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update(IAccountRepository baseRepository) + { + var accounts = SetupTestData(baseRepository); + + foreach (var account in accounts) + { + var accountId = account.Id; + account.Name += "Update"; + account.IsActive = 0; + var result = baseRepository.Update(account); + Assert.Equal(1, result); + Assert.Equal(accountId, account.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(baseRepository, accounts); + + DeleteAllExtension.DeleteAll(baseRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete(IAccountRepository baseRepository) + { + var accounts = SetupTestData(baseRepository); + + var deleteResult1 = baseRepository.Delete(accounts.First().Id); + var deleteResult2 = baseRepository.Delete(accounts.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + accounts.Remove(accounts.First()); + accounts.Remove(accounts.Last()); + + RunChecks(baseRepository, accounts); + + DeleteAllExtension.DeleteAll(baseRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BankTransactionDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BankTransactionDatabaseTest.cs new file mode 100644 index 0000000..1d7811d --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BankTransactionDatabaseTest.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BankTransactionDatabaseTest : BaseDatabaseTest +{ + private Account _testAccount1 = new(); + private Account _testAccount2 = new(); + + protected override void CompareEntities(BankTransaction expected, BankTransaction actual) + { + Assert.Equal(expected.AccountId, actual.AccountId); + Assert.Equal(expected.TransactionDate, actual.TransactionDate); + Assert.Equal(expected.Payee, actual.Payee); + Assert.Equal(expected.Memo, actual.Memo); + Assert.Equal(expected.Amount, actual.Amount); + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockBankTransactionRepository(mockDb), + new MockAccountRepository(mockDb) + }, + new object[] + { + new BankTransactionRepository(new DatabaseContext(MariaDbContextOptions)), + new AccountRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData( + IBankTransactionRepository bankTransactionRepository, + IAccountRepository accountRepository) + { + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + + _testAccount1 = TestDataGenerator.Current.GenerateAccount(); + _testAccount2 = TestDataGenerator.Current.GenerateAccount(); + accountRepository.Create(_testAccount1); + accountRepository.Create(_testAccount2); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var bankTransaction = i < 4 + ? TestDataGenerator.Current.GenerateBankTransaction(_testAccount1) + : TestDataGenerator.Current.GenerateBankTransaction(_testAccount2); + result.Add(bankTransaction); + var repositoryResult = bankTransactionRepository.Create(bankTransaction); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, bankTransaction.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IBankTransactionRepository bankTransactionRepository, + IAccountRepository accountRepository) + { + var bankTransactions = SetupTestData(bankTransactionRepository, accountRepository); + RunChecks(bankTransactionRepository, bankTransactions); + + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IBankTransactionRepository bankTransactionRepository, + IAccountRepository accountRepository) + { + var bankTransactions = SetupTestData(bankTransactionRepository, accountRepository); + + foreach (var bankTransaction in bankTransactions) + { + var bankTransactionId = bankTransaction.Id; + bankTransaction.TransactionDate = bankTransaction.TransactionDate.AddDays(1); + bankTransaction.AccountId = + bankTransaction.AccountId == _testAccount1.Id ? _testAccount2.Id : _testAccount1.Id; + bankTransaction.Payee += "Update"; + bankTransaction.Memo += "Update"; + bankTransaction.Amount += 1; + + var result = bankTransactionRepository.Update(bankTransaction); + Assert.Equal(1, result); + Assert.Equal(bankTransactionId, bankTransaction.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(bankTransactionRepository, bankTransactions); + + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IBankTransactionRepository bankTransactionRepository, + IAccountRepository accountRepository) + { + var bankTransactions = SetupTestData(bankTransactionRepository, accountRepository); + + var deleteResult1 = bankTransactionRepository.Delete(bankTransactions.First().Id); + var deleteResult2 = bankTransactionRepository.Delete(bankTransactions.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + bankTransactions.Remove(bankTransactions.First()); + bankTransactions.Remove(bankTransactions.Last()); + + RunChecks(bankTransactionRepository, bankTransactions); + + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BaseDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BaseDatabaseTest.cs new file mode 100644 index 0000000..4480885 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BaseDatabaseTest.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using OpenBudgeteer.Core.Data; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public abstract class BaseDatabaseTest where TEntity : IEntity +{ + protected static DbContextOptions MariaDbContextOptions + { + get + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [ConfigurationKeyConstants.CONNECTION_PROVIDER] = "mariadb", + [ConfigurationKeyConstants.CONNECTION_SERVER] = Environment.GetEnvironmentVariable(ConfigurationKeyConstants.CONNECTION_SERVER) ?? "192.168.178.153", + [ConfigurationKeyConstants.CONNECTION_PORT] = Environment.GetEnvironmentVariable(ConfigurationKeyConstants.CONNECTION_PORT) ?? "3306", + [ConfigurationKeyConstants.CONNECTION_USER] = Environment.GetEnvironmentVariable(ConfigurationKeyConstants.CONNECTION_USER) ?? "openbudgeteer_unit_test", + [ConfigurationKeyConstants.CONNECTION_PASSWORD] = Environment.GetEnvironmentVariable(ConfigurationKeyConstants.CONNECTION_PASSWORD) ?? "openbudgeteer_unit_test", + [ConfigurationKeyConstants.CONNECTION_DATABASE] = Environment.GetEnvironmentVariable(ConfigurationKeyConstants.CONNECTION_DATABASE) ?? "openbudgeteer_unit_test", + }!) + .Build(); + var contextOptions = DbContextOptionsFactory.GetContextOptions(configuration); + var dbContext = new DatabaseContext(contextOptions); + dbContext.Database.Migrate(); + return contextOptions; + } + } + + protected abstract void CompareEntities(TEntity expected, TEntity actual); + // public abstract void Create(IBaseRepository baseRepository); + // public abstract void Update(IBaseRepository baseRepository); + // public abstract void Delete(IBaseRepository baseRepository); + + protected virtual void RunChecks(IBaseRepository baseRepository, List testEntities) + { + var dbEntities = baseRepository.All().ToList(); + //Assert.Equal(4, dbEntity.Count); + foreach (var testEntity in testEntities) + { + var dbEntity = dbEntities.First(i => i.Id == testEntity.Id); + CompareEntities(testEntity, dbEntity); + } + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BucketDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BucketDatabaseTest.cs new file mode 100644 index 0000000..a70e368 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BucketDatabaseTest.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BucketDatabaseTest : BaseDatabaseTest +{ + private BucketGroup _testBucketGroup1 = new(); + private BucketGroup _testBucketGroup2 = new(); + + protected override void CompareEntities(Bucket expected, Bucket actual) + { + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.BucketGroupId, actual.BucketGroupId); + Assert.Equal(expected.ColorCode, actual.ColorCode); + Assert.Equal(expected.TextColorCode, actual.TextColorCode); + Assert.Equal(expected.ValidFrom, actual.ValidFrom); + Assert.Equal(expected.IsInactive, actual.IsInactive); + Assert.Equal(expected.IsInactiveFrom, actual.IsInactiveFrom); + + Assert.NotNull(expected.BucketVersions); + Assert.NotNull(actual.BucketVersions); + Assert.Equal(expected.BucketVersions.Count, actual.BucketVersions.Count); + var expectedBucketVersion = expected.BucketVersions.First(); + var actualBucketVersion = actual.BucketVersions.First(); + Assert.Equal(expectedBucketVersion.BucketId, actualBucketVersion.BucketId); + Assert.Equal(expectedBucketVersion.Version, actualBucketVersion.Version); + Assert.Equal(expectedBucketVersion.BucketType, actualBucketVersion.BucketType); + Assert.Equal(expectedBucketVersion.BucketTypeXParam, actualBucketVersion.BucketTypeXParam); + Assert.Equal(expectedBucketVersion.BucketTypeYParam, actualBucketVersion.BucketTypeYParam); + Assert.Equal(expectedBucketVersion.BucketTypeZParam, actualBucketVersion.BucketTypeZParam); + Assert.Equal(expectedBucketVersion.Notes, actualBucketVersion.Notes); + Assert.Equal(expectedBucketVersion.ValidFrom, actualBucketVersion.ValidFrom); + } + + protected override void RunChecks(IBaseRepository baseRepository, List testEntities) + { + var dbEntities = baseRepository.AllWithIncludedEntities().ToList(); + foreach (var testEntity in testEntities) + { + var dbEntity = dbEntities.First(i => i.Id == testEntity.Id); + CompareEntities(testEntity, dbEntity); + } + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockBucketRepository(mockDb), + new MockBucketGroupRepository(mockDb) + }, + new object[] + { + new BucketRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketGroupRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData( + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + + _testBucketGroup1 = TestDataGenerator.Current.GenerateBucketGroup(); + _testBucketGroup2 = TestDataGenerator.Current.GenerateBucketGroup(); + + bucketGroupRepository.Create(_testBucketGroup1); + bucketGroupRepository.Create(_testBucketGroup2); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var bucket = i < 4 + ? TestDataGenerator.Current.GenerateBucket(_testBucketGroup1) + : TestDataGenerator.Current.GenerateBucket(_testBucketGroup2); + result.Add(bucket); + var repositoryResult = bucketRepository.Create(bucket); + Assert.Equal(2, repositoryResult); // Bucket + BucketVersion + Assert.NotEqual(Guid.Empty, bucket.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var buckets = SetupTestData(bucketRepository, bucketGroupRepository); + RunChecks(bucketRepository, buckets); + + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var buckets = SetupTestData(bucketRepository, bucketGroupRepository); + + foreach (var bucket in buckets) + { + var bucketId = bucket.Id; + bucket.Name += "Update"; + bucket.BucketGroupId = _testBucketGroup2.Id; + bucket.ColorCode += "Update"; + bucket.TextColorCode += "Update"; + bucket.ValidFrom = bucket.ValidFrom.AddDays(1); + bucket.IsInactive = !bucket.IsInactive; + bucket.IsInactiveFrom = bucket.IsInactive + ? new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day) + : DateTime.MinValue; + + var result = bucketRepository.Update(bucket); + Assert.Equal(1, result); + Assert.Equal(bucketId, bucket.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(bucketRepository, buckets); + + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var buckets = SetupTestData(bucketRepository, bucketGroupRepository); + + var deleteResult1 = bucketRepository.Delete(buckets.First().Id); + var deleteResult2 = bucketRepository.Delete(buckets.Last().Id); + Assert.Equal(2, deleteResult1); // Bucket + BucketVersion + Assert.Equal(2, deleteResult2); // Bucket + BucketVersion + buckets.Remove(buckets.First()); + buckets.Remove(buckets.Last()); + + RunChecks(bucketRepository, buckets); + + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BucketGroupDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BucketGroupDatabaseTest.cs new file mode 100644 index 0000000..562442c --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BucketGroupDatabaseTest.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BucketGroupDatabaseTest : BaseDatabaseTest +{ + protected override void CompareEntities(BucketGroup expected, BucketGroup actual) + { + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.Position, actual.Position); + } + + public static IEnumerable TestData_Repository + { + get + { + return new[] + { + new object[] { new MockBucketGroupRepository(new MockDatabase()) }, + new object[] { new BucketGroupRepository(new DatabaseContext(MariaDbContextOptions)) } + }; + } + } + + private List SetupTestData(IBucketGroupRepository bucketGroupRepository) + { + DeleteAllExtension.DeleteAll(bucketGroupRepository); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var bucketGroup = TestDataGenerator.Current.GenerateBucketGroup(); + result.Add(bucketGroup); + var repositoryResult = bucketGroupRepository.Create(bucketGroup); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, bucketGroup.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create(IBucketGroupRepository baseRepository) + { + var bucketGroups = SetupTestData(baseRepository); + RunChecks(baseRepository, bucketGroups); + + DeleteAllExtension.DeleteAll(baseRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update(IBucketGroupRepository baseRepository) + { + var bucketGroups = SetupTestData(baseRepository); + + foreach (var bucketGroup in bucketGroups) + { + var bucketGroupId = bucketGroup.Id; + bucketGroup.Name += "Update"; + bucketGroup.Position += 1; + var result = baseRepository.Update(bucketGroup); + Assert.Equal(1, result); + Assert.Equal(bucketGroupId, bucketGroup.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(baseRepository, bucketGroups); + + DeleteAllExtension.DeleteAll(baseRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete(IBucketGroupRepository baseRepository) + { + var bucketGroups = SetupTestData(baseRepository); + + var deleteResult1 = baseRepository.Delete(bucketGroups.First().Id); + var deleteResult2 = baseRepository.Delete(bucketGroups.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + bucketGroups.Remove(bucketGroups.First()); + bucketGroups.Remove(bucketGroups.Last()); + + RunChecks(baseRepository, bucketGroups); + + DeleteAllExtension.DeleteAll(baseRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BucketMovementDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BucketMovementDatabaseTest.cs new file mode 100644 index 0000000..642cd61 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BucketMovementDatabaseTest.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BucketMovementDatabaseTest : BaseDatabaseTest +{ + private BucketGroup _testBucketGroup = new(); + private Bucket _testBucket1 = new(); + private Bucket _testBucket2 = new(); + + protected override void CompareEntities(BucketMovement expected, BucketMovement actual) + { + Assert.Equal(expected.BucketId, actual.BucketId); + Assert.Equal(expected.Amount, actual.Amount); + Assert.Equal(expected.MovementDate, actual.MovementDate); + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockBucketMovementRepository(mockDb), + new MockBucketRepository(mockDb), + new MockBucketGroupRepository(mockDb) + }, + new object[] + { + new BucketMovementRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketGroupRepository(new DatabaseContext(MariaDbContextOptions)), + } + }; + } + } + + private List SetupTestData( + IBucketMovementRepository bucketMovementRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + DeleteAllExtension.DeleteAll(bucketMovementRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + + _testBucketGroup = TestDataGenerator.Current.GenerateBucketGroup(); + bucketGroupRepository.Create(_testBucketGroup); + + _testBucket1 = TestDataGenerator.Current.GenerateBucket(_testBucketGroup); + _testBucket2 = TestDataGenerator.Current.GenerateBucket(_testBucketGroup); + bucketRepository.Create(_testBucket1); + bucketRepository.Create(_testBucket2); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var bucketMovement = i < 4 + ? TestDataGenerator.Current.GenerateBucketMovement(_testBucket1) + : TestDataGenerator.Current.GenerateBucketMovement(_testBucket2); + result.Add(bucketMovement); + var repositoryResult = bucketMovementRepository.Create(bucketMovement); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, bucketMovement.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IBucketMovementRepository bucketMovementRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var bucketMovements = SetupTestData(bucketMovementRepository, bucketRepository, bucketGroupRepository); + RunChecks(bucketMovementRepository, bucketMovements); + + DeleteAllExtension.DeleteAll(bucketMovementRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IBucketMovementRepository bucketMovementRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var bucketMovements = SetupTestData(bucketMovementRepository, bucketRepository, bucketGroupRepository); + + foreach (var bucketMovement in bucketMovements) + { + var bucketMovementId = bucketMovement.Id; + bucketMovement.BucketId = + bucketMovement.BucketId == _testBucket1.Id ? _testBucket2.Id : _testBucket1.Id; + bucketMovement.Amount += 1; + bucketMovement.MovementDate = bucketMovement.MovementDate.AddDays(1); + var result = bucketMovementRepository.Update(bucketMovement); + Assert.Equal(1, result); + Assert.Equal(bucketMovementId, bucketMovement.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(bucketMovementRepository, bucketMovements); + + DeleteAllExtension.DeleteAll(bucketMovementRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IBucketMovementRepository bucketMovementRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var bucketMovements = SetupTestData(bucketMovementRepository, bucketRepository, bucketGroupRepository); + + var deleteResult1 = bucketMovementRepository.Delete(bucketMovements.First().Id); + var deleteResult2 = bucketMovementRepository.Delete(bucketMovements.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + bucketMovements.Remove(bucketMovements.First()); + bucketMovements.Remove(bucketMovements.Last()); + + RunChecks(bucketMovementRepository, bucketMovements); + + DeleteAllExtension.DeleteAll(bucketMovementRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BucketRuleSetDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BucketRuleSetDatabaseTest.cs new file mode 100644 index 0000000..407cefa --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BucketRuleSetDatabaseTest.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BucketRuleSetDatabaseTest : BaseDatabaseTest +{ + private readonly Bucket _incomeBucket = new() + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000001"), + Name = "Income" + }; + private readonly Bucket _transferBucket = new() + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000002"), + Name = "Transfer" + }; + + protected override void CompareEntities(BucketRuleSet expected, BucketRuleSet actual) + { + Assert.Equal(expected.Priority, actual.Priority); + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.TargetBucketId, actual.TargetBucketId); + } + + public static IEnumerable TestData_Repository + { + get + { + return new[] + { + new object[] + { + new MockBucketRuleSetRepository(new MockDatabase()) + }, + new object[] + { + new BucketRuleSetRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData(IBucketRuleSetRepository bucketRuleSetRepository) + { + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + + var bucketRuleSets = new List(); + for (var i = 1; i <= 4; i++) + { + var date = DateTime.Now.AddDays(i); + bucketRuleSets.Add(new() + { + Priority = i, + Name = $"RuleSet {i}", + TargetBucketId = i < 4 ? _incomeBucket.Id : _transferBucket.Id + }); + } + + foreach (var bucketRuleSet in bucketRuleSets) + { + var result = bucketRuleSetRepository.Create(bucketRuleSet); + Assert.Equal(1, result); + Assert.NotEqual(Guid.Empty, bucketRuleSet.Id); + } + + return bucketRuleSets; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create(IBucketRuleSetRepository bucketRuleSetRepository) + { + var bucketRuleSets = SetupTestData(bucketRuleSetRepository); + RunChecks(bucketRuleSetRepository, bucketRuleSets); + + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update(IBucketRuleSetRepository bucketRuleSetRepository) + { + var bucketRuleSets = SetupTestData(bucketRuleSetRepository); + + foreach (var bucketRuleSet in bucketRuleSets) + { + var bucketRuleSetId = bucketRuleSet.Id; + bucketRuleSet.Priority += 1; + bucketRuleSet.Name += "Update"; + bucketRuleSet.TargetBucketId = + bucketRuleSet.TargetBucketId == _incomeBucket.Id ? _transferBucket.Id : _incomeBucket.Id; + var result = bucketRuleSetRepository.Update(bucketRuleSet); + Assert.Equal(1, result); + Assert.Equal(bucketRuleSetId, bucketRuleSet.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(bucketRuleSetRepository, bucketRuleSets); + + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete(IBucketRuleSetRepository bucketRuleSetRepository) + { + var bucketRuleSets = SetupTestData(bucketRuleSetRepository); + + var deleteResult1 = bucketRuleSetRepository.Delete(bucketRuleSets.First().Id); + var deleteResult2 = bucketRuleSetRepository.Delete(bucketRuleSets.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + bucketRuleSets.Remove(bucketRuleSets.First()); + bucketRuleSets.Remove(bucketRuleSets.Last()); + + RunChecks(bucketRuleSetRepository, bucketRuleSets); + + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BucketVersionDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BucketVersionDatabaseTest.cs new file mode 100644 index 0000000..7caea5e --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BucketVersionDatabaseTest.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BucketVersionDatabaseTest : BaseDatabaseTest +{ + private readonly Bucket _incomeBucket = new() + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000001"), + Name = "Income" + }; + private readonly Bucket _transferBucket = new() + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000002"), + Name = "Transfer" + }; + + protected override void CompareEntities(BucketVersion expected, BucketVersion actual) + { + Assert.Equal(expected.BucketId, actual.BucketId); + Assert.Equal(expected.Version, actual.Version); + Assert.Equal(expected.BucketType, actual.BucketType); + Assert.Equal(expected.BucketTypeXParam, actual.BucketTypeXParam); + Assert.Equal(expected.BucketTypeYParam, actual.BucketTypeYParam); + Assert.Equal(expected.BucketTypeZParam, actual.BucketTypeZParam); + Assert.Equal(expected.Notes, actual.Notes); + Assert.Equal(expected.ValidFrom, actual.ValidFrom); + } + + public static IEnumerable TestData_Repository + { + get + { + return new[] + { + new object[] + { + new MockBucketVersionRepository(new MockDatabase()) + }, + new object[] + { + new BucketVersionRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData(IBucketVersionRepository bucketVersionRepository) + { + DeleteAllExtension.DeleteAll(bucketVersionRepository); + + var bucketVersions = new List(); + for (var i = 1; i <= 4; i++) + { + var date = DateTime.Now.AddDays(i); + bucketVersions.Add(new() + { + BucketId = i < 4 ? _incomeBucket.Id : _transferBucket.Id, + Version = 1, + BucketType = i, + BucketTypeXParam = i, + BucketTypeYParam = i, + BucketTypeZParam = new DateTime(date.Year, date.Month, date.Day), + Notes = $"Notes {i}", + ValidFrom = new DateTime(date.Year, date.Month, date.Day) + }); + } + + foreach (var bucketVersion in bucketVersions) + { + var result = bucketVersionRepository.Create(bucketVersion); + Assert.Equal(1, result); + Assert.NotEqual(Guid.Empty, bucketVersion.Id); + } + + return bucketVersions; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create(IBucketVersionRepository bucketVersionRepository) + { + var bucketVersions = SetupTestData(bucketVersionRepository); + RunChecks(bucketVersionRepository, bucketVersions); + + DeleteAllExtension.DeleteAll(bucketVersionRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update(IBucketVersionRepository bucketVersionRepository) + { + var bucketVersions = SetupTestData(bucketVersionRepository); + + foreach (var bucketVersion in bucketVersions) + { + var bucketVersionId = bucketVersion.Id; + bucketVersion.BucketId = + bucketVersion.BucketId == _incomeBucket.Id ? _transferBucket.Id : _incomeBucket.Id; + bucketVersion.Version += 1; + bucketVersion.BucketType += 1; + bucketVersion.BucketTypeXParam += 1; + bucketVersion.BucketTypeYParam += 1; + bucketVersion.BucketTypeZParam = bucketVersion.BucketTypeZParam.AddDays(1); + bucketVersion.Notes += "Update"; + bucketVersion.ValidFrom = bucketVersion.ValidFrom.AddDays(1); + var result = bucketVersionRepository.Update(bucketVersion); + Assert.Equal(1, result); + Assert.Equal(bucketVersionId, bucketVersion.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(bucketVersionRepository, bucketVersions); + + DeleteAllExtension.DeleteAll(bucketVersionRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete(IBucketVersionRepository bucketVersionRepository) + { + var bucketVersions = SetupTestData(bucketVersionRepository); + + var deleteResult1 = bucketVersionRepository.Delete(bucketVersions.First().Id); + var deleteResult2 = bucketVersionRepository.Delete(bucketVersions.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + bucketVersions.Remove(bucketVersions.First()); + bucketVersions.Remove(bucketVersions.Last()); + + RunChecks(bucketVersionRepository, bucketVersions); + + DeleteAllExtension.DeleteAll(bucketVersionRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/BudgetedTransactionDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/BudgetedTransactionDatabaseTest.cs new file mode 100644 index 0000000..7cdb285 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/BudgetedTransactionDatabaseTest.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class BudgetedTransactionDatabaseTest : BaseDatabaseTest +{ + private readonly Bucket _incomeBucket = new() + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000001"), + Name = "Income" + }; + private readonly Bucket _transferBucket = new() + { + Id = Guid.Parse("00000000-0000-0000-0000-000000000002"), + Name = "Transfer" + }; + + private Account _testAccount = new(); + private BankTransaction _testBankTransaction1 = new(); + private BankTransaction _testBankTransaction2 = new(); + private BucketGroup _testBucketGroup = new(); + private Bucket _testBucket1 = new(); + private Bucket _testBucket2 = new(); + + protected override void CompareEntities(BudgetedTransaction expected, BudgetedTransaction actual) + { + Assert.Equal(expected.TransactionId, actual.TransactionId); + Assert.Equal(expected.BucketId, actual.BucketId); + Assert.Equal(expected.Amount, actual.Amount); + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockBudgetedTransactionRepository(mockDb), + new MockAccountRepository(mockDb), + new MockBankTransactionRepository(mockDb), + new MockBucketRepository(mockDb), + new MockBucketGroupRepository(mockDb) + }, + new object[] + { + new BudgetedTransactionRepository(new DatabaseContext(MariaDbContextOptions)), + new AccountRepository(new DatabaseContext(MariaDbContextOptions)), + new BankTransactionRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketGroupRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData( + IBudgetedTransactionRepository budgetedTransactionRepository, + IAccountRepository accountRepository, + IBankTransactionRepository bankTransactionRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + DeleteAllExtension.DeleteAll(budgetedTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + + _testAccount = TestDataGenerator.Current.GenerateAccount(); + accountRepository.Create(_testAccount); + + _testBankTransaction1 = TestDataGenerator.Current.GenerateBankTransaction(_testAccount); + _testBankTransaction2 = TestDataGenerator.Current.GenerateBankTransaction(_testAccount); + _testBankTransaction2.Amount = -25; + bankTransactionRepository.Create(_testBankTransaction1); + bankTransactionRepository.Create(_testBankTransaction2); + + _testBucketGroup = TestDataGenerator.Current.GenerateBucketGroup(); + bucketGroupRepository.Create(_testBucketGroup); + + _testBucket1 = TestDataGenerator.Current.GenerateBucket(_testBucketGroup); + _testBucket2 = TestDataGenerator.Current.GenerateBucket(_testBucketGroup); + bucketRepository.Create(_testBucket1); + bucketRepository.Create(_testBucket2); + + var budgetedTransactions = new List(); + budgetedTransactions.Add(new() + { + BucketId = _testBucket1.Id, + TransactionId = _testBankTransaction1.Id, + Amount = _testBankTransaction1.Amount + }); + budgetedTransactions.Add(new() + { + BucketId = _testBucket1.Id, + TransactionId = _testBankTransaction2.Id, + Amount = -15 + }); + budgetedTransactions.Add(new() + { + BucketId = _testBucket2.Id, + TransactionId = _testBankTransaction2.Id, + Amount = -10 + }); + + foreach (var budgetedTransaction in budgetedTransactions) + { + var result = budgetedTransactionRepository.Create(budgetedTransaction); + Assert.Equal(1, result); + Assert.NotEqual(Guid.Empty, budgetedTransaction.Id); + } + + return budgetedTransactions; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IBudgetedTransactionRepository budgetedTransactionRepository, + IAccountRepository accountRepository, + IBankTransactionRepository bankTransactionRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var budgetedTransactions = SetupTestData( + budgetedTransactionRepository, + accountRepository, + bankTransactionRepository, + bucketRepository, + bucketGroupRepository); + RunChecks(budgetedTransactionRepository, budgetedTransactions); + + DeleteAllExtension.DeleteAll(budgetedTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IBudgetedTransactionRepository budgetedTransactionRepository, + IAccountRepository accountRepository, + IBankTransactionRepository bankTransactionRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var budgetedTransactions = SetupTestData( + budgetedTransactionRepository, + accountRepository, + bankTransactionRepository, + bucketRepository, + bucketGroupRepository); + + foreach (var budgetedTransaction in budgetedTransactions) + { + Assert.Throws(() => budgetedTransactionRepository.Update(budgetedTransaction)); + // var budgetedTransactionId = budgetedTransaction.Id; + // if (budgetedTransaction.TransactionId == _testBankTransaction1.Id) continue; + // budgetedTransaction.Amount = budgetedTransaction.BucketId == _testBucket1.Id ? -10 : -15; + // var result = budgetedTransactionRepository.Update(budgetedTransaction); + // Assert.Equal(1, result); + // Assert.Equal(budgetedTransactionId, budgetedTransaction.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(budgetedTransactionRepository, budgetedTransactions); + + DeleteAllExtension.DeleteAll(budgetedTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + DeleteAllExtension.DeleteAll(bankTransactionRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IBudgetedTransactionRepository budgetedTransactionRepository, + IAccountRepository accountRepository, + IBankTransactionRepository bankTransactionRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var budgetedTransactions = SetupTestData( + budgetedTransactionRepository, + accountRepository, + bankTransactionRepository, + bucketRepository, + bucketGroupRepository); + + var deleteResult1 = budgetedTransactionRepository.Delete(budgetedTransactions.First().Id); + var deleteResult2 = budgetedTransactionRepository.Delete(budgetedTransactions.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + budgetedTransactions.Remove(budgetedTransactions.First()); + budgetedTransactions.Remove(budgetedTransactions.Last()); + + RunChecks(budgetedTransactionRepository, budgetedTransactions); + + DeleteAllExtension.DeleteAll(budgetedTransactionRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/ImportProfileDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/ImportProfileDatabaseTest.cs new file mode 100644 index 0000000..0a924b8 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/ImportProfileDatabaseTest.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class ImportProfileDatabaseTest : BaseDatabaseTest +{ + private Account _testAccount1 = new(); + private Account _testAccount2 = new(); + + protected override void CompareEntities(ImportProfile expected, ImportProfile actual) + { + Assert.Equal(expected.ProfileName, actual.ProfileName); + Assert.Equal(expected.AccountId, actual.AccountId); + Assert.Equal(expected.HeaderRow, actual.HeaderRow); + Assert.Equal(expected.Delimiter, actual.Delimiter); + Assert.Equal(expected.TextQualifier, actual.TextQualifier); + Assert.Equal(expected.DateFormat, actual.DateFormat); + Assert.Equal(expected.NumberFormat, actual.NumberFormat); + Assert.Equal(expected.TransactionDateColumnName, actual.TransactionDateColumnName); + Assert.Equal(expected.PayeeColumnName, actual.PayeeColumnName); + Assert.Equal(expected.MemoColumnName, actual.MemoColumnName); + Assert.Equal(expected.AmountColumnName, actual.AmountColumnName); + Assert.Equal(expected.AdditionalSettingCreditValue, actual.AdditionalSettingCreditValue); + Assert.Equal(expected.CreditColumnName, actual.CreditColumnName); + Assert.Equal(expected.CreditColumnIdentifierColumnName, actual.CreditColumnIdentifierColumnName); + Assert.Equal(expected.CreditColumnIdentifierValue, actual.CreditColumnIdentifierValue); + Assert.Equal(expected.AdditionalSettingAmountCleanup, actual.AdditionalSettingAmountCleanup); + Assert.Equal(expected.AdditionalSettingAmountCleanupValue, actual.AdditionalSettingAmountCleanupValue); + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockImportProfileRepository(mockDb), + new MockAccountRepository(mockDb) + }, + new object[] + { + new ImportProfileRepository(new DatabaseContext(MariaDbContextOptions)), + new AccountRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData( + IImportProfileRepository importProfileRepository, + IAccountRepository accountRepository) + { + DeleteAllExtension.DeleteAll(importProfileRepository); + DeleteAllExtension.DeleteAll(accountRepository); + + _testAccount1 = TestDataGenerator.Current.GenerateAccount(); + _testAccount2 = TestDataGenerator.Current.GenerateAccount(); + accountRepository.Create(_testAccount1); + accountRepository.Create(_testAccount2); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var importProfile = i < 4 + ? TestDataGenerator.Current.GenerateImportProfile(_testAccount1) + : TestDataGenerator.Current.GenerateImportProfile(_testAccount2); + result.Add(importProfile); + var repositoryResult = importProfileRepository.Create(importProfile); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, importProfile.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IImportProfileRepository importProfileRepository, + IAccountRepository accountRepository) + { + var importProfiles = SetupTestData(importProfileRepository, accountRepository); + RunChecks(importProfileRepository, importProfiles); + + DeleteAllExtension.DeleteAll(importProfileRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IImportProfileRepository importProfileRepository, + IAccountRepository accountRepository) + { + var importProfiles = SetupTestData(importProfileRepository, accountRepository); + var random = new Random(); + + foreach (var importProfile in importProfiles) + { + var importProfileId = importProfile.Id; + importProfile.ProfileName += "Update"; + importProfile.AccountId = + importProfile.AccountId == _testAccount1.Id ? _testAccount2.Id : _testAccount1.Id; + importProfile.HeaderRow += 1; + importProfile.Delimiter = (char)random.Next(32, 127); // Generate a random printable ASCII character + importProfile.TextQualifier = (char)random.Next(32, 127); + importProfile.DateFormat += "Update"; + importProfile.NumberFormat += "Update"; + importProfile.TransactionDateColumnName += "Update"; + importProfile.PayeeColumnName += "Update"; + importProfile.MemoColumnName += "Update"; + importProfile.AmountColumnName += "Update"; + importProfile.AdditionalSettingCreditValue += 1; + importProfile.CreditColumnName += "Update"; + importProfile.CreditColumnIdentifierColumnName += "Update"; + importProfile.CreditColumnIdentifierValue += "Update"; + importProfile.AdditionalSettingAmountCleanup = !importProfile.AdditionalSettingAmountCleanup; + importProfile.AdditionalSettingAmountCleanupValue += "Update"; + + var result = importProfileRepository.Update(importProfile); + Assert.Equal(1, result); + Assert.Equal(importProfileId, importProfile.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(importProfileRepository, importProfiles); + + DeleteAllExtension.DeleteAll(importProfileRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IImportProfileRepository importProfileRepository, + IAccountRepository accountRepository) + { + var importProfiles = SetupTestData(importProfileRepository, accountRepository); + + var deleteResult1 = importProfileRepository.Delete(importProfiles.First().Id); + var deleteResult2 = importProfileRepository.Delete(importProfiles.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + importProfiles.Remove(importProfiles.First()); + importProfiles.Remove(importProfiles.Last()); + + RunChecks(importProfileRepository, importProfiles); + + DeleteAllExtension.DeleteAll(importProfileRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/MappingRuleDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/MappingRuleDatabaseTest.cs new file mode 100644 index 0000000..10944e9 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/MappingRuleDatabaseTest.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class MappingRuleDatabaseTest : BaseDatabaseTest +{ + private BucketGroup _testBucketGroup = new(); + private Bucket _testBucket = new(); + private BucketRuleSet _testBucketRuleSet1 = new(); + private BucketRuleSet _testBucketRuleSet2 = new(); + + protected override void CompareEntities(MappingRule expected, MappingRule actual) + { + Assert.Equal(expected.BucketRuleSetId, actual.BucketRuleSetId); + Assert.Equal(expected.ComparisonField, actual.ComparisonField); + Assert.Equal(expected.ComparisonType, actual.ComparisonType); + Assert.Equal(expected.ComparisonValue, actual.ComparisonValue); + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockMappingRuleRepository(mockDb), + new MockBucketRuleSetRepository(mockDb), + new MockBucketRepository(mockDb), + new MockBucketGroupRepository(mockDb) + }, + new object[] + { + new MappingRuleRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketRuleSetRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketRepository(new DatabaseContext(MariaDbContextOptions)), + new BucketGroupRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData( + IMappingRuleRepository mappingRuleRepository, + IBucketRuleSetRepository bucketRuleSetRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + DeleteAllExtension.DeleteAll(mappingRuleRepository); + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + + _testBucketGroup = TestDataGenerator.Current.GenerateBucketGroup(); + bucketGroupRepository.Create(_testBucketGroup); + + _testBucket = TestDataGenerator.Current.GenerateBucket(_testBucketGroup); + bucketRepository.Create(_testBucket); + + _testBucketRuleSet1 = TestDataGenerator.Current.GenerateBucketRuleSet(_testBucket); + _testBucketRuleSet2 = TestDataGenerator.Current.GenerateBucketRuleSet(_testBucket); + bucketRuleSetRepository.Create(_testBucketRuleSet1); + bucketRuleSetRepository.Create(_testBucketRuleSet2); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var mappingRule = i < 4 + ? TestDataGenerator.Current.GenerateMappingRule(_testBucketRuleSet1) + : TestDataGenerator.Current.GenerateMappingRule(_testBucketRuleSet2); + result.Add(mappingRule); + var repositoryResult = mappingRuleRepository.Create(mappingRule); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, mappingRule.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IMappingRuleRepository mappingRuleRepository, + IBucketRuleSetRepository bucketRuleSetRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var mappingRules = SetupTestData( + mappingRuleRepository, bucketRuleSetRepository, bucketRepository, bucketGroupRepository); + RunChecks(mappingRuleRepository, mappingRules); + + DeleteAllExtension.DeleteAll(mappingRuleRepository); + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IMappingRuleRepository mappingRuleRepository, + IBucketRuleSetRepository bucketRuleSetRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var mappingRules = SetupTestData( + mappingRuleRepository, bucketRuleSetRepository, bucketRepository, bucketGroupRepository); + + foreach (var mappingRule in mappingRules) + { + var mappingRuleId = mappingRule.Id; + mappingRule.BucketRuleSetId = + mappingRule.BucketRuleSetId == _testBucketRuleSet1.Id ? _testBucketRuleSet2.Id : _testBucketRuleSet1.Id; + mappingRule.ComparisonField += 1; + mappingRule.ComparisonType += 1; + mappingRule.ComparisonValue += "Update"; + + var result = mappingRuleRepository.Update(mappingRule); + Assert.Equal(1, result); + Assert.Equal(mappingRuleId, mappingRule.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(mappingRuleRepository, mappingRules); + + DeleteAllExtension.DeleteAll(mappingRuleRepository); + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IMappingRuleRepository mappingRuleRepository, + IBucketRuleSetRepository bucketRuleSetRepository, + IBucketRepository bucketRepository, + IBucketGroupRepository bucketGroupRepository) + { + var mappingRules = SetupTestData( + mappingRuleRepository, bucketRuleSetRepository, bucketRepository, bucketGroupRepository); + + var deleteResult1 = mappingRuleRepository.Delete(mappingRules.First().Id); + var deleteResult2 = mappingRuleRepository.Delete(mappingRules.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + mappingRules.Remove(mappingRules.First()); + mappingRules.Remove(mappingRules.Last()); + + RunChecks(mappingRuleRepository, mappingRules); + + DeleteAllExtension.DeleteAll(mappingRuleRepository); + DeleteAllExtension.DeleteAll(bucketRuleSetRepository); + DeleteAllExtension.DeleteAll(bucketRepository); + DeleteAllExtension.DeleteAll(bucketGroupRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/Tests/Database/RecurringBankTransactionDatabaseTest.cs b/OpenBudgeteer.Core.Test/Tests/Database/RecurringBankTransactionDatabaseTest.cs new file mode 100644 index 0000000..9d57105 --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/Database/RecurringBankTransactionDatabaseTest.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenBudgeteer.Core.Data.Contracts.Repositories; +using OpenBudgeteer.Core.Data.Entities; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Data.Repository; +using OpenBudgeteer.Core.Test.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Repository; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.Database; + +public class RecurringBankTransactionDatabaseTest : BaseDatabaseTest +{ + private Account _testAccount1 = new(); + private Account _testAccount2 = new(); + + protected override void CompareEntities(RecurringBankTransaction expected, RecurringBankTransaction actual) + { + Assert.Equal(expected.AccountId, actual.AccountId); + Assert.Equal(expected.RecurrenceType, actual.RecurrenceType); + Assert.Equal(expected.RecurrenceAmount, actual.RecurrenceAmount); + Assert.Equal(expected.FirstOccurrenceDate, actual.FirstOccurrenceDate); + Assert.Equal(expected.Payee, actual.Payee); + Assert.Equal(expected.Memo, actual.Memo); + Assert.Equal(expected.Amount, actual.Amount); + } + + public static IEnumerable TestData_Repository + { + get + { + var mockDb = new MockDatabase(); + return new[] + { + new object[] + { + new MockRecurringBankTransactionRepository(mockDb), + new MockAccountRepository(mockDb) + }, + new object[] + { + new RecurringBankTransactionRepository(new DatabaseContext(MariaDbContextOptions)), + new AccountRepository(new DatabaseContext(MariaDbContextOptions)) + } + }; + } + } + + private List SetupTestData( + IRecurringBankTransactionRepository recurringBankTransactionRepository, + IAccountRepository accountRepository) + { + DeleteAllExtension.DeleteAll(recurringBankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + + _testAccount1 = TestDataGenerator.Current.GenerateAccount(); + _testAccount2 = TestDataGenerator.Current.GenerateAccount(); + accountRepository.Create(_testAccount1); + accountRepository.Create(_testAccount2); + + var result = new List(); + for (var i = 1; i <= 4; i++) + { + var bucketMovement = i < 4 + ? TestDataGenerator.Current.GenerateRecurringBankTransaction(_testAccount1) + : TestDataGenerator.Current.GenerateRecurringBankTransaction(_testAccount2); + result.Add(bucketMovement); + var repositoryResult = recurringBankTransactionRepository.Create(bucketMovement); + Assert.Equal(1, repositoryResult); + Assert.NotEqual(Guid.Empty, bucketMovement.Id); + } + + return result; + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Create( + IRecurringBankTransactionRepository recurringBankTransactionRepository, + IAccountRepository accountRepository) + { + var recurringBankTransactions = SetupTestData(recurringBankTransactionRepository, accountRepository); + RunChecks(recurringBankTransactionRepository, recurringBankTransactions); + + DeleteAllExtension.DeleteAll(recurringBankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Update( + IRecurringBankTransactionRepository recurringBankTransactionRepository, + IAccountRepository accountRepository) + { + var recurringBankTransactions = SetupTestData(recurringBankTransactionRepository, accountRepository); + + foreach (var recurringBankTransaction in recurringBankTransactions) + { + var bucketMovementId = recurringBankTransaction.Id; + recurringBankTransaction.AccountId = + recurringBankTransaction.AccountId == _testAccount1.Id ? _testAccount2.Id : _testAccount1.Id; + recurringBankTransaction.RecurrenceType += 1; + recurringBankTransaction.RecurrenceAmount += 1; + recurringBankTransaction.FirstOccurrenceDate = recurringBankTransaction.FirstOccurrenceDate.AddDays(1); + recurringBankTransaction.Payee += "Update"; + recurringBankTransaction.Memo += "Update"; + recurringBankTransaction.Amount += 1; + + var result = recurringBankTransactionRepository.Update(recurringBankTransaction); + Assert.Equal(1, result); + Assert.Equal(bucketMovementId, recurringBankTransaction.Id); // Check if no new Guid has been generated (no CREATE) + } + + RunChecks(recurringBankTransactionRepository, recurringBankTransactions); + + DeleteAllExtension.DeleteAll(recurringBankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } + + [Theory] + [MemberData(nameof(TestData_Repository))] + public void Delete( + IRecurringBankTransactionRepository recurringBankTransactionRepository, + IAccountRepository accountRepository) + { + var recurringBankTransactions = SetupTestData(recurringBankTransactionRepository, accountRepository); + + var deleteResult1 = recurringBankTransactionRepository.Delete(recurringBankTransactions.First().Id); + var deleteResult2 = recurringBankTransactionRepository.Delete(recurringBankTransactions.Last().Id); + Assert.Equal(1, deleteResult1); + Assert.Equal(1, deleteResult2); + recurringBankTransactions.Remove(recurringBankTransactions.First()); + recurringBankTransactions.Remove(recurringBankTransactions.Last()); + + RunChecks(recurringBankTransactionRepository, recurringBankTransactions); + + DeleteAllExtension.DeleteAll(recurringBankTransactionRepository); + DeleteAllExtension.DeleteAll(accountRepository); + } +} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelTest.cs b/OpenBudgeteer.Core.Test/Tests/PageViewModels/AccountPageViewModelTest.cs similarity index 50% rename from OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelTest.cs rename to OpenBudgeteer.Core.Test/Tests/PageViewModels/AccountPageViewModelTest.cs index be2f4d9..b038da4 100644 --- a/OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelTest.cs +++ b/OpenBudgeteer.Core.Test/Tests/PageViewModels/AccountPageViewModelTest.cs @@ -2,17 +2,43 @@ using System.Collections.Generic; using System.Linq; using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Services; using OpenBudgeteer.Core.ViewModels.PageViewModels; using Xunit; -namespace OpenBudgeteer.Core.Test.ViewModelTest; +namespace OpenBudgeteer.Core.Test.Tests.PageViewModels; -public class AccountViewModelTest : BaseTest +public class AccountPageViewModelTest { - public AccountViewModelTest() : base(nameof(AccountViewModelTest)) + [Fact] + public void LoadData_CheckNameAndLoadOnlyActiveAccounts() { - } + var serviceManager = new MockServiceManager(new MockDatabase()); + + var accounts = new List() + { + new() {Name = "Test Account1", IsActive = 1}, + new() {Name = "Test Account2", IsActive = 1}, + new() {Name = "Test Account3", IsActive = 0} + }; + foreach (var account in accounts) + { + serviceManager.AccountService.Create(account); + } + + var viewModel = new AccountPageViewModel(serviceManager); + viewModel.LoadData(); + + Assert.Equal(2, viewModel.Accounts.Count); + + var testItem1 = viewModel.Accounts.ElementAt(0); + var testItem2 = viewModel.Accounts.ElementAt(1); + Assert.Equal("Test Account1", testItem1.Name); + Assert.Equal("Test Account2", testItem2.Name); + } + public static IEnumerable TestData_LoadData_CheckTransactionCalculations { get @@ -33,12 +59,14 @@ public void LoadData_CheckTransactionCalculations( decimal expectedIn, decimal expectedOut) { - var testAccount = new Account() {Name = "Test Account", IsActive = 1}; - ServiceManager.AccountService.Create(testAccount); + var serviceManager = new MockServiceManager(new MockDatabase()); + + var testAccount = new Account() { Name = "Test Account", IsActive = 1 }; + serviceManager.AccountService.Create(testAccount); foreach (var transactionAmount in transactionAmounts) { - ServiceManager.BankTransactionService.Create(new BankTransaction() + serviceManager.BankTransactionService.Create(new BankTransaction() { AccountId = testAccount.Id, TransactionDate = DateTime.Now, @@ -46,7 +74,7 @@ public void LoadData_CheckTransactionCalculations( }); } - var viewModel = new AccountPageViewModel(ServiceManager); + var viewModel = new AccountPageViewModel(serviceManager); viewModel.LoadData(); var testItem1 = viewModel.Accounts .FirstOrDefault(i => i.AccountId == testAccount.Id); diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelTest.cs b/OpenBudgeteer.Core.Test/Tests/PageViewModels/BucketPageViewModelTest.cs similarity index 72% rename from OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelTest.cs rename to OpenBudgeteer.Core.Test/Tests/PageViewModels/BucketPageViewModelTest.cs index d569dd1..f412b1a 100644 --- a/OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelTest.cs +++ b/OpenBudgeteer.Core.Test/Tests/PageViewModels/BucketPageViewModelTest.cs @@ -3,19 +3,128 @@ using System.Linq; using System.Threading.Tasks; using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Services; using OpenBudgeteer.Core.ViewModels.EntityViewModels; using OpenBudgeteer.Core.ViewModels.Helper; using OpenBudgeteer.Core.ViewModels.PageViewModels; using Xunit; -namespace OpenBudgeteer.Core.Test.ViewModelTest; +namespace OpenBudgeteer.Core.Test.Tests.PageViewModels; -public class BucketPageViewModelTest : BaseTest +public class BucketPageViewModelTest { - public BucketPageViewModelTest() : base(nameof(BucketPageViewModelTest)) + [Fact] + public async Task LoadDataAsync_CheckBucketGroupsNamesAndPositions() { + var serviceManager = new MockServiceManager(new MockDatabase()); + + var bucketGroups = new List() + { + new() { Name = "Bucket Group 1", Position = 1 }, + new() { Name = "Bucket Group 3", Position = 2 }, + new() { Name = "Bucket Group 2", Position = 3 } + }; + foreach (var bucketGroup in bucketGroups) + { + serviceManager.BucketGroupService.Create(bucketGroup); + } + + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); + await viewModel.LoadDataAsync(); + + Assert.Equal(3, viewModel.BucketGroups.Count); + Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(0).Name); + Assert.Equal("Bucket Group 3", viewModel.BucketGroups.ElementAt(1).Name); + Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(2).Name); + + Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); + Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); + Assert.Equal(3, viewModel.BucketGroups.ElementAt(2).Position); } + + [Fact] + public async Task CreateGroup_CheckGroupCreationAndPositions() + { + var serviceManager = new MockServiceManager(new MockDatabase()); + + var bucketGroups = new List() + { + new() { Name = "Bucket Group 1", Position = 1 }, + new() { Name = "Bucket Group 3", Position = 2 }, + new() { Name = "Bucket Group 2", Position = 3 } + }; + foreach (var bucketGroup in bucketGroups) + { + serviceManager.BucketGroupService.Create(bucketGroup); + } + + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); + await viewModel.LoadDataAsync(); + Assert.Equal(3, viewModel.BucketGroups.Count); + + viewModel.CreateEmptyGroup(); + var result = viewModel.NewBucketGroup!.CreateGroup(); + await viewModel.LoadDataAsync(); + + Assert.True(result.IsSuccessful); + Assert.Equal(4, viewModel.BucketGroups.Count); + Assert.Equal("New Bucket Group", viewModel.BucketGroups.ElementAt(0).Name); + Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(1).Name); + Assert.Equal("Bucket Group 3", viewModel.BucketGroups.ElementAt(2).Name); + Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(3).Name); + Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); + Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); + Assert.Equal(3, viewModel.BucketGroups.ElementAt(2).Position); + Assert.Equal(4, viewModel.BucketGroups.ElementAt(3).Position); + } + + [Fact] + public async Task DeleteGroup_CheckGroupDeletionAndPositions() + { + var serviceManager = new MockServiceManager(new MockDatabase()); + + var bucketGroups = new List() + { + new() { Name = "Bucket Group 1", Position = 1 }, + new() { Name = "Bucket Group 3", Position = 2 }, + new() { Name = "Bucket Group 2", Position = 3 } + }; + foreach (var bucketGroup in bucketGroups) + { + serviceManager.BucketGroupService.Create(bucketGroup); + } + + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); + await viewModel.LoadDataAsync(); + + var groupToDelete = viewModel.BucketGroups.ElementAt(1); + var result = groupToDelete.DeleteGroup(); + + Assert.True(result.IsSuccessful); + Assert.True(result.ViewModelReloadRequired); + await viewModel.LoadDataAsync(); + + Assert.Equal(2, viewModel.BucketGroups.Count); + Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(0).Name); + Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(1).Name); + Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); + Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); + + // Reload ViewModel to see if changes has been also reflected onto the database + await viewModel.LoadDataAsync(); + + Assert.Equal(2, viewModel.BucketGroups.Count); + Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(0).Name); + Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(1).Name); + Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); + Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); + } + public static IEnumerable TestData_LoadDataAsync_CheckBucketGroupAssignedBuckets { get @@ -33,8 +142,10 @@ public static IEnumerable TestData_LoadDataAsync_CheckBucketGroupAssig [MemberData(nameof(TestData_LoadDataAsync_CheckBucketGroupAssignedBuckets))] public async Task LoadDataAsync_CheckBucketGroupAssignedBuckets(List bucketNames) { + var serviceManager = new MockServiceManager(new MockDatabase()); + var testBucketGroup = new BucketGroup { Name = "Bucket Group", Position = 1 }; - ServiceManager.BucketGroupService.Create(testBucketGroup); + serviceManager.BucketGroupService.Create(testBucketGroup); foreach (var bucketName in bucketNames) { @@ -46,11 +157,11 @@ public async Task LoadDataAsync_CheckBucketGroupAssignedBuckets(List buc ValidFrom = new DateTime(2010, 1, 1), CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } }; - ServiceManager.BucketService.Create(newBucket); + serviceManager.BucketService.Create(newBucket); } - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager); - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); await viewModel.LoadDataAsync(); var testObject = viewModel.BucketGroups @@ -79,8 +190,10 @@ public static IEnumerable TestData_LoadDataAsync_CheckBucketSorting [MemberData(nameof(TestData_LoadDataAsync_CheckBucketSorting))] public async Task LoadDataAsync_CheckBucketSorting(List bucketNamesUnsorted, List expectedBucketNamesSorted) { + var serviceManager = new MockServiceManager(new MockDatabase()); + var testBucketGroup = new BucketGroup { Name = "Bucket Group", Position = 1 }; - ServiceManager.BucketGroupService.Create(testBucketGroup); + serviceManager.BucketGroupService.Create(testBucketGroup); foreach (var bucketName in bucketNamesUnsorted) { @@ -92,11 +205,11 @@ public async Task LoadDataAsync_CheckBucketSorting(List bucketNamesUnsor ValidFrom = new DateTime(2010, 1, 1), CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } }; - ServiceManager.BucketService.Create(newBucket); + serviceManager.BucketService.Create(newBucket); } - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager); - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); await viewModel.LoadDataAsync(); var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); @@ -147,11 +260,13 @@ public async Task LoadDataAsync_LoadOnlyActiveBuckets( bool expectedBucketAvailable ) { + var serviceManager = new MockServiceManager(new MockDatabase()); + var testAccount = new Account {IsActive = 1, Name = "Account"}; var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; - ServiceManager.AccountService.Create(testAccount); - ServiceManager.BucketGroupService.Create(testBucketGroup); + serviceManager.AccountService.Create(testAccount); + serviceManager.BucketGroupService.Create(testBucketGroup); var testBucket = new Bucket { @@ -168,14 +283,14 @@ bool expectedBucketAvailable } }; - ServiceManager.BucketService.Create(testBucket); + serviceManager.BucketService.Create(testBucket); - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager) + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager) { SelectedYear = testMonth.Year, SelectedMonth = testMonth.Month }; - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); await viewModel.LoadDataAsync(); var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); @@ -187,155 +302,145 @@ bool expectedBucketAvailable [Fact] public async Task LoadDataAsync_CheckValidFromHandling() { - try - { - var testAccount = new Account {IsActive = 1, Name = "Account"}; - var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; + var serviceManager = new MockServiceManager(new MockDatabase()); + + var testAccount = new Account {IsActive = 1, Name = "Account"}; + var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; - ServiceManager.AccountService.Create(testAccount); - ServiceManager.BucketGroupService.Create(testBucketGroup); + serviceManager.AccountService.Create(testAccount); + serviceManager.BucketGroupService.Create(testBucketGroup); - var testBucket1 = new Bucket - { - BucketGroupId = testBucketGroup.Id, - Name = "Bucket Active Current Month", - ValidFrom = new DateTime(2010, 1, 1), - CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } - }; - var testBucket2 = new Bucket - { - BucketGroupId = testBucketGroup.Id, - Name = "Bucket Active Past", - ValidFrom = new DateTime(2009, 1, 1), - CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } - }; - var testBucket3 = new Bucket - { - BucketGroupId = testBucketGroup.Id, - Name = "Bucket Active Future", - ValidFrom = new DateTime(2010, 2, 1), - CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } - }; + var testBucket1 = new Bucket + { + BucketGroupId = testBucketGroup.Id, + Name = "Bucket Active Current Month", + ValidFrom = new DateTime(2010, 1, 1), + CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } + }; + var testBucket2 = new Bucket + { + BucketGroupId = testBucketGroup.Id, + Name = "Bucket Active Past", + ValidFrom = new DateTime(2009, 1, 1), + CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } + }; + var testBucket3 = new Bucket + { + BucketGroupId = testBucketGroup.Id, + Name = "Bucket Active Future", + ValidFrom = new DateTime(2010, 2, 1), + CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } + }; - ServiceManager.BucketService.Create(testBucket1); - ServiceManager.BucketService.Create(testBucket2); - ServiceManager.BucketService.Create(testBucket3); + serviceManager.BucketService.Create(testBucket1); + serviceManager.BucketService.Create(testBucket2); + serviceManager.BucketService.Create(testBucket3); - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager) - { - SelectedYear = 2010, - SelectedMonth = 1 - }; - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); - await viewModel.LoadDataAsync(); + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager) + { + SelectedYear = 2010, + SelectedMonth = 1 + }; + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); + await viewModel.LoadDataAsync(); - var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); - Assert.NotNull(bucketGroup); + var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); + Assert.NotNull(bucketGroup); - Assert.Equal(2, bucketGroup.Buckets.Count); - Assert.Contains(bucketGroup.Buckets, i => i.BucketId == testBucket1.Id); - Assert.Contains(bucketGroup.Buckets, i => i.BucketId == testBucket2.Id); - Assert.DoesNotContain(bucketGroup.Buckets, i => i.BucketId == testBucket3.Id); - } - finally - { - Cleanup(); - } + Assert.Equal(2, bucketGroup.Buckets.Count); + Assert.Contains(bucketGroup.Buckets, i => i.BucketId == testBucket1.Id); + Assert.Contains(bucketGroup.Buckets, i => i.BucketId == testBucket2.Id); + Assert.DoesNotContain(bucketGroup.Buckets, i => i.BucketId == testBucket3.Id); } [Fact] public async Task LoadDataAsync_CheckCalculatedValues() { - try - { - var testAccount = new Account {IsActive = 1, Name = "Account"}; - var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; - - ServiceManager.AccountService.Create(testAccount); - ServiceManager.BucketGroupService.Create(testBucketGroup); - - var testBucket1 = new Bucket - { - BucketGroupId = testBucketGroup.Id, - Name = "Bucket 1", - ValidFrom = new DateTime(2010, 1, 1), - CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } - }; - var testBucket2 = new Bucket - { - BucketGroupId = testBucketGroup.Id, - Name = "Bucket 2", - ValidFrom = new DateTime(2010, 1, 1), - CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } - }; + var serviceManager = new MockServiceManager(new MockDatabase()); - ServiceManager.BucketService.Create(testBucket1); - ServiceManager.BucketService.Create(testBucket2); + var testAccount = new Account {IsActive = 1, Name = "Account"}; + var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; - var testTransactions = new List - { - new() { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = 1 }, - new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = -10 }, - new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = 100 }, - new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = -1000 }, - new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = 10000 }, - new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2009,1,1), Amount = 100000 }, - new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,2,1), Amount = 1000000 }, - }; - foreach (var transaction in testTransactions) - { - ServiceManager.BankTransactionService.Create(transaction); - } + serviceManager.AccountService.Create(testAccount); + serviceManager.BucketGroupService.Create(testBucketGroup); - var testBudgetedTransactions = new List - { - new () { TransactionId = testTransactions[0].Id, BucketId = testBucket1.Id, Amount = 1 }, - new () { TransactionId = testTransactions[1].Id, BucketId = testBucket1.Id, Amount = -5 }, - new () { TransactionId = testTransactions[1].Id, BucketId = testBucket2.Id, Amount = -5 }, - new () { TransactionId = testTransactions[2].Id, BucketId = testBucket1.Id, Amount = 100 }, - new () { TransactionId = testTransactions[3].Id, BucketId = testBucket2.Id, Amount = -1000 }, - new () { TransactionId = testTransactions[4].Id, BucketId = testBucket2.Id, Amount = 10000 }, - new () { TransactionId = testTransactions[5].Id, BucketId = testBucket2.Id, Amount = 100000 }, - new () { TransactionId = testTransactions[6].Id, BucketId = testBucket2.Id, Amount = 1000000 }, - }; - foreach (var budgetedTransaction in testBudgetedTransactions) - { - ServiceManager.BudgetedTransactionService.Create(budgetedTransaction); - } + var testBucket1 = new Bucket + { + BucketGroupId = testBucketGroup.Id, + Name = "Bucket 1", + ValidFrom = new DateTime(2010, 1, 1), + CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } + }; + var testBucket2 = new Bucket + { + BucketGroupId = testBucketGroup.Id, + Name = "Bucket 2", + ValidFrom = new DateTime(2010, 1, 1), + CurrentVersion = new BucketVersion { Version = 1, BucketType = 1 } + }; + + serviceManager.BucketService.Create(testBucket1); + serviceManager.BucketService.Create(testBucket2); - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager) - { - SelectedYear = 2010, - SelectedMonth = 1 - }; - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); - await viewModel.LoadDataAsync(); - - var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); - Assert.NotNull(bucketGroup); - - // This test includes: - // - Bucket Split - var testObject = bucketGroup.Buckets.FirstOrDefault(i => i.BucketId == testBucket1.Id); - Assert.NotNull(testObject); - Assert.Equal(-5, testObject.Activity); - Assert.Equal(96, testObject.Balance); - Assert.Equal(101, testObject.In); - - // This test includes: - // - Bucket Split - // - Include Transactions in previous months for Balance - // - Exclude Transactions in the future - testObject = bucketGroup.Buckets.FirstOrDefault(i => i.BucketId == testBucket2.Id); - Assert.NotNull(testObject); - Assert.Equal(-1005, testObject.Activity); - Assert.Equal(108995, testObject.Balance); - Assert.Equal(10000, testObject.In); + var testTransactions = new List + { + new() { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = 1 }, + new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = -10 }, + new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = 100 }, + new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = -1000 }, + new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,1,1), Amount = 10000 }, + new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2009,1,1), Amount = 100000 }, + new () { AccountId = testAccount.Id, TransactionDate = new DateTime(2010,2,1), Amount = 1000000 }, + }; + foreach (var transaction in testTransactions) + { + serviceManager.BankTransactionService.Create(transaction); } - finally + + var testBudgetedTransactions = new List { - Cleanup(); + new () { TransactionId = testTransactions[0].Id, BucketId = testBucket1.Id, Amount = 1 }, + new () { TransactionId = testTransactions[1].Id, BucketId = testBucket1.Id, Amount = -5 }, + new () { TransactionId = testTransactions[1].Id, BucketId = testBucket2.Id, Amount = -5 }, + new () { TransactionId = testTransactions[2].Id, BucketId = testBucket1.Id, Amount = 100 }, + new () { TransactionId = testTransactions[3].Id, BucketId = testBucket2.Id, Amount = -1000 }, + new () { TransactionId = testTransactions[4].Id, BucketId = testBucket2.Id, Amount = 10000 }, + new () { TransactionId = testTransactions[5].Id, BucketId = testBucket2.Id, Amount = 100000 }, + new () { TransactionId = testTransactions[6].Id, BucketId = testBucket2.Id, Amount = 1000000 }, + }; + foreach (var budgetedTransaction in testBudgetedTransactions) + { + serviceManager.BudgetedTransactionService.Create(budgetedTransaction); } + + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager) + { + SelectedYear = 2010, + SelectedMonth = 1 + }; + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); + await viewModel.LoadDataAsync(); + + var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); + Assert.NotNull(bucketGroup); + + // This test includes: + // - Bucket Split + var testObject = bucketGroup.Buckets.FirstOrDefault(i => i.BucketId == testBucket1.Id); + Assert.NotNull(testObject); + Assert.Equal(-5, testObject.Activity); + Assert.Equal(96, testObject.Balance); + Assert.Equal(101, testObject.In); + + // This test includes: + // - Bucket Split + // - Include Transactions in previous months for Balance + // - Exclude Transactions in the future + testObject = bucketGroup.Buckets.FirstOrDefault(i => i.BucketId == testBucket2.Id); + Assert.NotNull(testObject); + Assert.Equal(-1005, testObject.Activity); + Assert.Equal(108995, testObject.Balance); + Assert.Equal(10000, testObject.In); } public static IEnumerable TestData_CheckWantAndDetailCalculation_MonthlyExpenses @@ -443,20 +548,12 @@ public async Task LoadDataAsync_CheckWantAndDetailCalculation_MonthlyExpenses( decimal expectedActivity ) { - try - { - var testObject = await ExecuteBucketCreationAndTransactionMovementsAsync( - testBucket, testTransactions, testBucketMovements, new DateTime(2010,1,1)); + var testObject = await ExecuteBucketCreationAndTransactionMovementsAsync( + testBucket, testTransactions, testBucketMovements, new DateTime(2010,1,1)); - Assert.Equal(expectedWant, testObject.Want); - Assert.Equal(expectedIn, testObject.In); - Assert.Equal(expectedActivity, testObject.Activity); - - } - finally - { - Cleanup(); - } + Assert.Equal(expectedWant, testObject.Want); + Assert.Equal(expectedIn, testObject.In); + Assert.Equal(expectedActivity, testObject.Activity); } public static IEnumerable TestData_CheckWantAndDetailCalculation_ExpenseEveryXMonths @@ -892,22 +989,15 @@ public async Task LoadDataAsync_CheckWantAndDetailCalculation_ExpenseEveryXMonth int expectedProgress ) { - try - { - var testObject = await ExecuteBucketCreationAndTransactionMovementsAsync( - testBucket, testTransactions, testBucketMovements, new DateTime(2010,1,1)); + var testObject = await ExecuteBucketCreationAndTransactionMovementsAsync( + testBucket, testTransactions, testBucketMovements, new DateTime(2010,1,1)); - Assert.Equal(expectedWant, testObject.Want); - Assert.Equal(expectedIn, testObject.In); - Assert.Equal(expectedActivity, testObject.Activity); - Assert.Equal(expectedBalance, testObject.Balance); - Assert.Equal(expectedDetails, testObject.Details); - Assert.Equal(expectedProgress, testObject.Progress); - } - finally - { - Cleanup(); - } + Assert.Equal(expectedWant, testObject.Want); + Assert.Equal(expectedIn, testObject.In); + Assert.Equal(expectedActivity, testObject.Activity); + Assert.Equal(expectedBalance, testObject.Balance); + Assert.Equal(expectedDetails, testObject.Details); + Assert.Equal(expectedProgress, testObject.Progress); } private async Task ExecuteBucketCreationAndTransactionMovementsAsync( @@ -916,21 +1006,23 @@ private async Task ExecuteBucketCreationAndTransactionMovements IEnumerable testBucketMovements, DateTime testMonth) { + var serviceManager = new MockServiceManager(new MockDatabase()); + var testAccount = new Account {IsActive = 1, Name = "Account"}; var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; - ServiceManager.AccountService.Create(testAccount); - ServiceManager.BucketGroupService.Create(testBucketGroup); + serviceManager.AccountService.Create(testAccount); + serviceManager.BucketGroupService.Create(testBucketGroup); testBucket.BucketGroupId = testBucketGroup.Id; - ServiceManager.BucketService.Create(testBucket); + serviceManager.BucketService.Create(testBucket); foreach (var testTransaction in testTransactions) { testTransaction.AccountId = testAccount.Id; - ServiceManager.BankTransactionService.Create(testTransaction); - ServiceManager.BudgetedTransactionService.Create(new BudgetedTransaction + serviceManager.BankTransactionService.Create(testTransaction); + serviceManager.BudgetedTransactionService.Create(new BudgetedTransaction { TransactionId = testTransaction.Id, BucketId = testBucket.Id, @@ -941,15 +1033,15 @@ private async Task ExecuteBucketCreationAndTransactionMovements foreach (var testBucketMovement in testBucketMovements) { testBucketMovement.BucketId = testBucket.Id; - ServiceManager.BucketMovementService.Create(testBucketMovement); + serviceManager.BucketMovementService.Create(testBucketMovement); } - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager) + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager) { SelectedYear = testMonth.Year, SelectedMonth = testMonth.Month }; - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); + var viewModel = new BucketPageViewModel(serviceManager, monthSelectorViewModel); await viewModel.LoadDataAsync(); var bucketGroup = viewModel.BucketGroups.FirstOrDefault(i => i.BucketGroupId == testBucketGroup.Id); @@ -981,16 +1073,18 @@ public static IEnumerable TestData_DistributeBudget_CheckDistributedMo public void DistributeBudget_CheckDistributedMoney( IEnumerable> testBuckets) { + var serviceManager = new MockServiceManager(new MockDatabase()); + var testAccount = new Account {IsActive = 1, Name = "Account"}; var testBucketGroup = new BucketGroup {Name = "Bucket Group", Position = 1}; - ServiceManager.AccountService.Create(testAccount); - ServiceManager.BucketGroupService.Create(testBucketGroup); + serviceManager.AccountService.Create(testAccount); + serviceManager.BucketGroupService.Create(testBucketGroup); foreach (var (bucket, bucketVersion) in testBuckets) { bucket.BucketGroupId = testBucketGroup.Id; - ServiceManager.BucketService.Create(bucket); + serviceManager.BucketService.Create(bucket); } } } diff --git a/OpenBudgeteer.Core.Test/Tests/PageViewModels/ImportPageViewModelTest.cs b/OpenBudgeteer.Core.Test/Tests/PageViewModels/ImportPageViewModelTest.cs new file mode 100644 index 0000000..172495b --- /dev/null +++ b/OpenBudgeteer.Core.Test/Tests/PageViewModels/ImportPageViewModelTest.cs @@ -0,0 +1,582 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Services; +using OpenBudgeteer.Core.ViewModels.PageViewModels; +using Xunit; + +namespace OpenBudgeteer.Core.Test.Tests.PageViewModels; + +public class ImportPageViewModelTest +{ + private readonly Account _testAccount = new() + { + Id = Guid.NewGuid(), + Name = "Test Account", + IsActive = 1 + }; + + public ImportPageViewModelTest() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Required to read ANSI Text files + } + + private MockDatabase SetupMockDatabase() + { + return new MockDatabase() + { + Accounts = { [_testAccount.Id] = _testAccount } + }; + } + + public static IEnumerable TestData_LoadData_CheckAvailableProfiles + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Amount", + CreditColumnName = "Credit", + + Delimiter = ';', + TextQualifier = '"', + DateFormat = "dd.MM.yyyy", + NumberFormat = "de-DE", + HeaderRow = 2 + } + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadData_CheckAvailableProfiles))] + public void LoadData_CheckAvailableProfiles( + ImportProfile importProfile) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + var inactiveTestAccount = new Account() { Name = "Inactive Test Account", IsActive = 0 }; + serviceManager.AccountService.Create(inactiveTestAccount); + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + var loadedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + + Assert.Equal(2, viewModel.AvailableImportProfiles.Count); // Dummy + 1 Single Import Profile + Assert.Equal(importProfile.ProfileName, loadedImportProfile.ProfileName); + Assert.Equal(importProfile.AccountId, loadedImportProfile.Account.AccountId); + Assert.Equal(importProfile.TransactionDateColumnName, loadedImportProfile.TransactionDateColumnName); + Assert.Equal(importProfile.PayeeColumnName, loadedImportProfile.PayeeColumnName); + Assert.Equal(importProfile.MemoColumnName, loadedImportProfile.MemoColumnName); + Assert.Equal(importProfile.AmountColumnName, loadedImportProfile.AmountColumnName); + Assert.Equal(importProfile.CreditColumnName, loadedImportProfile.CreditColumnName); + Assert.Equal(importProfile.Delimiter, loadedImportProfile.Delimiter); + Assert.Equal(importProfile.TextQualifier, loadedImportProfile.TextQualifier); + Assert.Equal(importProfile.DateFormat, loadedImportProfile.DateFormat); + Assert.Equal(importProfile.NumberFormat, loadedImportProfile.NumberFormat); + Assert.Equal(importProfile.HeaderRow, loadedImportProfile.HeaderRow); + } + + public static IEnumerable TestData_LoadProfileAsync_CheckSelectedImportProfileHeaders + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Amount (EUR)", + CreditColumnName = "Credit", + + Delimiter = ';', + TextQualifier = '"', + DateFormat = "dd.MM.yyyy", + NumberFormat = "de-DE", + HeaderRow = 11 + }, + "./Resources/TestImportFile1.txt", + new List {"Dummy Column", "Accounting Date", "Date", "Type", "Payee", "Memo", "IBAN", "Amount (EUR)"} + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadProfileAsync_CheckSelectedImportProfileHeaders))] + public async Task LoadProfileAsync_CheckSelectedImportProfileHeaders( + ImportProfile importProfile, + string testFilePath, + List fileHeaders) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); + viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + viewModel.ResetLoadFigures(); + + Assert.Equal(_testAccount.Id, viewModel.SelectedImportProfile.Account.AccountId); + + Assert.Equal(8, viewModel.IdentifiedColumns.Count); // Dummy + 7 Identified columns + for (int i = 1; i < viewModel.IdentifiedColumns.Count; i++) + { + Assert.Equal(fileHeaders[i], viewModel.IdentifiedColumns[i]); + } + } + + public static IEnumerable TestData_LoadProfileAsync_CheckValidateData + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Amount (EUR)", + CreditColumnName = string.Empty, + + Delimiter = ';', + TextQualifier = '"', + DateFormat = "dd.MM.yyyy", + NumberFormat = "de-DE", + HeaderRow = 11 + }, + "./Resources/TestImportFile1.txt" + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateData))] + public async Task LoadProfileAsync_CheckValidateData( + ImportProfile importProfile, + string testFilePath) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); + viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + viewModel.ResetLoadFigures(); + await viewModel.ValidateDataAsync(); + + Assert.Equal(4, viewModel.TotalRecords); + Assert.Equal(4, viewModel.ValidRecords); + Assert.Equal(0, viewModel.RecordsWithErrors); + Assert.Equal(0, viewModel.PotentialDuplicates); + Assert.Equal(4, viewModel.ParsedRecords.Count); + Assert.Empty(viewModel.Duplicates); + + // Check Record 1 + var checkRecord = viewModel.ParsedRecords[0].Result; + Assert.Equal(new DateTime(2022,02,14), checkRecord.TransactionDate); + Assert.Equal("Lorem ipsum", checkRecord.Payee); + Assert.Equal("dolor sit amet", checkRecord.Memo); + Assert.Equal(new decimal(-2.95), checkRecord.Amount); + + // Check Record 2 + checkRecord = viewModel.ParsedRecords[1].Result; + Assert.Equal(new DateTime(2022, 02, 15), checkRecord.TransactionDate); + Assert.Equal("Foobar Company", checkRecord.Payee); + Assert.Equal(string.Empty, checkRecord.Memo); + Assert.Equal(new decimal(-27.5), checkRecord.Amount); + + // Check Record 3 + checkRecord = viewModel.ParsedRecords[2].Result; + Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); + Assert.Equal("EMPLOYER", checkRecord.Payee); + Assert.Equal("Income Feb/2022", checkRecord.Memo); + Assert.Equal(new decimal(43), checkRecord.Amount); + + // Check Record 4 + checkRecord = viewModel.ParsedRecords[3].Result; + Assert.Equal(new DateTime(2022, 02, 17), checkRecord.TransactionDate); + Assert.Equal("The Webshop.com", checkRecord.Payee); + Assert.Equal("Billing", checkRecord.Memo); + Assert.Equal(new decimal(-6.34), checkRecord.Amount); + } + + public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithCreditColumn + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Debit (EUR)", + CreditColumnName = "Credit (EUR)", + + AdditionalSettingCreditValue = 1, + + Delimiter = ';', + TextQualifier = '"', + DateFormat = "dd.MM.yyyy", + NumberFormat = "de-DE", + HeaderRow = 11 + }, + "./Resources/TestImportFile2.txt" + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithCreditColumn))] + public async Task LoadProfileAsync_CheckValidateDataWithCreditColumn( + ImportProfile importProfile, + string testFilePath) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); + viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + viewModel.ResetLoadFigures(); + await viewModel.ValidateDataAsync(); + + Assert.Equal(4, viewModel.TotalRecords); + Assert.Equal(4, viewModel.ValidRecords); + Assert.Equal(0, viewModel.RecordsWithErrors); + Assert.Equal(0, viewModel.PotentialDuplicates); + Assert.Equal(4, viewModel.ParsedRecords.Count); + Assert.Empty(viewModel.Duplicates); + + // Check Record 1 + var checkRecord = viewModel.ParsedRecords[0].Result; + Assert.Equal(new DateTime(2022, 02, 14), checkRecord.TransactionDate); + Assert.Equal("Lorem ipsum", checkRecord.Payee); + Assert.Equal("dolor sit amet", checkRecord.Memo); + Assert.Equal(new decimal(-2.95), checkRecord.Amount); + + // Check Record 2 + checkRecord = viewModel.ParsedRecords[1].Result; + Assert.Equal(new DateTime(2022, 02, 15), checkRecord.TransactionDate); + Assert.Equal("Foobar Company", checkRecord.Payee); + Assert.Equal(string.Empty, checkRecord.Memo); + Assert.Equal(new decimal(-27.5), checkRecord.Amount); + + // Check Record 3 + checkRecord = viewModel.ParsedRecords[2].Result; + Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); + Assert.Equal("EMPLOYER", checkRecord.Payee); + Assert.Equal("Income Feb/2022", checkRecord.Memo); + Assert.Equal(new decimal(43), checkRecord.Amount); + + // Check Record 4 + // Credit Column value is positive in file and should be negative after import + // Debit Column value is 0,00 and should be skipped + checkRecord = viewModel.ParsedRecords[3].Result; + Assert.Equal(new DateTime(2022, 02, 17), checkRecord.TransactionDate); + Assert.Equal("The Webshop.com", checkRecord.Payee); + Assert.Equal("Billing", checkRecord.Memo); + Assert.Equal(new decimal(-6.34), checkRecord.Amount); + } + + public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithInvalidRecords + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Amount (EUR)", + CreditColumnName = string.Empty, + + Delimiter = ';', + TextQualifier = '"', + DateFormat = "dd.MM.yyyy", + NumberFormat = "de-DE", + HeaderRow = 11 + }, + "./Resources/TestImportFile3.txt" + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithInvalidRecords))] + public async Task LoadProfileAsync_CheckValidateDataWithInvalidRecords( + ImportProfile importProfile, + string testFilePath) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); + viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + viewModel.ResetLoadFigures(); + await viewModel.ValidateDataAsync(); + + Assert.Equal(4, viewModel.TotalRecords); + Assert.Equal(2, viewModel.ValidRecords); + Assert.Equal(2, viewModel.RecordsWithErrors); + Assert.Equal(0, viewModel.PotentialDuplicates); + Assert.Equal(4, viewModel.ParsedRecords.Count); + Assert.Empty(viewModel.Duplicates); + + // Check Valid Record 1 + var checkRecord = viewModel.ParsedRecords[0].Result; + Assert.Equal(new DateTime(2022, 02, 14), checkRecord.TransactionDate); + Assert.Equal("Lorem ipsum", checkRecord.Payee); + Assert.Equal("dolor sit amet", checkRecord.Memo); + Assert.Equal(new decimal(-2.95), checkRecord.Amount); + + // Check Valid Record 2 + checkRecord = viewModel.ParsedRecords[2].Result; + Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); + Assert.Equal("EMPLOYER", checkRecord.Payee); + Assert.Equal("Income Feb/2022", checkRecord.Memo); + Assert.Equal(new decimal(43), checkRecord.Amount); + } + + public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithDuplicates + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Amount (EUR)", + CreditColumnName = string.Empty, + + Delimiter = ';', + TextQualifier = '"', + DateFormat = "dd.MM.yyyy", + NumberFormat = "de-DE", + HeaderRow = 11 + }, + "./Resources/TestImportFile3.txt", + "./Resources/TestImportFile1.txt" + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithDuplicates))] + public async Task LoadProfileAsync_CheckValidateDataWithDuplicates( + ImportProfile importProfile, + string testFilePath1, + string testFilePath2) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath1)); + viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + viewModel.ResetLoadFigures(); + await viewModel.ValidateDataAsync(); + + Assert.Equal(4, viewModel.TotalRecords); + Assert.Equal(2, viewModel.ValidRecords); + Assert.Equal(2, viewModel.RecordsWithErrors); + Assert.Equal(0, viewModel.PotentialDuplicates); + Assert.Equal(4, viewModel.ParsedRecords.Count); + Assert.Empty(viewModel.Duplicates); + + await viewModel.ImportDataAsync(); + + Assert.Equal(2, serviceManager.BankTransactionService.GetAll().ToList().Count); + + // Load next file including two duplicates on existing BankTransaction + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath2)); + await viewModel.ValidateDataAsync(); + + Assert.Equal(4, viewModel.TotalRecords); + Assert.Equal(4, viewModel.ValidRecords); + Assert.Equal(0, viewModel.RecordsWithErrors); + Assert.Equal(2, viewModel.PotentialDuplicates); + Assert.Equal(4, viewModel.ParsedRecords.Count); + Assert.Equal(2, viewModel.Duplicates.Count); + + // Exclude just one duplicate and import the rest + viewModel.ExcludeDuplicateRecord(viewModel.Duplicates.First()); + + Assert.Equal(3, viewModel.TotalRecords); + Assert.Equal(3, viewModel.ValidRecords); + Assert.Equal(0, viewModel.RecordsWithErrors); + Assert.Equal(1, viewModel.PotentialDuplicates); + Assert.Equal(3, viewModel.ParsedRecords.Count); + Assert.Single(viewModel.Duplicates); + + await viewModel.ImportDataAsync(false); + + Assert.Equal(5, serviceManager.BankTransactionService.GetAll().ToList().Count); + } + + public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithDifferentSettings + { + get + { + return new[] + { + new object[] + { + new ImportProfile() + { + ProfileName = "Test Profile", + + TransactionDateColumnName = "Date", + PayeeColumnName = "Payee", + MemoColumnName = "Memo", + AmountColumnName = "Amount (USD)", + CreditColumnName = string.Empty, + + Delimiter = ',', + TextQualifier = '\'', + DateFormat = "yyyy-MM-dd", + NumberFormat = "en-US", + HeaderRow = 11 + }, + "./Resources/TestImportFile4.txt" + } + }; + } + } + + [Theory] + [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithDifferentSettings))] + public async Task LoadProfileAsync_CheckValidateDataWithDifferentSettings( + ImportProfile importProfile, + string testFilePath) + { + var serviceManager = new MockServiceManager(SetupMockDatabase()); + + importProfile.AccountId = _testAccount.Id; + serviceManager.ImportProfileService.Create(importProfile); + + var viewModel = new ImportPageViewModel(serviceManager); + viewModel.LoadData(); + + await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); + viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( + i => i.ImportProfileId == importProfile.Id); + viewModel.ResetLoadFigures(); + await viewModel.ValidateDataAsync(); + + Assert.Equal(4, viewModel.TotalRecords); + Assert.Equal(4, viewModel.ValidRecords); + Assert.Equal(0, viewModel.RecordsWithErrors); + Assert.Equal(0, viewModel.PotentialDuplicates); + Assert.Equal(4, viewModel.ParsedRecords.Count); + Assert.Empty(viewModel.Duplicates); + + // Check Record 1 + var checkRecord = viewModel.ParsedRecords[0].Result; + Assert.Equal(new DateTime(2022, 02, 14), checkRecord.TransactionDate); + Assert.Equal("Lorem ipsum", checkRecord.Payee); + Assert.Equal("dolor sit amet", checkRecord.Memo); + Assert.Equal(new decimal(-2.95), checkRecord.Amount); + + // Check Record 2 + checkRecord = viewModel.ParsedRecords[1].Result; + Assert.Equal(new DateTime(2022, 02, 15), checkRecord.TransactionDate); + Assert.Equal("Foobar Company", checkRecord.Payee); + Assert.Equal(string.Empty, checkRecord.Memo); + Assert.Equal(new decimal(-27.5), checkRecord.Amount); + + // Check Record 3 + checkRecord = viewModel.ParsedRecords[2].Result; + Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); + Assert.Equal("EMPLOYER", checkRecord.Payee); + Assert.Equal("Income Feb/2022", checkRecord.Memo); + Assert.Equal(new decimal(43), checkRecord.Amount); + + // Check Record 4 + checkRecord = viewModel.ParsedRecords[3].Result; + Assert.Equal(new DateTime(2022, 02, 17), checkRecord.TransactionDate); + Assert.Equal("The Webshop.com", checkRecord.Payee); + Assert.Equal("Billing", checkRecord.Memo); + Assert.Equal(new decimal(-6.34), checkRecord.Amount); + } +} diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/TransactionViewModelTest.cs b/OpenBudgeteer.Core.Test/Tests/PageViewModels/TransactionPageViewModelTest.cs similarity index 90% rename from OpenBudgeteer.Core.Test/ViewModelTest/TransactionViewModelTest.cs rename to OpenBudgeteer.Core.Test/Tests/PageViewModels/TransactionPageViewModelTest.cs index 5621bc6..4030a19 100644 --- a/OpenBudgeteer.Core.Test/ViewModelTest/TransactionViewModelTest.cs +++ b/OpenBudgeteer.Core.Test/Tests/PageViewModels/TransactionPageViewModelTest.cs @@ -3,18 +3,16 @@ using System.Linq; using System.Threading.Tasks; using OpenBudgeteer.Core.Data.Entities.Models; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Services; using OpenBudgeteer.Core.ViewModels.Helper; using OpenBudgeteer.Core.ViewModels.PageViewModels; using Xunit; -namespace OpenBudgeteer.Core.Test.ViewModelTest; +namespace OpenBudgeteer.Core.Test.Tests.PageViewModels; -public class TransactionPageViewModelTest : BaseTest +public class TransactionPageViewModelTest { - public TransactionPageViewModelTest() : base(nameof(TransactionPageViewModelTest)) - { - } - public static IEnumerable TestData_AddRecurringTransactionsAsync_CheckRecurrance => new[] { // Every week, starting 1.1.2010 @@ -309,43 +307,37 @@ public async Task AddRecurringTransactionsAsync_CheckRecurrence( Tuple[] expectedCreationDatesPerMonth ) { - try + var serviceManager = new MockServiceManager(new MockDatabase()); + var testAccount = new Account() { IsActive = 1, Name = "Account" }; + + serviceManager.AccountService.Create(testAccount); + serviceManager.RecurringBankTransactionService.Create(new RecurringBankTransaction { - var testAccount = new Account() { IsActive = 1, Name = "Account" }; + FirstOccurrenceDate = firstOccurrence, + AccountId = testAccount.Id, + RecurrenceType = recurrenceType, + RecurrenceAmount = recurrenceAmount, + Memo = memo + }); - ServiceManager.AccountService.Create(testAccount); - ServiceManager.RecurringBankTransactionService.Create(new RecurringBankTransaction + foreach (var monthData in expectedCreationDatesPerMonth.Select(i => new + { Year = i.Item1, Month = i.Item2, Dates = i.Item3 })) + { + var monthSelectorViewModel = new YearMonthSelectorViewModel(serviceManager) { - FirstOccurrenceDate = firstOccurrence, - AccountId = testAccount.Id, - RecurrenceType = recurrenceType, - RecurrenceAmount = recurrenceAmount, - Memo = memo - }); + SelectedYear = monthData.Year, + SelectedMonth = monthData.Month + }; + var viewModel = new TransactionPageViewModel(serviceManager, monthSelectorViewModel); + await viewModel.LoadDataAsync(); + await viewModel.AddRecurringTransactionsAsync(); + await viewModel.LoadDataAsync(); - foreach (var monthData in expectedCreationDatesPerMonth.Select(i => new - { Year = i.Item1, Month = i.Item2, Dates = i.Item3 })) + Assert.Equal(monthData.Dates.Count(), viewModel.Transactions.Count); + foreach (var item in monthData.Dates) { - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager) - { - SelectedYear = monthData.Year, - SelectedMonth = monthData.Month - }; - var viewModel = new TransactionPageViewModel(ServiceManager, monthSelectorViewModel); - await viewModel.LoadDataAsync(); - await viewModel.AddRecurringTransactionsAsync(); - await viewModel.LoadDataAsync(); - - Assert.Equal(monthData.Dates.Count(), viewModel.Transactions.Count); - foreach (var item in monthData.Dates) - { - Assert.Contains(item, viewModel.Transactions.Select(i => i.TransactionDate)); - } + Assert.Contains(item, viewModel.Transactions.Select(i => i.TransactionDate)); } } - finally - { - Cleanup(); - } } } \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/YearMonthSelectorViewModelTest.cs b/OpenBudgeteer.Core.Test/Tests/PageViewModels/YearMonthSelectorViewModelTest.cs similarity index 84% rename from OpenBudgeteer.Core.Test/ViewModelTest/YearMonthSelectorViewModelTest.cs rename to OpenBudgeteer.Core.Test/Tests/PageViewModels/YearMonthSelectorViewModelTest.cs index 0f084d8..27c0164 100644 --- a/OpenBudgeteer.Core.Test/ViewModelTest/YearMonthSelectorViewModelTest.cs +++ b/OpenBudgeteer.Core.Test/Tests/PageViewModels/YearMonthSelectorViewModelTest.cs @@ -2,17 +2,19 @@ using System.Globalization; using System.Linq; using OpenBudgeteer.Core.Common; +using OpenBudgeteer.Core.Test.Mocking; +using OpenBudgeteer.Core.Test.Mocking.Services; using OpenBudgeteer.Core.ViewModels.Helper; using Xunit; using Xunit.Abstractions; -namespace OpenBudgeteer.Core.Test.ViewModelTest; +namespace OpenBudgeteer.Core.Test.Tests.PageViewModels; -public class YearMonthSelectorViewModelTest : BaseTest +public class YearMonthSelectorViewModelTest { private readonly ITestOutputHelper _output; - public YearMonthSelectorViewModelTest(ITestOutputHelper output) : base(nameof(YearMonthSelectorViewModelTest)) + public YearMonthSelectorViewModelTest(ITestOutputHelper output) { _output = output ?? throw new ArgumentNullException(nameof(output)); } @@ -20,7 +22,8 @@ public YearMonthSelectorViewModelTest(ITestOutputHelper output) : base(nameof(Ye [Fact] public void Constructor_CheckDefaults() { - var viewModel = new YearMonthSelectorViewModel(ServiceManager); + var serviceManager = new MockServiceManager(new MockDatabase()); + var viewModel = new YearMonthSelectorViewModel(serviceManager); Assert.Equal(DateTime.Now.Year, viewModel.SelectedYear); Assert.Equal(DateTime.Now.Month, viewModel.SelectedMonth); @@ -58,7 +61,8 @@ public void Constructor_CheckDefaults() [Fact] public void PreviousMonth_CheckMonth() { - var viewModel = new YearMonthSelectorViewModel(ServiceManager) + var serviceManager = new MockServiceManager(new MockDatabase()); + var viewModel = new YearMonthSelectorViewModel(serviceManager) { SelectedYear = 2010, SelectedMonth = 2 @@ -78,7 +82,8 @@ public void PreviousMonth_CheckMonth() [Fact] public void NextMonth_CheckMonth() { - var viewModel = new YearMonthSelectorViewModel(ServiceManager) + var serviceManager = new MockServiceManager(new MockDatabase()); + var viewModel = new YearMonthSelectorViewModel(serviceManager) { SelectedYear = 2009, SelectedMonth = 11 @@ -98,7 +103,8 @@ public void NextMonth_CheckMonth() [Fact] public void SelectedYearMonthChanged_CheckEventHasBeenInvoked() { - var viewModel = new YearMonthSelectorViewModel(ServiceManager) + var serviceManager = new MockServiceManager(new MockDatabase()); + var viewModel = new YearMonthSelectorViewModel(serviceManager) { SelectedYear = 2010, SelectedMonth = 1 diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelIsolatedTest.cs b/OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelIsolatedTest.cs deleted file mode 100644 index 4f9e0a4..0000000 --- a/OpenBudgeteer.Core.Test/ViewModelTest/AccountViewModelIsolatedTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.ViewModels.PageViewModels; -using Xunit; - -namespace OpenBudgeteer.Core.Test.ViewModelTest; - -[CollectionDefinition("AccountViewModelIsolatedTest", DisableParallelization = true)] -public class AccountViewModelIsolatedTest : BaseTest -{ - public AccountViewModelIsolatedTest() : base(nameof(AccountViewModelIsolatedTest)) - { - } - - [Fact] - public void LoadData_CheckNameAndLoadOnlyActiveAccounts() - { - Cleanup(); - - var accounts = new List() - { - new() {Name = "Test Account1", IsActive = 1}, - new() {Name = "Test Account2", IsActive = 1}, - new() {Name = "Test Account3", IsActive = 0} - }; - foreach (var account in accounts) - { - ServiceManager.AccountService.Create(account); - } - - var viewModel = new AccountPageViewModel(ServiceManager); - viewModel.LoadData(); - - Assert.Equal(2, viewModel.Accounts.Count); - - var testItem1 = viewModel.Accounts.ElementAt(0); - var testItem2 = viewModel.Accounts.ElementAt(1); - - Assert.Equal("Test Account1", testItem1.Name); - Assert.Equal("Test Account2", testItem2.Name); - } -} diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/BaseTest.cs b/OpenBudgeteer.Core.Test/ViewModelTest/BaseTest.cs deleted file mode 100644 index 76a77a1..0000000 --- a/OpenBudgeteer.Core.Test/ViewModelTest/BaseTest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using OpenBudgeteer.Core.Data.Contracts.Services; - -namespace OpenBudgeteer.Core.Test.ViewModelTest; - -public abstract class BaseTest -{ - protected readonly TestServiceManager ServiceManager; - - protected BaseTest(string dbName) - { - ServiceManager = TestServiceManager.CreateUsingSqlite(dbName); - //ServiceManager = TestServiceManager.CreateUsingMySql(); - ServiceManager.CleanupDatabase(); - } - - protected void Cleanup() - { - ServiceManager.CleanupDatabase(); - } -} \ No newline at end of file diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelIsolatedTest.cs b/OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelIsolatedTest.cs deleted file mode 100644 index bf8e061..0000000 --- a/OpenBudgeteer.Core.Test/ViewModelTest/BucketViewModelIsolatedTest.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.ViewModels.Helper; -using OpenBudgeteer.Core.ViewModels.PageViewModels; -using Xunit; - -namespace OpenBudgeteer.Core.Test.ViewModelTest; - -[CollectionDefinition("BucketPageViewModelIsolatedTest", DisableParallelization = true)] -public class BucketPageViewModelIsolatedTest : BaseTest -{ - public BucketPageViewModelIsolatedTest() : base(nameof(BucketPageViewModelIsolatedTest)) - { - } - - [Fact] - public async Task LoadDataAsync_CheckBucketGroupsNamesAndPositions() - { - Cleanup(); - - var bucketGroups = new List() - { - new() { Name = "Bucket Group 1", Position = 1 }, - new() { Name = "Bucket Group 3", Position = 2 }, - new() { Name = "Bucket Group 2", Position = 3 } - }; - foreach (var bucketGroup in bucketGroups) - { - ServiceManager.BucketGroupService.Create(bucketGroup); - } - - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager); - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); - await viewModel.LoadDataAsync(); - - Assert.Equal(3, viewModel.BucketGroups.Count); - Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(0).Name); - Assert.Equal("Bucket Group 3", viewModel.BucketGroups.ElementAt(1).Name); - Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(2).Name); - - Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); - Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); - Assert.Equal(3, viewModel.BucketGroups.ElementAt(2).Position); - } - - [Fact] - public async Task CreateGroup_CheckGroupCreationAndPositions() - { - Cleanup(); - - var bucketGroups = new List() - { - new() { Name = "Bucket Group 1", Position = 1 }, - new() { Name = "Bucket Group 3", Position = 2 }, - new() { Name = "Bucket Group 2", Position = 3 } - }; - foreach (var bucketGroup in bucketGroups) - { - ServiceManager.BucketGroupService.Create(bucketGroup); - } - - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager); - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); - await viewModel.LoadDataAsync(); - Assert.Equal(3, viewModel.BucketGroups.Count); - - viewModel.CreateEmptyGroup(); - var result = viewModel.NewBucketGroup!.CreateGroup(); - await viewModel.LoadDataAsync(); - - Assert.True(result.IsSuccessful); - Assert.Equal(4, viewModel.BucketGroups.Count); - Assert.Equal("New Bucket Group", viewModel.BucketGroups.ElementAt(0).Name); - Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(1).Name); - Assert.Equal("Bucket Group 3", viewModel.BucketGroups.ElementAt(2).Name); - Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(3).Name); - Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); - Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); - Assert.Equal(3, viewModel.BucketGroups.ElementAt(2).Position); - Assert.Equal(4, viewModel.BucketGroups.ElementAt(3).Position); - } - - [Fact] - public async Task DeleteGroup_CheckGroupDeletionAndPositions() - { - Cleanup(); - - var bucketGroups = new List() - { - new() { Name = "Bucket Group 1", Position = 1 }, - new() { Name = "Bucket Group 3", Position = 2 }, - new() { Name = "Bucket Group 2", Position = 3 } - }; - foreach (var bucketGroup in bucketGroups) - { - ServiceManager.BucketGroupService.Create(bucketGroup); - } - - var monthSelectorViewModel = new YearMonthSelectorViewModel(ServiceManager); - var viewModel = new BucketPageViewModel(ServiceManager, monthSelectorViewModel); - await viewModel.LoadDataAsync(); - - var groupToDelete = viewModel.BucketGroups.ElementAt(1); - var result = groupToDelete.DeleteGroup(); - - Assert.True(result.IsSuccessful); - Assert.True(result.ViewModelReloadRequired); - - await viewModel.LoadDataAsync(); - - Assert.Equal(2, viewModel.BucketGroups.Count); - Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(0).Name); - Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(1).Name); - Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); - Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); - - // Reload ViewModel to see if changes has been also reflected onto the database - await viewModel.LoadDataAsync(); - - Assert.Equal(2, viewModel.BucketGroups.Count); - Assert.Equal("Bucket Group 1", viewModel.BucketGroups.ElementAt(0).Name); - Assert.Equal("Bucket Group 2", viewModel.BucketGroups.ElementAt(1).Name); - Assert.Equal(1, viewModel.BucketGroups.ElementAt(0).Position); - Assert.Equal(2, viewModel.BucketGroups.ElementAt(1).Position); - } -} diff --git a/OpenBudgeteer.Core.Test/ViewModelTest/ImportDataViewModelTest.cs b/OpenBudgeteer.Core.Test/ViewModelTest/ImportDataViewModelTest.cs deleted file mode 100644 index cdf2432..0000000 --- a/OpenBudgeteer.Core.Test/ViewModelTest/ImportDataViewModelTest.cs +++ /dev/null @@ -1,613 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using OpenBudgeteer.Core.Data.Entities.Models; -using OpenBudgeteer.Core.ViewModels.EntityViewModels; -using OpenBudgeteer.Core.ViewModels.PageViewModels; -using Xunit; - -namespace OpenBudgeteer.Core.Test.ViewModelTest; -public class ImportPageViewModelTest : BaseTest -{ - private readonly Account _testAccount; - private AccountViewModel TestAccountViewModel => AccountViewModel.CreateFromAccount(ServiceManager, _testAccount); - - public ImportPageViewModelTest() : base(nameof(ImportPageViewModelTest)) - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Required to read ANSI Text files - Cleanup(); - _testAccount = new Account() { Name = "Test Account", IsActive = 1 }; - ServiceManager.AccountService.Create(_testAccount); - } - - public static IEnumerable TestData_LoadData_CheckAvailableProfiles - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Amount", - CreditColumnName = "Credit", - - Delimiter = ';', - TextQualifier = '"', - DateFormat = "dd.MM.yyyy", - NumberFormat = "de-DE", - HeaderRow = 2 - } - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadData_CheckAvailableProfiles))] - public void LoadData_CheckAvailableProfiles( - ImportProfile importProfile) - { - try - { - var inactiveTestAccount = new Account() { Name = "Inactive Test Account", IsActive = 0 }; - ServiceManager.AccountService.Create(inactiveTestAccount); - - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - var loadedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - - var activeAccountViewModel = - viewModel.AvailableAccounts.Single(i => i.AccountId == TestAccountViewModel.AccountId); - Assert.Equal(2, viewModel.AvailableAccounts.Count); // Dummy + 1 Single active account - Assert.Contains(activeAccountViewModel, viewModel.AvailableAccounts); - Assert.Equal(TestAccountViewModel.Name, activeAccountViewModel.Name); - Assert.Equal(TestAccountViewModel.IsActive, activeAccountViewModel.IsActive); - - Assert.Equal(2, viewModel.AvailableImportProfiles.Count); // Dummy + 1 Single Import Profile - Assert.Equal(importProfile.ProfileName, loadedImportProfile.ProfileName); - Assert.Equal(importProfile.AccountId, loadedImportProfile.Account.AccountId); - Assert.Equal(importProfile.TransactionDateColumnName, loadedImportProfile.TransactionDateColumnName); - Assert.Equal(importProfile.PayeeColumnName, loadedImportProfile.PayeeColumnName); - Assert.Equal(importProfile.MemoColumnName, loadedImportProfile.MemoColumnName); - Assert.Equal(importProfile.AmountColumnName, loadedImportProfile.AmountColumnName); - Assert.Equal(importProfile.CreditColumnName, loadedImportProfile.CreditColumnName); - Assert.Equal(importProfile.Delimiter, loadedImportProfile.Delimiter); - Assert.Equal(importProfile.TextQualifier, loadedImportProfile.TextQualifier); - Assert.Equal(importProfile.DateFormat, loadedImportProfile.DateFormat); - Assert.Equal(importProfile.NumberFormat, loadedImportProfile.NumberFormat); - Assert.Equal(importProfile.HeaderRow, loadedImportProfile.HeaderRow); - } - finally - { - Cleanup(); - } - } - - public static IEnumerable TestData_LoadProfileAsync_CheckSelectedImportProfileHeaders - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Amount (EUR)", - CreditColumnName = "Credit", - - Delimiter = ';', - TextQualifier = '"', - DateFormat = "dd.MM.yyyy", - NumberFormat = "de-DE", - HeaderRow = 11 - }, - "./Resources/TestImportFile1.txt", - new List {"Dummy Column", "Accounting Date", "Date", "Type", "Payee", "Memo", "IBAN", "Amount (EUR)"} - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadProfileAsync_CheckSelectedImportProfileHeaders))] - public async Task LoadProfileAsync_CheckSelectedImportProfileHeaders( - ImportProfile importProfile, - string testFilePath, - List fileHeaders) - { - try - { - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); - viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - viewModel.ResetLoadFigures(); - - Assert.Equal(TestAccountViewModel.AccountId, viewModel.SelectedImportProfile.Account.AccountId); - - Assert.Equal(8, viewModel.IdentifiedColumns.Count); // Dummy + 7 Identified columns - for (int i = 1; i < viewModel.IdentifiedColumns.Count; i++) - { - Assert.Equal(fileHeaders[i], viewModel.IdentifiedColumns[i]); - } - } - finally - { - Cleanup(); - } - } - - public static IEnumerable TestData_LoadProfileAsync_CheckValidateData - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Amount (EUR)", - CreditColumnName = string.Empty, - - Delimiter = ';', - TextQualifier = '"', - DateFormat = "dd.MM.yyyy", - NumberFormat = "de-DE", - HeaderRow = 11 - }, - "./Resources/TestImportFile1.txt" - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateData))] - public async Task LoadProfileAsync_CheckValidateData( - ImportProfile importProfile, - string testFilePath) - { - try - { - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); - viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - viewModel.ResetLoadFigures(); - await viewModel.ValidateDataAsync(); - - Assert.Equal(4, viewModel.TotalRecords); - Assert.Equal(4, viewModel.ValidRecords); - Assert.Equal(0, viewModel.RecordsWithErrors); - Assert.Equal(0, viewModel.PotentialDuplicates); - Assert.Equal(4, viewModel.ParsedRecords.Count); - Assert.Empty(viewModel.Duplicates); - - // Check Record 1 - var checkRecord = viewModel.ParsedRecords[0].Result; - Assert.Equal(new DateTime(2022,02,14), checkRecord.TransactionDate); - Assert.Equal("Lorem ipsum", checkRecord.Payee); - Assert.Equal("dolor sit amet", checkRecord.Memo); - Assert.Equal(new decimal(-2.95), checkRecord.Amount); - - // Check Record 2 - checkRecord = viewModel.ParsedRecords[1].Result; - Assert.Equal(new DateTime(2022, 02, 15), checkRecord.TransactionDate); - Assert.Equal("Foobar Company", checkRecord.Payee); - Assert.Equal(string.Empty, checkRecord.Memo); - Assert.Equal(new decimal(-27.5), checkRecord.Amount); - - // Check Record 3 - checkRecord = viewModel.ParsedRecords[2].Result; - Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); - Assert.Equal("EMPLOYER", checkRecord.Payee); - Assert.Equal("Income Feb/2022", checkRecord.Memo); - Assert.Equal(new decimal(43), checkRecord.Amount); - - // Check Record 4 - checkRecord = viewModel.ParsedRecords[3].Result; - Assert.Equal(new DateTime(2022, 02, 17), checkRecord.TransactionDate); - Assert.Equal("The Webshop.com", checkRecord.Payee); - Assert.Equal("Billing", checkRecord.Memo); - Assert.Equal(new decimal(-6.34), checkRecord.Amount); - } - finally - { - Cleanup(); - } - } - - public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithCreditColumn - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Debit (EUR)", - CreditColumnName = "Credit (EUR)", - - AdditionalSettingCreditValue = 1, - - Delimiter = ';', - TextQualifier = '"', - DateFormat = "dd.MM.yyyy", - NumberFormat = "de-DE", - HeaderRow = 11 - }, - "./Resources/TestImportFile2.txt" - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithCreditColumn))] - public async Task LoadProfileAsync_CheckValidateDataWithCreditColumn( - ImportProfile importProfile, - string testFilePath) - { - try - { - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); - viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - viewModel.ResetLoadFigures(); - await viewModel.ValidateDataAsync(); - - Assert.Equal(4, viewModel.TotalRecords); - Assert.Equal(4, viewModel.ValidRecords); - Assert.Equal(0, viewModel.RecordsWithErrors); - Assert.Equal(0, viewModel.PotentialDuplicates); - Assert.Equal(4, viewModel.ParsedRecords.Count); - Assert.Empty(viewModel.Duplicates); - - // Check Record 1 - var checkRecord = viewModel.ParsedRecords[0].Result; - Assert.Equal(new DateTime(2022, 02, 14), checkRecord.TransactionDate); - Assert.Equal("Lorem ipsum", checkRecord.Payee); - Assert.Equal("dolor sit amet", checkRecord.Memo); - Assert.Equal(new decimal(-2.95), checkRecord.Amount); - - // Check Record 2 - checkRecord = viewModel.ParsedRecords[1].Result; - Assert.Equal(new DateTime(2022, 02, 15), checkRecord.TransactionDate); - Assert.Equal("Foobar Company", checkRecord.Payee); - Assert.Equal(string.Empty, checkRecord.Memo); - Assert.Equal(new decimal(-27.5), checkRecord.Amount); - - // Check Record 3 - checkRecord = viewModel.ParsedRecords[2].Result; - Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); - Assert.Equal("EMPLOYER", checkRecord.Payee); - Assert.Equal("Income Feb/2022", checkRecord.Memo); - Assert.Equal(new decimal(43), checkRecord.Amount); - - // Check Record 4 - // Credit Column value is positive in file and should be negative after import - // Debit Column value is 0,00 and should be skipped - checkRecord = viewModel.ParsedRecords[3].Result; - Assert.Equal(new DateTime(2022, 02, 17), checkRecord.TransactionDate); - Assert.Equal("The Webshop.com", checkRecord.Payee); - Assert.Equal("Billing", checkRecord.Memo); - Assert.Equal(new decimal(-6.34), checkRecord.Amount); - } - finally - { - Cleanup(); - } - } - - public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithInvalidRecords - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Amount (EUR)", - CreditColumnName = string.Empty, - - Delimiter = ';', - TextQualifier = '"', - DateFormat = "dd.MM.yyyy", - NumberFormat = "de-DE", - HeaderRow = 11 - }, - "./Resources/TestImportFile3.txt" - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithInvalidRecords))] - public async Task LoadProfileAsync_CheckValidateDataWithInvalidRecords( - ImportProfile importProfile, - string testFilePath) - { - try - { - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); - viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - viewModel.ResetLoadFigures(); - await viewModel.ValidateDataAsync(); - - Assert.Equal(4, viewModel.TotalRecords); - Assert.Equal(2, viewModel.ValidRecords); - Assert.Equal(2, viewModel.RecordsWithErrors); - Assert.Equal(0, viewModel.PotentialDuplicates); - Assert.Equal(4, viewModel.ParsedRecords.Count); - Assert.Empty(viewModel.Duplicates); - - // Check Valid Record 1 - var checkRecord = viewModel.ParsedRecords[0].Result; - Assert.Equal(new DateTime(2022, 02, 14), checkRecord.TransactionDate); - Assert.Equal("Lorem ipsum", checkRecord.Payee); - Assert.Equal("dolor sit amet", checkRecord.Memo); - Assert.Equal(new decimal(-2.95), checkRecord.Amount); - - // Check Valid Record 2 - checkRecord = viewModel.ParsedRecords[2].Result; - Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); - Assert.Equal("EMPLOYER", checkRecord.Payee); - Assert.Equal("Income Feb/2022", checkRecord.Memo); - Assert.Equal(new decimal(43), checkRecord.Amount); - } - finally - { - Cleanup(); - } - } - - public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithDuplicates - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Amount (EUR)", - CreditColumnName = string.Empty, - - Delimiter = ';', - TextQualifier = '"', - DateFormat = "dd.MM.yyyy", - NumberFormat = "de-DE", - HeaderRow = 11 - }, - "./Resources/TestImportFile3.txt", - "./Resources/TestImportFile1.txt" - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithDuplicates))] - public async Task LoadProfileAsync_CheckValidateDataWithDuplicates( - ImportProfile importProfile, - string testFilePath1, - string testFilePath2) - { - try - { - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath1)); - viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - viewModel.ResetLoadFigures(); - await viewModel.ValidateDataAsync(); - - Assert.Equal(4, viewModel.TotalRecords); - Assert.Equal(2, viewModel.ValidRecords); - Assert.Equal(2, viewModel.RecordsWithErrors); - Assert.Equal(0, viewModel.PotentialDuplicates); - Assert.Equal(4, viewModel.ParsedRecords.Count); - Assert.Empty(viewModel.Duplicates); - - await viewModel.ImportDataAsync(); - - Assert.Equal(2, ServiceManager.BankTransactionService.GetAll().ToList().Count); - - // Load next file including two duplicates on existing BankTransaction - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath2)); - await viewModel.ValidateDataAsync(); - - Assert.Equal(4, viewModel.TotalRecords); - Assert.Equal(4, viewModel.ValidRecords); - Assert.Equal(0, viewModel.RecordsWithErrors); - Assert.Equal(2, viewModel.PotentialDuplicates); - Assert.Equal(4, viewModel.ParsedRecords.Count); - Assert.Equal(2, viewModel.Duplicates.Count); - - // Exclude just one duplicate and import the rest - viewModel.ExcludeDuplicateRecord(viewModel.Duplicates.First()); - - Assert.Equal(3, viewModel.TotalRecords); - Assert.Equal(3, viewModel.ValidRecords); - Assert.Equal(0, viewModel.RecordsWithErrors); - Assert.Equal(1, viewModel.PotentialDuplicates); - Assert.Equal(3, viewModel.ParsedRecords.Count); - Assert.Single(viewModel.Duplicates); - - await viewModel.ImportDataAsync(false); - - Assert.Equal(5, ServiceManager.BankTransactionService.GetAll().ToList().Count); - } - finally - { - Cleanup(); - } - } - - public static IEnumerable TestData_LoadProfileAsync_CheckValidateDataWithDifferentSettings - { - get - { - return new[] - { - new object[] - { - new ImportProfile() - { - ProfileName = "Test Profile", - - TransactionDateColumnName = "Date", - PayeeColumnName = "Payee", - MemoColumnName = "Memo", - AmountColumnName = "Amount (USD)", - CreditColumnName = string.Empty, - - Delimiter = ',', - TextQualifier = '\'', - DateFormat = "yyyy-MM-dd", - NumberFormat = "en-US", - HeaderRow = 11 - }, - "./Resources/TestImportFile4.txt" - } - }; - } - } - - [Theory] - [MemberData(nameof(TestData_LoadProfileAsync_CheckValidateDataWithDifferentSettings))] - public async Task LoadProfileAsync_CheckValidateDataWithDifferentSettings( - ImportProfile importProfile, - string testFilePath) - { - try - { - importProfile.AccountId = TestAccountViewModel.AccountId; - ServiceManager.ImportProfileService.Create(importProfile); - - var viewModel = new ImportPageViewModel(ServiceManager); - viewModel.LoadData(); - - await viewModel.HandleOpenFileAsync(File.OpenRead(testFilePath)); - viewModel.SelectedImportProfile = viewModel.AvailableImportProfiles.Single( - i => i.ImportProfileId == importProfile.Id); - viewModel.ResetLoadFigures(); - await viewModel.ValidateDataAsync(); - - Assert.Equal(4, viewModel.TotalRecords); - Assert.Equal(4, viewModel.ValidRecords); - Assert.Equal(0, viewModel.RecordsWithErrors); - Assert.Equal(0, viewModel.PotentialDuplicates); - Assert.Equal(4, viewModel.ParsedRecords.Count); - Assert.Empty(viewModel.Duplicates); - - // Check Record 1 - var checkRecord = viewModel.ParsedRecords[0].Result; - Assert.Equal(new DateTime(2022, 02, 14), checkRecord.TransactionDate); - Assert.Equal("Lorem ipsum", checkRecord.Payee); - Assert.Equal("dolor sit amet", checkRecord.Memo); - Assert.Equal(new decimal(-2.95), checkRecord.Amount); - - // Check Record 2 - checkRecord = viewModel.ParsedRecords[1].Result; - Assert.Equal(new DateTime(2022, 02, 15), checkRecord.TransactionDate); - Assert.Equal("Foobar Company", checkRecord.Payee); - Assert.Equal(string.Empty, checkRecord.Memo); - Assert.Equal(new decimal(-27.5), checkRecord.Amount); - - // Check Record 3 - checkRecord = viewModel.ParsedRecords[2].Result; - Assert.Equal(new DateTime(2022, 02, 16), checkRecord.TransactionDate); - Assert.Equal("EMPLOYER", checkRecord.Payee); - Assert.Equal("Income Feb/2022", checkRecord.Memo); - Assert.Equal(new decimal(43), checkRecord.Amount); - - // Check Record 4 - checkRecord = viewModel.ParsedRecords[3].Result; - Assert.Equal(new DateTime(2022, 02, 17), checkRecord.TransactionDate); - Assert.Equal("The Webshop.com", checkRecord.Payee); - Assert.Equal("Billing", checkRecord.Memo); - Assert.Equal(new decimal(-6.34), checkRecord.Amount); - } - finally - { - Cleanup(); - } - } - -} diff --git a/OpenBudgeteer.Core.Test/xunit.runner.json b/OpenBudgeteer.Core.Test/xunit.runner.json new file mode 100644 index 0000000..369786b --- /dev/null +++ b/OpenBudgeteer.Core.Test/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false +} \ No newline at end of file diff --git a/OpenBudgeteer.Core/OpenBudgeteer.Core.csproj b/OpenBudgeteer.Core/OpenBudgeteer.Core.csproj index c05b776..0052c2c 100644 --- a/OpenBudgeteer.Core/OpenBudgeteer.Core.csproj +++ b/OpenBudgeteer.Core/OpenBudgeteer.Core.csproj @@ -6,7 +6,6 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/OpenBudgeteer.sln b/OpenBudgeteer.sln index cc63ade..6e5ed8b 100644 --- a/OpenBudgeteer.sln +++ b/OpenBudgeteer.sln @@ -20,8 +20,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution SECURITY.md = SECURITY.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBudgeteer.Core.Data.Sqlite.Migrations", "OpenBudgeteer.Core.Data.Sqlite.Migrations\OpenBudgeteer.Core.Data.Sqlite.Migrations.csproj", "{7D6ED82D-BAE4-4BC4-AEA9-22801037C6C3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBudgeteer.Core.Data.MySql.Migrations", "OpenBudgeteer.Core.Data.MySql.Migrations\OpenBudgeteer.Core.Data.MySql.Migrations.csproj", "{3455E778-BED7-433F-A100-0874FF0D15C7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBudgeteer.Core.Data", "OpenBudgeteer.Core.Data\OpenBudgeteer.Core.Data.csproj", "{DEB5B758-7AA9-45C6-96C5-371DA35F9E5C}" @@ -56,10 +54,6 @@ Global {2AFE22D2-18FA-4326-B011-B6E3D6365E6B}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AFE22D2-18FA-4326-B011-B6E3D6365E6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AFE22D2-18FA-4326-B011-B6E3D6365E6B}.Release|Any CPU.Build.0 = Release|Any CPU - {7D6ED82D-BAE4-4BC4-AEA9-22801037C6C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7D6ED82D-BAE4-4BC4-AEA9-22801037C6C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7D6ED82D-BAE4-4BC4-AEA9-22801037C6C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7D6ED82D-BAE4-4BC4-AEA9-22801037C6C3}.Release|Any CPU.Build.0 = Release|Any CPU {3455E778-BED7-433F-A100-0874FF0D15C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3455E778-BED7-433F-A100-0874FF0D15C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {3455E778-BED7-433F-A100-0874FF0D15C7}.Release|Any CPU.ActiveCfg = Release|Any CPU