From fc4d297bc947a7f195fe58d22b160c302bde5e88 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:32:23 +0200 Subject: [PATCH 01/23] Voeg nieuwe projecten toe voor Core, Application, Infrastructure en Application.Tests; update projectreferenties en afhankelijkheden naar .NET 8.0 en bijgewerkte versies van pakketten. --- ...nments.EPDConsole.Application.Tests.csproj | 30 +++++++++++++++++++ ....Assignments.EPDConsole.Application.csproj | 19 ++++++++++++ ...hipsoft.Assignments.EPDConsole.Core.csproj | 13 ++++++++ ...signments.EPDConsole.Infrastructure.csproj | 18 +++++++++++ Chipsoft.Assignments.EPDConsole.sln | 24 +++++++++++++++ .../Chipsoft.Assignments.EPDConsole.csproj | 13 ++++++-- 6 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Chipsoft.Assignments.EPDConsole.Application.Tests.csproj create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Chipsoft.Assignments.EPDConsole.Application.csproj create mode 100644 Chipsoft.Assignments.EPDConsole.Core/Chipsoft.Assignments.EPDConsole.Core.csproj create mode 100644 Chipsoft.Assignments.EPDConsole.Infrastructure/Chipsoft.Assignments.EPDConsole.Infrastructure.csproj diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Chipsoft.Assignments.EPDConsole.Application.Tests.csproj b/Chipsoft.Assignments.EPDConsole.Application.Tests/Chipsoft.Assignments.EPDConsole.Application.Tests.csproj new file mode 100644 index 0000000..545fd2e --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Chipsoft.Assignments.EPDConsole.Application.Tests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/Chipsoft.Assignments.EPDConsole.Application/Chipsoft.Assignments.EPDConsole.Application.csproj b/Chipsoft.Assignments.EPDConsole.Application/Chipsoft.Assignments.EPDConsole.Application.csproj new file mode 100644 index 0000000..a08d803 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Chipsoft.Assignments.EPDConsole.Application.csproj @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + net8.0 + enable + enable + + + diff --git a/Chipsoft.Assignments.EPDConsole.Core/Chipsoft.Assignments.EPDConsole.Core.csproj b/Chipsoft.Assignments.EPDConsole.Core/Chipsoft.Assignments.EPDConsole.Core.csproj new file mode 100644 index 0000000..6603dda --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Core/Chipsoft.Assignments.EPDConsole.Core.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/Chipsoft.Assignments.EPDConsole.Infrastructure/Chipsoft.Assignments.EPDConsole.Infrastructure.csproj b/Chipsoft.Assignments.EPDConsole.Infrastructure/Chipsoft.Assignments.EPDConsole.Infrastructure.csproj new file mode 100644 index 0000000..8a3d95f --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Infrastructure/Chipsoft.Assignments.EPDConsole.Infrastructure.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/Chipsoft.Assignments.EPDConsole.sln b/Chipsoft.Assignments.EPDConsole.sln index 01aeb4e..ee4e438 100644 --- a/Chipsoft.Assignments.EPDConsole.sln +++ b/Chipsoft.Assignments.EPDConsole.sln @@ -5,6 +5,14 @@ VisualStudioVersion = 17.3.32819.101 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chipsoft.Assignments.EPDConsole", "Chipsoft.Assignments.EPDConsole\Chipsoft.Assignments.EPDConsole.csproj", "{C3E36DFE-5F45-40C4-BCF8-CEACF7987B54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chipsoft.Assignments.EPDConsole.Core", "Chipsoft.Assignments.EPDConsole.Core\Chipsoft.Assignments.EPDConsole.Core.csproj", "{13E9F04A-65F5-4805-A725-D449E249FDF6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chipsoft.Assignments.EPDConsole.Application", "Chipsoft.Assignments.EPDConsole.Application\Chipsoft.Assignments.EPDConsole.Application.csproj", "{2DE32729-1EF3-402A-816E-041CC4D4CB3C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chipsoft.Assignments.EPDConsole.Infrastructure", "Chipsoft.Assignments.EPDConsole.Infrastructure\Chipsoft.Assignments.EPDConsole.Infrastructure.csproj", "{CF90E1EF-00EA-48BD-8ED7-531A91BDD45F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chipsoft.Assignments.EPDConsole.Application.Tests", "Chipsoft.Assignments.EPDConsole.Application.Tests\Chipsoft.Assignments.EPDConsole.Application.Tests.csproj", "{04FF203F-12B0-40F2-BF3C-B83EA01F140B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +23,22 @@ Global {C3E36DFE-5F45-40C4-BCF8-CEACF7987B54}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3E36DFE-5F45-40C4-BCF8-CEACF7987B54}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3E36DFE-5F45-40C4-BCF8-CEACF7987B54}.Release|Any CPU.Build.0 = Release|Any CPU + {13E9F04A-65F5-4805-A725-D449E249FDF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13E9F04A-65F5-4805-A725-D449E249FDF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13E9F04A-65F5-4805-A725-D449E249FDF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13E9F04A-65F5-4805-A725-D449E249FDF6}.Release|Any CPU.Build.0 = Release|Any CPU + {2DE32729-1EF3-402A-816E-041CC4D4CB3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DE32729-1EF3-402A-816E-041CC4D4CB3C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DE32729-1EF3-402A-816E-041CC4D4CB3C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DE32729-1EF3-402A-816E-041CC4D4CB3C}.Release|Any CPU.Build.0 = Release|Any CPU + {CF90E1EF-00EA-48BD-8ED7-531A91BDD45F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF90E1EF-00EA-48BD-8ED7-531A91BDD45F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF90E1EF-00EA-48BD-8ED7-531A91BDD45F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF90E1EF-00EA-48BD-8ED7-531A91BDD45F}.Release|Any CPU.Build.0 = Release|Any CPU + {04FF203F-12B0-40F2-BF3C-B83EA01F140B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04FF203F-12B0-40F2-BF3C-B83EA01F140B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04FF203F-12B0-40F2-BF3C-B83EA01F140B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04FF203F-12B0-40F2-BF3C-B83EA01F140B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Chipsoft.Assignments.EPDConsole/Chipsoft.Assignments.EPDConsole.csproj b/Chipsoft.Assignments.EPDConsole/Chipsoft.Assignments.EPDConsole.csproj index c7f7512..f348df4 100644 --- a/Chipsoft.Assignments.EPDConsole/Chipsoft.Assignments.EPDConsole.csproj +++ b/Chipsoft.Assignments.EPDConsole/Chipsoft.Assignments.EPDConsole.csproj @@ -2,13 +2,18 @@ Exe - net6.0 + net8.0 enable enable - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + @@ -17,4 +22,8 @@ + + + + From 4da1ed9efd5e47737835900de0724323217991f9 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:32:47 +0200 Subject: [PATCH 02/23] Verwijder de EPDDbContext-klasse en bijbehorende configuratie voor de database. --- Chipsoft.Assignments.EPDConsole/EPDDbContext.cs | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 Chipsoft.Assignments.EPDConsole/EPDDbContext.cs diff --git a/Chipsoft.Assignments.EPDConsole/EPDDbContext.cs b/Chipsoft.Assignments.EPDConsole/EPDDbContext.cs deleted file mode 100644 index ec44f0c..0000000 --- a/Chipsoft.Assignments.EPDConsole/EPDDbContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Chipsoft.Assignments.EPDConsole -{ - public class EPDDbContext : DbContext - { - // The following configures EF to create a Sqlite database file in the - protected override void OnConfiguring(DbContextOptionsBuilder options) - => options.UseSqlite($"Data Source=epd.db"); - public DbSet Patients { get; set; } - } -} From 3716dbfdcab831d6d6212fa85d2e75d02d30fddc Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:33:13 +0200 Subject: [PATCH 03/23] =?UTF-8?q?Herstructureer=20de=20hoofdprogramma-logi?= =?UTF-8?q?ca=20door=20gebruik=20te=20maken=20van=20Dependency=20Injection?= =?UTF-8?q?=20en=20MediatR;=20voeg=20functionaliteit=20toe=20voor=20het=20?= =?UTF-8?q?toevoegen,=20verwijderen=20en=20weergeven=20van=20pati=C3=ABnte?= =?UTF-8?q?n=20en=20artsen,=20evenals=20afsprakenbeheer.=20Implementeer=20?= =?UTF-8?q?validatie=20en=20foutafhandeling=20voor=20gebruikersinvoer.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Chipsoft.Assignments.EPDConsole/Program.cs | 500 ++++++++++++++++++--- 1 file changed, 446 insertions(+), 54 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole/Program.cs b/Chipsoft.Assignments.EPDConsole/Program.cs index 0f69d9d..79e0b51 100644 --- a/Chipsoft.Assignments.EPDConsole/Program.cs +++ b/Chipsoft.Assignments.EPDConsole/Program.cs @@ -1,99 +1,491 @@ -namespace Chipsoft.Assignments.EPDConsole +using Chipsoft.Assignments.EPDConsole.Application.Common.Behaviors; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Chipsoft.Assignments.EPDConsole.Infrastructure.Persistence; +using FluentValidation; +using MediatR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using ValidationException = Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions.ValidationException; + +namespace Chipsoft.Assignments.EPDConsole { public class Program { - //Don't create EF migrations, use the reset db option - //This deletes and recreates the db, this makes sure all tables exist + private static IServiceProvider _serviceProvider; + private static IMediator _mediator; - private static void AddPatient() + static async Task Main(string[] args) { - //Do action - //return to show menu again. + RegisterServices(); + _mediator = _serviceProvider.GetRequiredService(); + + await ShowMenu(); + + DisposeServices(); } - private static void ShowAppointment() + private static void RegisterServices() { + var services = new ServiceCollection(); + var applicationAssembly = typeof(Application.Appointments.Queries.GetAppointmentsListQuery).Assembly; + + services.AddDbContext(options => + options.UseSqlite("Data Source=epd.db")); + + services.AddScoped(provider => provider.GetService()); + + services.AddValidatorsFromAssembly(applicationAssembly); + services.AddMediatR(cfg => { + cfg.RegisterServicesFromAssembly(applicationAssembly); + cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); + }); + + _serviceProvider = services.BuildServiceProvider(); } - private static void AddAppointment() + private static void DisposeServices() { + if (_serviceProvider == null) + { + return; + } + if (_serviceProvider is IDisposable) + { + ((IDisposable)_serviceProvider).Dispose(); + } } - private static void DeletePhysician() + public static async Task ShowMenu() { + bool continueRunning = true; + while (continueRunning) + { + Console.Clear(); + foreach (var line in File.ReadAllLines("logo.txt")) + { + Console.WriteLine(line); + } + Console.WriteLine(""); + Console.WriteLine("1 - Patient toevoegen"); + Console.WriteLine("2 - Patienten verwijderen"); + Console.WriteLine("3 - Arts toevoegen"); + Console.WriteLine("4 - Arts verwijderen"); + Console.WriteLine("5 - Afspraak toevoegen"); + Console.WriteLine("6 - Afspraken inzien"); + Console.WriteLine("7 - Sluiten"); + Console.WriteLine("8 - Reset db"); + + if (int.TryParse(Console.ReadLine(), out int option)) + { + switch (option) + { + case 1: + await AddPatient(); + break; + case 2: + await DeletePatient(); + break; + case 3: + await AddPhysician(); + break; + case 4: + await DeletePhysician(); + break; + case 5: + await AddAppointment(); + break; + case 6: + await ShowAppointments(); + break; + case 7: + continueRunning = false; + break; + case 8: + ResetDatabase(); + break; + } + } + } } - private static void AddPhysician() + private static async Task AddPatient() { - } + var command = new Application.Patients.Commands.AddPatientCommand(); - private static void DeletePatient() + Console.WriteLine("--- Nieuwe patiënt toevoegen ---"); + Console.Write("Voornaam: "); + command.FirstName = Console.ReadLine(); + Console.Write("Achternaam: "); + command.LastName = Console.ReadLine(); + Console.Write("BSN: "); + command.BSN = Console.ReadLine(); + Console.Write("Adres: "); + command.Address = Console.ReadLine(); + Console.Write("Telefoonnummer: "); + command.PhoneNumber = Console.ReadLine(); + Console.Write("E-mail: "); + command.Email = Console.ReadLine(); + + while (true) + { + Console.Write("Geboortedatum (dd-mm-jjjj): "); + if (DateTime.TryParse(Console.ReadLine(), out DateTime dob)) + { + command.DateOfBirth = dob; + break; + } + Console.WriteLine("Ongeldige datum, probeer opnieuw."); + } + + try + { + var patientId = await _mediator.Send(command); + Console.WriteLine($"Patiënt succesvol toegevoegd met ID: {patientId}."); + } + catch (ValidationException ex) + { + Console.WriteLine("\nValidatie mislukt:"); + foreach (var error in ex.Errors) + { + Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + } + + WaitForKeyPress(); + } + + private static async Task DeletePatient() { + Console.WriteLine("--- Patiënt verwijderen ---"); + + var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); + if (!patients.Any()) + { + Console.WriteLine("Er zijn geen patiënten om te verwijderen."); + WaitForKeyPress(); + return; + } + + Console.WriteLine("Huidige patiënten:"); + foreach (var p in patients) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}, BSN: {p.BSN}"); + } + + Console.Write("\nVoer het ID in van de patiënt die u wilt verwijderen: "); + if (int.TryParse(Console.ReadLine(), out int id)) + { + try + { + var command = new Application.Patients.Commands.DeletePatientCommand { Id = id }; + await _mediator.Send(command); + Console.WriteLine("Patiënt succesvol verwijderd."); + } + catch (Exception ex) + { + Console.WriteLine($"Fout bij verwijderen: {ex.Message}"); + } + } + else + { + Console.WriteLine("Ongeldige invoer."); + } + WaitForKeyPress(); } + private static async Task AddPhysician() + { + Console.WriteLine("--- Nieuwe arts toevoegen ---"); + Console.Write("Voornaam: "); + var firstName = Console.ReadLine(); - #region FreeCodeForAssignment - static void Main(string[] args) + Console.Write("Achternaam: "); + var lastName = Console.ReadLine(); + + try + { + var command = new Application.Physicians.Commands.AddPhysicianCommand + { + FirstName = firstName, + LastName = lastName + }; + var physicianId = await _mediator.Send(command); + Console.WriteLine($"Arts succesvol toegevoegd met ID: {physicianId}."); + } + catch (ValidationException ex) + { + Console.WriteLine("\nValidatie mislukt:"); + foreach (var error in ex.Errors) + { + Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + } + + WaitForKeyPress(); + } + + private static async Task DeletePhysician() { - while (ShowMenu()) + Console.WriteLine("--- Arts verwijderen ---"); + + var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); + if (!physicians.Any()) + { + Console.WriteLine("Er zijn geen artsen om te verwijderen."); + WaitForKeyPress(); + return; + } + + Console.WriteLine("Huidige artsen:"); + foreach (var p in physicians) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + + Console.Write("\nVoer het ID in van de arts die u wilt verwijderen: "); + if (int.TryParse(Console.ReadLine(), out int id)) { - //Continue + try + { + var command = new Application.Physicians.Commands.DeletePhysicianCommand { Id = id }; + await _mediator.Send(command); + Console.WriteLine("Arts succesvol verwijderd."); + } + catch (Exception ex) + { + Console.WriteLine($"Fout bij verwijderen: {ex.Message}"); + } } + else + { + Console.WriteLine("Ongeldige invoer."); + } + WaitForKeyPress(); } - public static bool ShowMenu() + private static async Task AddAppointment() { - Console.Clear(); - foreach (var line in File.ReadAllLines("logo.txt")) + Console.WriteLine("--- Nieuwe afspraak toevoegen ---"); + + var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); + if (!patients.Any()) { - Console.WriteLine(line); + Console.WriteLine("Er zijn geen patiënten beschikbaar. Voeg eerst een patiënt toe."); + WaitForKeyPress(); + return; } - Console.WriteLine(""); - Console.WriteLine("1 - Patient toevoegen"); - Console.WriteLine("2 - Patienten verwijderen"); - Console.WriteLine("3 - Arts toevoegen"); - Console.WriteLine("4 - Arts verwijderen"); - Console.WriteLine("5 - Afspraak toevoegen"); - Console.WriteLine("6 - Afspraken inzien"); - Console.WriteLine("7 - Sluiten"); - Console.WriteLine("8 - Reset db"); + + var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); + if (!physicians.Any()) + { + Console.WriteLine("Er zijn geen artsen beschikbaar. Voeg eerst een arts toe."); + WaitForKeyPress(); + return; + } + + Console.WriteLine("\nBeschikbare Patiënten:"); + foreach (var p in patients) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + Console.Write("Kies een patiënt ID: "); + if (!int.TryParse(Console.ReadLine(), out int patientId) || !patients.Any(p => p.Id == patientId)) + { + Console.WriteLine("Ongeldig patiënt ID."); + WaitForKeyPress(); + return; + } + + Console.WriteLine("\nBeschikbare Artsen:"); + foreach (var p in physicians) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + Console.Write("Kies een arts ID: "); + if (!int.TryParse(Console.ReadLine(), out int physicianId) || !physicians.Any(p => p.Id == physicianId)) + { + Console.WriteLine("Ongeldig arts ID."); + WaitForKeyPress(); + return; + } + + DateTime appointmentDateTime; + while (true) + { + Console.Write("Voer de datum en tijd in (dd-mm-jjjj uu:mm): "); + if (DateTime.TryParseExact(Console.ReadLine(), "dd-MM-yyyy HH:mm", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out appointmentDateTime)) + { + break; + } + Console.WriteLine("Ongeldige datum/tijd, probeer opnieuw."); + } + + try + { + var command = new Application.Appointments.Commands.AddAppointmentCommand + { + PatientId = patientId, + PhysicianId = physicianId, + AppointmentDateTime = appointmentDateTime + }; + + var appointmentId = await _mediator.Send(command); + Console.WriteLine($"Afspraak succesvol toegevoegd met ID: {appointmentId}."); + } + catch (ValidationException ex) + { + Console.WriteLine("\nValidatie mislukt:"); + foreach (var error in ex.Errors) + { + Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + } + WaitForKeyPress(); + } + + private static async Task ShowAppointments() + { + Console.Clear(); + Console.WriteLine("--- Afspraken Inzien ---"); + Console.WriteLine("1 - Toon alle afspraken"); + Console.WriteLine("2 - Filter op patiënt"); + Console.WriteLine("3 - Filter op arts"); + Console.Write("Kies een optie: "); if (int.TryParse(Console.ReadLine(), out int option)) { switch (option) { case 1: - AddPatient(); - return true; + var allAppointments = await _mediator.Send(new Application.Appointments.Queries.GetAppointmentsListQuery()); + await DisplayAppointments(allAppointments); + break; case 2: - DeletePatient(); - return true; + await ShowAppointmentsByPatient(); + break; case 3: - AddPhysician(); - return true; - case 4: - DeletePhysician(); - return true; - case 5: - AddAppointment(); - return true; - case 6: - ShowAppointment(); - return true; - case 7: - return false; - case 8: - EPDDbContext dbContext = new EPDDbContext(); - dbContext.Database.EnsureDeleted(); - dbContext.Database.EnsureCreated(); - return true; + await ShowAppointmentsByPhysician(); + break; default: - return true; + Console.WriteLine("Ongeldige optie."); + break; } } - return true; + else + { + Console.WriteLine("Ongeldige invoer."); + } + + WaitForKeyPress(); } - #endregion + private static async Task ShowAppointmentsByPatient() + { + var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); + if (!patients.Any()) + { + Console.WriteLine("\nEr zijn geen patiënten om op te filteren."); + return; + } + + Console.WriteLine("\nKies een patiënt:"); + foreach (var p in patients) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + + Console.Write("Voer patiënt ID in: "); + if (int.TryParse(Console.ReadLine(), out int patientId) && patients.Any(p => p.Id == patientId)) + { + var query = new Application.Appointments.Queries.GetAppointmentsByPatientQuery { PatientId = patientId }; + var appointments = await _mediator.Send(query); + await DisplayAppointments(appointments); + } + else + { + Console.WriteLine("Ongeldig patiënt ID."); + } + } + + private static async Task ShowAppointmentsByPhysician() + { + var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); + if (!physicians.Any()) + { + Console.WriteLine("\nEr zijn geen artsen om op te filteren."); + return; + } + + Console.WriteLine("\nKies een arts:"); + foreach (var p in physicians) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + + Console.Write("Voer arts ID in: "); + if (int.TryParse(Console.ReadLine(), out int physicianId) && physicians.Any(p => p.Id == physicianId)) + { + var query = new Application.Appointments.Queries.GetAppointmentsByPhysicianQuery { PhysicianId = physicianId }; + var appointments = await _mediator.Send(query); + await DisplayAppointments(appointments); + } + else + { + Console.WriteLine("Ongeldig arts ID."); + } + } + + private static Task DisplayAppointments(System.Collections.Generic.List appointments) + { + Console.WriteLine("\n--- Overzicht afspraken ---"); + if (!appointments.Any()) + { + Console.WriteLine("Er zijn geen afspraken gevonden voor deze selectie."); + } + else + { + foreach (var app in appointments) + { + Console.WriteLine($"Datum/Tijd: {app.AppointmentDateTime:dd-MM-yyyy HH:mm}"); + Console.WriteLine($" Patiënt: {app.PatientName}"); + Console.WriteLine($" Arts: {app.PhysicianName}"); + Console.WriteLine(new string('-', 20)); + } + } + return Task.CompletedTask; + } + + private static void ResetDatabase() + { + using var scope = _serviceProvider.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Database.EnsureDeleted(); + dbContext.Database.EnsureCreated(); + Console.WriteLine("Database is gereset."); + WaitForKeyPress(); + } + + private static void WaitForKeyPress() + { + Console.WriteLine("\nDruk op een toets om terug te keren naar het menu..."); + Console.ReadKey(); + } } } \ No newline at end of file From 325e79ffc7b24998b2ea9e673cc29c142e687d4d Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:33:45 +0200 Subject: [PATCH 04/23] =?UTF-8?q?Voeg=20de=20AddAppointmentCommand=20en=20?= =?UTF-8?q?bijbehorende=20validator=20toe=20voor=20het=20beheren=20van=20a?= =?UTF-8?q?fspraken;=20implementeer=20validatieregels=20voor=20toekomstige?= =?UTF-8?q?=20afspraken=20en=20beschikbaarheid=20van=20artsen=20en=20pati?= =?UTF-8?q?=C3=ABnten.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/AddAppointmentCommand.cs | 40 +++++++++++++++++ .../AddAppointmentCommandValidator.cs | 45 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs new file mode 100644 index 0000000..7d9f577 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs @@ -0,0 +1,40 @@ +using MediatR; +using System; +using System.Threading; +using System.Threading.Tasks; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; + +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands +{ + public class AddAppointmentCommand : IRequest + { + public int PatientId { get; set; } + public int PhysicianId { get; set; } + public DateTime AppointmentDateTime { get; set; } + } + + public class AddAppointmentCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + + public AddAppointmentCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(AddAppointmentCommand request, CancellationToken cancellationToken) + { + var appointment = new Appointment + { + PatientId = request.PatientId, + PhysicianId = request.PhysicianId, + AppointmentDateTime = request.AppointmentDateTime + }; + + _context.Appointments.Add(appointment); + await _context.SaveChangesAsync(cancellationToken); + return appointment.Id; + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs new file mode 100644 index 0000000..b71f70a --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs @@ -0,0 +1,45 @@ +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands +{ + public class AddAppointmentCommandValidator : AbstractValidator + { + private readonly IApplicationDbContext _context; + + public AddAppointmentCommandValidator(IApplicationDbContext context) + { + _context = context; + + RuleFor(v => v.AppointmentDateTime) + .GreaterThan(DateTime.Now).WithMessage("Een afspraak moet in de toekomst worden gepland."); + + RuleFor(v => v) + .MustAsync(BeAvailable).WithMessage("De arts of patiënt heeft op dit tijdstip al een andere afspraak."); + } + + private async Task BeAvailable(AddAppointmentCommand command, CancellationToken cancellationToken) + { + var appointmentTime = command.AppointmentDateTime; + var appointmentEndTime = appointmentTime.AddMinutes(30); // Assuming 30 minute slots + + var physicianHasConflict = await _context.Appointments + .AnyAsync(a => a.PhysicianId == command.PhysicianId && + appointmentTime < a.AppointmentDateTime.AddMinutes(30) && + appointmentEndTime > a.AppointmentDateTime, cancellationToken); + + if (physicianHasConflict) return false; + + var patientHasConflict = await _context.Appointments + .AnyAsync(a => a.PatientId == command.PatientId && + appointmentTime < a.AppointmentDateTime.AddMinutes(30) && + appointmentEndTime > a.AppointmentDateTime, cancellationToken); + + return !patientHasConflict; + } + } +} \ No newline at end of file From b313f78a4f91ff8732db5f63e2c884f3bdc05529 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:34:31 +0200 Subject: [PATCH 05/23] =?UTF-8?q?Voeg=20AppointmentDto=20toe=20voor=20het?= =?UTF-8?q?=20beheren=20van=20afspraakgegevens,=20inclusief=20datum,=20pat?= =?UTF-8?q?i=C3=ABntnaam=20en=20artsnaam.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Appointments/Dtos/AppointmentDto.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs new file mode 100644 index 0000000..89465c0 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs @@ -0,0 +1,9 @@ +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Dtos +{ + public class AppointmentDto + { + public DateTime AppointmentDateTime { get; set; } + public string PatientName { get; set; } = string.Empty; + public string PhysicianName { get; set; } = string.Empty; + } +} \ No newline at end of file From ea937c39dd5e10588bebcab5a2893638f5033dd3 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:34:59 +0200 Subject: [PATCH 06/23] =?UTF-8?q?Voeg=20query's=20toe=20voor=20het=20ophal?= =?UTF-8?q?en=20van=20afspraken=20op=20basis=20van=20pati=C3=ABnt-=20en=20?= =?UTF-8?q?arts-ID;=20implementeer=20de=20bijbehorende=20handlers=20voor?= =?UTF-8?q?=20het=20verwerken=20van=20verzoeken.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Queries/GetAppointmentsByPatientQuery.cs | 42 +++++++++++++++++++ .../GetAppointmentsByPhysicianQuery.cs | 42 +++++++++++++++++++ .../Queries/GetAppointmentsListQuery.cs | 40 ++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs new file mode 100644 index 0000000..df4f11f --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs @@ -0,0 +1,42 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Dtos; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries +{ + public class GetAppointmentsByPatientQuery : IRequest> + { + public int PatientId { get; set; } + } + + public class GetAppointmentsByPatientQueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + + public GetAppointmentsByPatientQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task> Handle(GetAppointmentsByPatientQuery request, CancellationToken cancellationToken) + { + return await _context.Appointments + .Where(a => a.PatientId == request.PatientId) + .Include(a => a.Patient) + .Include(a => a.Physician) + .OrderBy(a => a.AppointmentDateTime) + .Select(a => new AppointmentDto + { + AppointmentDateTime = a.AppointmentDateTime, + PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", + PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" + }) + .ToListAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs new file mode 100644 index 0000000..f3286ed --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs @@ -0,0 +1,42 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Dtos; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries +{ + public class GetAppointmentsByPhysicianQuery : IRequest> + { + public int PhysicianId { get; set; } + } + + public class GetAppointmentsByPhysicianQueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + + public GetAppointmentsByPhysicianQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task> Handle(GetAppointmentsByPhysicianQuery request, CancellationToken cancellationToken) + { + return await _context.Appointments + .Where(a => a.PhysicianId == request.PhysicianId) + .Include(a => a.Patient) + .Include(a => a.Physician) + .OrderBy(a => a.AppointmentDateTime) + .Select(a => new AppointmentDto + { + AppointmentDateTime = a.AppointmentDateTime, + PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", + PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" + }) + .ToListAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs new file mode 100644 index 0000000..df5d50d --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs @@ -0,0 +1,40 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Dtos; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries +{ + public class GetAppointmentsListQuery : IRequest> + { + } + + public class GetAppointmentsListQueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + + public GetAppointmentsListQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task> Handle(GetAppointmentsListQuery request, CancellationToken cancellationToken) + { + return await _context.Appointments + .Include(a => a.Patient) + .Include(a => a.Physician) + .OrderBy(a => a.AppointmentDateTime) + .Select(a => new AppointmentDto + { + AppointmentDateTime = a.AppointmentDateTime, + PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", + PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" + }) + .ToListAsync(cancellationToken); + } + } +} \ No newline at end of file From 0dd22da7d9a542c929636f3ad72ec6b80e76ab09 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:35:47 +0200 Subject: [PATCH 07/23] Voeg validatiegedrag en bijbehorende uitzondering toe voor het verwerken van verzoeken; implementeer validatie met FluentValidation en verbeter foutafhandeling bij validatiefouten. --- .../Common/Behaviors/ValidationBehavior.cs | 43 +++++++++++++++++++ .../Common/Exceptions/ValidationException.cs | 26 +++++++++++ 2 files changed, 69 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs b/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs new file mode 100644 index 0000000..54ae5f7 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs @@ -0,0 +1,43 @@ +using FluentValidation; +using MediatR; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ValidationException = Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions.ValidationException; + + +namespace Chipsoft.Assignments.EPDConsole.Application.Common.Behaviors +{ + public class ValidationBehavior : IPipelineBehavior + where TRequest : IRequest + { + private readonly IEnumerable> _validators; + + public ValidationBehavior(IEnumerable> validators) + { + _validators = validators; + } + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + if (_validators.Any()) + { + var context = new ValidationContext(request); + + var validationResults = await Task.WhenAll( + _validators.Select(v => + v.ValidateAsync(context, cancellationToken))); + + var failures = validationResults + .SelectMany(r => r.Errors) + .Where(f => f != null) + .ToList(); + + if (failures.Count != 0) + throw new ValidationException(failures); + } + return await next(); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs b/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs new file mode 100644 index 0000000..3f7a80c --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs @@ -0,0 +1,26 @@ +using FluentValidation.Results; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions +{ + public class ValidationException : Exception + { + public ValidationException() + : base("One or more validation failures have occurred.") + { + Errors = new Dictionary(); + } + + public ValidationException(IEnumerable failures) + : this() + { + Errors = failures + .GroupBy(e => e.PropertyName, e => e.ErrorMessage) + .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); + } + + public IDictionary Errors { get; } + } +} \ No newline at end of file From b084648827f7c31fea7a1a2fa37fa29c28b52eaf Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:36:02 +0200 Subject: [PATCH 08/23] =?UTF-8?q?Voeg=20AddPatientCommand=20en=20bijbehore?= =?UTF-8?q?nde=20validator=20toe=20voor=20het=20beheren=20van=20pati=C3=AB?= =?UTF-8?q?ntgegevens;=20implementeer=20validatieregels=20voor=20verplicht?= =?UTF-8?q?e=20velden=20en=20unieke=20BSN.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Patients/Commands/AddPatientCommand.cs | 48 ++++++++++++++++ .../Commands/AddPatientCommandValidator.cs | 55 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs new file mode 100644 index 0000000..81716db --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs @@ -0,0 +1,48 @@ +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using MediatR; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands +{ + public class AddPatientCommand : IRequest + { + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string BSN { get; set; } = string.Empty; + public string Address { get; set; } = string.Empty; + public string PhoneNumber { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public DateTime DateOfBirth { get; set; } + } + + public class AddPatientCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + + public AddPatientCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(AddPatientCommand request, CancellationToken cancellationToken) + { + var patient = new Patient + { + FirstName = request.FirstName, + LastName = request.LastName, + BSN = request.BSN, + Address = request.Address, + PhoneNumber = request.PhoneNumber, + Email = request.Email, + DateOfBirth = request.DateOfBirth + }; + + _context.Patients.Add(patient); + await _context.SaveChangesAsync(cancellationToken); + return patient.Id; + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs new file mode 100644 index 0000000..d13a778 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs @@ -0,0 +1,55 @@ +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using System; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands +{ + public class AddPatientCommandValidator : AbstractValidator + { + private readonly IApplicationDbContext _context; + + public AddPatientCommandValidator(IApplicationDbContext context) + { + _context = context; + + RuleFor(v => v.FirstName) + .NotEmpty().WithMessage("Voornaam is een verplicht veld.") + .MaximumLength(200); + + RuleFor(v => v.LastName) + .NotEmpty().WithMessage("Achternaam is een verplicht veld.") + .MaximumLength(200); + + RuleFor(v => v.BSN) + .NotEmpty().WithMessage("BSN is een verplicht veld.") + .Length(9).WithMessage("BSN moet 9 karakters lang zijn.") + .MustAsync(BeUniqueBsn).WithMessage("Een patiënt met dit BSN bestaat al."); + + RuleFor(v => v.PhoneNumber) + .Matches(new Regex(@"^[\d\s\(\)\+\-]+$")).WithMessage("Telefoonnummer mag alleen nummers en gebruikelijke tekens (+, -, (, )) bevatten."); + + RuleFor(v => v.Email) + .NotEmpty() + .EmailAddress(); + + RuleFor(v => v.DateOfBirth) + .NotEmpty() + .Must(BeAValidDate).WithMessage("Geboortedatum moet in het verleden liggen."); + } + + private bool BeAValidDate(DateTime date) + { + return date < DateTime.Now; + } + + private async Task BeUniqueBsn(string bsn, CancellationToken cancellationToken) + { + return await _context.Patients + .AllAsync(p => p.BSN != bsn, cancellationToken); + } + } +} \ No newline at end of file From ddda141c56d5f083a74e4febd9da3afbb013ce5d Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:36:33 +0200 Subject: [PATCH 09/23] =?UTF-8?q?Voeg=20DeletePatientCommand=20en=20Delete?= =?UTF-8?q?PhysicianCommand=20toe=20met=20bijbehorende=20handlers=20voor?= =?UTF-8?q?=20het=20verwijderen=20van=20pati=C3=ABnten=20en=20artsen;=20im?= =?UTF-8?q?plementeer=20foutafhandeling=20voor=20niet-bestaande=20entiteit?= =?UTF-8?q?en=20en=20actieve=20afspraken.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Patients/Commands/DeletePatientCommand.cs | 45 +++++++++++++++++++ .../Commands/DeletePhysicianCommand.cs | 45 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs new file mode 100644 index 0000000..b06e6b6 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs @@ -0,0 +1,45 @@ +using MediatR; +using System.Threading; +using System.Threading.Tasks; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Microsoft.EntityFrameworkCore; +using System; + +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands +{ + public class DeletePatientCommand : IRequest + { + public int Id { get; set; } + } + + public class DeletePatientCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + + public DeletePatientCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(DeletePatientCommand request, CancellationToken cancellationToken) + { + var entity = await _context.Patients + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (entity == null) + { + throw new Exception($"Patient with id {request.Id} not found"); + } + + var hasAppointments = await _context.Appointments.AnyAsync(a => a.PatientId == request.Id, cancellationToken); + if(hasAppointments) + { + throw new Exception("Cannot delete patient with active appointments."); + } + + _context.Patients.Remove(entity); + + await _context.SaveChangesAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs new file mode 100644 index 0000000..28bd2e7 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs @@ -0,0 +1,45 @@ +using MediatR; +using System.Threading; +using System.Threading.Tasks; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Microsoft.EntityFrameworkCore; +using System; + +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands +{ + public class DeletePhysicianCommand : IRequest + { + public int Id { get; set; } + } + + public class DeletePhysicianCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + + public DeletePhysicianCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(DeletePhysicianCommand request, CancellationToken cancellationToken) + { + var entity = await _context.Physicians + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (entity == null) + { + throw new Exception($"Physician with id {request.Id} not found"); + } + + var hasAppointments = await _context.Appointments.AnyAsync(a => a.PhysicianId == request.Id, cancellationToken); + if(hasAppointments) + { + throw new Exception("Cannot delete physician with active appointments."); + } + + _context.Physicians.Remove(entity); + + await _context.SaveChangesAsync(cancellationToken); + } + } +} \ No newline at end of file From 4897787fb461a03e37c8eb64389e56295f418c84 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:36:51 +0200 Subject: [PATCH 10/23] =?UTF-8?q?Voeg=20GetPatientsListQuery=20en=20AddPhy?= =?UTF-8?q?sicianCommand=20toe=20met=20bijbehorende=20handlers=20en=20vali?= =?UTF-8?q?datie;=20implementeer=20logica=20voor=20het=20ophalen=20van=20p?= =?UTF-8?q?ati=C3=ABnten=20en=20het=20toevoegen=20van=20artsen=20met=20val?= =?UTF-8?q?idatieregels=20voor=20verplichte=20velden=20en=20unieke=20namen?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Patients/Queries/GetPatientsListQuery.cs | 43 +++++++++++++++++++ .../Commands/AddPhysicianCommand.cs | 39 +++++++++++++++++ .../Commands/AddPhysicianCommandValidator.cs | 35 +++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs new file mode 100644 index 0000000..01d73ac --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs @@ -0,0 +1,43 @@ +using MediatR; +using System.Collections.Generic; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.EntityFrameworkCore; +using System.Linq; + +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Queries +{ + public class PatientDto + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public string BSN { get; set; } = string.Empty; + } + + public class GetPatientsListQuery : IRequest> + { + } + + public class GetPatientsListQueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + + public GetPatientsListQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task> Handle(GetPatientsListQuery request, CancellationToken cancellationToken) + { + return await _context.Patients + .Select(p => new PatientDto + { + Id = p.Id, + Name = $"{p.FirstName} {p.LastName}", + BSN = p.BSN + }) + .ToListAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs new file mode 100644 index 0000000..672fdf3 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs @@ -0,0 +1,39 @@ +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using MediatR; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands +{ + public class AddPhysicianCommand : IRequest + { + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + } + + public class AddPhysicianCommandHandler : IRequestHandler + { + private readonly IApplicationDbContext _context; + + public AddPhysicianCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(AddPhysicianCommand request, CancellationToken cancellationToken) + { + var physician = new Physician + { + FirstName = request.FirstName, + LastName = request.LastName + }; + + _context.Physicians.Add(physician); + + await _context.SaveChangesAsync(cancellationToken); + + return physician.Id; + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs new file mode 100644 index 0000000..8410138 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs @@ -0,0 +1,35 @@ +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using System.Threading; +using System.Threading.Tasks; + +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands +{ + public class AddPhysicianCommandValidator : AbstractValidator + { + private readonly IApplicationDbContext _context; + + public AddPhysicianCommandValidator(IApplicationDbContext context) + { + _context = context; + + RuleFor(v => v.FirstName) + .NotEmpty().WithMessage("Voornaam is een verplicht veld.") + .MaximumLength(200).WithMessage("Voornaam mag niet meer dan 200 karakters bevatten."); + + RuleFor(v => v.LastName) + .NotEmpty().WithMessage("Achternaam is een verplicht veld.") + .MaximumLength(200).WithMessage("Achternaam mag niet meer dan 200 karakters bevatten."); + + RuleFor(x => x) + .MustAsync(BeUniqueName).WithMessage("Een arts met deze naam bestaat al."); + } + + private async Task BeUniqueName(AddPhysicianCommand command, CancellationToken cancellationToken) + { + return await _context.Physicians + .AllAsync(p => p.FirstName != command.FirstName || p.LastName != command.LastName, cancellationToken); + } + } +} \ No newline at end of file From c6919a87ccf098cbe56709522f8ebc6c614d2625 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:37:03 +0200 Subject: [PATCH 11/23] Voeg GetPhysiciansListQuery en bijbehorende handler toe voor het ophalen van artsen; implementeer logica voor het retourneren van een lijst met artsen met hun ID en naam. --- .../Queries/GetPhysiciansListQuery.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs new file mode 100644 index 0000000..b76971c --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs @@ -0,0 +1,41 @@ +using MediatR; +using System.Collections.Generic; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.EntityFrameworkCore; +using System.Linq; + +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Queries +{ + public class PhysicianDto + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + } + + public class GetPhysiciansListQuery : IRequest> + { + } + + public class GetPhysiciansListQueryHandler : IRequestHandler> + { + private readonly IApplicationDbContext _context; + + public GetPhysiciansListQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task> Handle(GetPhysiciansListQuery request, CancellationToken cancellationToken) + { + return await _context.Physicians + .Select(p => new PhysicianDto + { + Id = p.Id, + Name = $"{p.FirstName} {p.LastName}" + }) + .ToListAsync(cancellationToken); + } + } +} \ No newline at end of file From 10cebec58adab30980cc56ff431b87c2a7112896 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:37:24 +0200 Subject: [PATCH 12/23] =?UTF-8?q?Voeg=20de=20entiteiten=20Appointment,=20P?= =?UTF-8?q?atient=20en=20Physician=20toe=20voor=20het=20beheren=20van=20af?= =?UTF-8?q?spraken,=20pati=C3=ABnten=20en=20artsen;=20implementeer=20de=20?= =?UTF-8?q?basisstructuur=20met=20relevante=20eigenschappen=20en=20relatie?= =?UTF-8?q?s.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entities/Appointment.cs | 16 ++++++++++++++++ .../Entities/Patient.cs | 18 ++++++++++++++++++ .../Entities/Physician.cs | 12 ++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs diff --git a/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs b/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs new file mode 100644 index 0000000..8a66382 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs @@ -0,0 +1,16 @@ +using System; + +namespace Chipsoft.Assignments.EPDConsole.Core.Entities +{ + public class Appointment + { + public int Id { get; set; } + public DateTime AppointmentDateTime { get; set; } + + public int PatientId { get; set; } + public Patient? Patient { get; set; } + + public int PhysicianId { get; set; } + public Physician? Physician { get; set; } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs b/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs new file mode 100644 index 0000000..681fa4c --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Chipsoft.Assignments.EPDConsole.Core.Entities +{ + public class Patient + { + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? BSN { get; set; } // Burger Service Nummer + public string? Address { get; set; } + public string? PhoneNumber { get; set; } + public string? Email { get; set; } + public DateTime DateOfBirth { get; set; } + public ICollection Appointments { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs b/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs new file mode 100644 index 0000000..cd8ad0d --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Chipsoft.Assignments.EPDConsole.Core.Entities +{ + public class Physician + { + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public ICollection Appointments { get; set; } = new List(); + } +} \ No newline at end of file From b78c4308ba0f9271ba90d22b7d5f65d567820326 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:37:41 +0200 Subject: [PATCH 13/23] Voeg IApplicationDbContext interface en EPDDbContext klasse toe voor database-interactie; implementeer DbSet-eigenschappen voor Patient, Physician en Appointment, en override SaveChangesAsync voor asynchrone opslag. --- .../Interfaces/IApplicationDbContext.cs | 15 +++++++++++++ .../Persistence/EPDDbContext.cs | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs diff --git a/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs b/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs new file mode 100644 index 0000000..9b44200 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs @@ -0,0 +1,15 @@ +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Microsoft.EntityFrameworkCore; + + +namespace Chipsoft.Assignments.EPDConsole.Core.Interfaces +{ + public interface IApplicationDbContext + { + DbSet Patients { get; set; } + DbSet Physicians { get; set; } + DbSet Appointments { get; set; } + + Task SaveChangesAsync(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs b/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs new file mode 100644 index 0000000..9e4af8b --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs @@ -0,0 +1,22 @@ +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace Chipsoft.Assignments.EPDConsole.Infrastructure.Persistence +{ + public class EPDDbContext : DbContext, IApplicationDbContext + { + public EPDDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet Patients { get; set; } + public DbSet Physicians { get; set; } + public DbSet Appointments { get; set; } + + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return await base.SaveChangesAsync(cancellationToken); + } + } +} \ No newline at end of file From 6c76a76f6fa49b54e5e0df8200704473a933dd19 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:38:07 +0200 Subject: [PATCH 14/23] =?UTF-8?q?Voeg=20unit=20tests=20toe=20voor=20het=20?= =?UTF-8?q?toevoegen,=20ophalen=20en=20verwijderen=20van=20pati=C3=ABnten?= =?UTF-8?q?=20en=20artsen;=20implementeer=20tests=20voor=20command=20handl?= =?UTF-8?q?ers=20en=20validatie=20om=20de=20functionaliteit=20en=20foutafh?= =?UTF-8?q?andeling=20te=20waarborgen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddAppointmentCommandHandlerTests.cs | 46 +++++++++ ...tAppointmentsByPatientQueryHandlerTests.cs | 55 +++++++++++ ...ppointmentsByPhysicianQueryHandlerTests.cs | 56 +++++++++++ .../GetAppointmentsListQueryHandlerTests.cs | 51 ++++++++++ .../AddAppointmentCommandValidatorTests.cs | 90 ++++++++++++++++++ .../Commands/AddPatientCommandHandlerTests.cs | 47 ++++++++++ .../DeletePatientCommandHandlerTests.cs | 93 ++++++++++++++++++ .../GetPatientsListQueryHandlerTests.cs | 49 ++++++++++ .../AddPatientCommandValidatorTests.cs | 66 +++++++++++++ .../AddPhysicianCommandHandlerTests.cs | 45 +++++++++ .../DeletePhysicianCommandHandlerTests.cs | 94 +++++++++++++++++++ .../GetPhysiciansListQueryHandlerTests.cs | 49 ++++++++++ .../AddPhysicianCommandValidatorTests.cs | 61 ++++++++++++ 13 files changed, 802 insertions(+) create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs new file mode 100644 index 0000000..bf5ab55 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs @@ -0,0 +1,46 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Moq; +using Moq.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Commands +{ + public class AddAppointmentCommandHandlerTests + { + private readonly Mock _contextMock; + private readonly AddAppointmentCommandHandler _handler; + + public AddAppointmentCommandHandlerTests() + { + _contextMock = new Mock(); + _handler = new AddAppointmentCommandHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Add_Appointment_And_Save_Changes() + { + // Arrange + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var command = new AddAppointmentCommand + { + PatientId = 1, + PhysicianId = 1, + AppointmentDateTime = DateTime.Now.AddHours(1) + }; + + // Act + await _handler.Handle(command, CancellationToken.None); + + // Assert + _contextMock.Verify(x => x.Appointments.Add(It.IsAny()), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs new file mode 100644 index 0000000..058eb95 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs @@ -0,0 +1,55 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentAssertions; +using Moq; +using Moq.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries +{ + public class GetAppointmentsByPatientQueryHandlerTests + { + private readonly Mock _contextMock; + private readonly GetAppointmentsByPatientQueryHandler _handler; + + public GetAppointmentsByPatientQueryHandlerTests() + { + _contextMock = new Mock(); + _handler = new GetAppointmentsByPatientQueryHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Return_Only_Appointments_For_Given_Patient() + { + // Arrange + var patient1 = new Patient { Id = 1, FirstName = "John", LastName = "Doe" }; + var patient2 = new Patient { Id = 2, FirstName = "Jane", LastName = "Doe" }; + var physician = new Physician { Id = 1, FirstName = "Dr.", LastName = "Smith" }; + + var appointments = new List + { + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient1, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(1) }, + new Appointment { PatientId = 2, PhysicianId = 1, Patient = patient2, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(2) }, + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient1, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(3) } + }; + + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var query = new GetAppointmentsByPatientQuery { PatientId = 1 }; + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result.All(a => a.PatientName == "John Doe").Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs new file mode 100644 index 0000000..37daf41 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs @@ -0,0 +1,56 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentAssertions; +using Moq; +using Moq.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries +{ + public class GetAppointmentsByPhysicianQueryHandlerTests + { + private readonly Mock _contextMock; + private readonly GetAppointmentsByPhysicianQueryHandler _handler; + + public GetAppointmentsByPhysicianQueryHandlerTests() + { + _contextMock = new Mock(); + _handler = new GetAppointmentsByPhysicianQueryHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Return_Only_Appointments_For_Given_Physician() + { + // Arrange + var patient = new Patient { Id = 1, FirstName = "John", LastName = "Doe" }; + var physician1 = new Physician { Id = 1, FirstName = "Dr.", LastName = "Smith" }; + var physician2 = new Physician { Id = 2, FirstName = "Dr.", LastName = "Who" }; + + + var appointments = new List + { + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient, Physician = physician1, AppointmentDateTime = DateTime.Now.AddDays(1) }, + new Appointment { PatientId = 1, PhysicianId = 2, Patient = patient, Physician = physician2, AppointmentDateTime = DateTime.Now.AddDays(2) }, + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient, Physician = physician1, AppointmentDateTime = DateTime.Now.AddDays(3) } + }; + + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var query = new GetAppointmentsByPhysicianQuery { PhysicianId = 1 }; + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result.All(a => a.PhysicianName == "Dr. Smith").Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs new file mode 100644 index 0000000..9f2fcba --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs @@ -0,0 +1,51 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentAssertions; +using Moq; +using Moq.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries +{ + public class GetAppointmentsListQueryHandlerTests + { + private readonly Mock _contextMock; + private readonly GetAppointmentsListQueryHandler _handler; + + public GetAppointmentsListQueryHandlerTests() + { + _contextMock = new Mock(); + _handler = new GetAppointmentsListQueryHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Return_All_Appointments_As_Dtos() + { + // Arrange + var patients = new List { new Patient { Id = 1, FirstName = "John", LastName = "Doe" } }; + var physicians = new List { new Physician { Id = 1, FirstName = "Jane", LastName = "Smith" } }; + var appointments = new List + { + new Appointment { Id = 1, PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1), Patient = patients[0], Physician = physicians[0] } + }; + + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var query = new GetAppointmentsListQuery(); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(1); + result[0].PatientName.Should().Be("John Doe"); + result[0].PhysicianName.Should().Be("Jane Smith"); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs new file mode 100644 index 0000000..fc03437 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs @@ -0,0 +1,90 @@ +using Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentValidation.TestHelper; +using Moq; +using Moq.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; +using FluentAssertions; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Validators +{ + public class AddAppointmentCommandValidatorTests + { + private readonly Mock _contextMock; + + public AddAppointmentCommandValidatorTests() + { + _contextMock = new Mock(); + } + + [Fact] + public async Task Should_Not_Have_Error_When_Appointment_Is_Valid() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldNotHaveAnyValidationErrors(); + } + + [Fact] + public async Task Should_Have_Error_When_Physician_Has_Conflict() + { + // Arrange + var appointmentTime = DateTime.Now.AddDays(1); + var existingAppointments = new List + { + new Appointment { PhysicianId = 1, AppointmentDateTime = appointmentTime } + }; + + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(existingAppointments); + + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = appointmentTime }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); + } + + [Fact] + public async Task Should_Have_Error_When_Patient_Has_Conflict() + { + // Arrange + var appointmentTime = DateTime.Now.AddDays(1); + var existingAppointments = new List + { + new Appointment { PatientId = 1, AppointmentDateTime = appointmentTime } + }; + + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(existingAppointments); + + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = appointmentTime }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs new file mode 100644 index 0000000..11ccae4 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs @@ -0,0 +1,47 @@ +using Chipsoft.Assignments.EPDConsole.Application.Patients.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands +{ + public class AddPatientCommandHandlerTests + { + private readonly Mock _contextMock; + private readonly AddPatientCommandHandler _handler; + + public AddPatientCommandHandlerTests() + { + _contextMock = new Mock(); + _handler = new AddPatientCommandHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Add_Patient_And_Save_Changes() + { + // Arrange + var patients = new List(); + _contextMock.Setup(c => c.Patients).ReturnsDbSet(patients); + + var command = new AddPatientCommand + { + FirstName = "John", + LastName = "Doe", + BSN = "123456789", + Email = "john.doe@test.com" + }; + + // Act + await _handler.Handle(command, CancellationToken.None); + + // Assert + _contextMock.Verify(x => x.Patients.Add(It.Is(p => p.FirstName == "John")), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs new file mode 100644 index 0000000..0c2ea51 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs @@ -0,0 +1,93 @@ +using Chipsoft.Assignments.EPDConsole.Application.Patients.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using FluentAssertions; +using System; +using Microsoft.EntityFrameworkCore; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands +{ + public class DeletePatientCommandHandlerTests + { + private readonly Mock _contextMock; + + public DeletePatientCommandHandlerTests() + { + _contextMock = new Mock(); + } + + [Fact] + public async Task Handle_Should_Remove_Patient_When_Found() + { + // Arrange + var patient = new Patient { Id = 1, FirstName = "Test", LastName = "Patient" }; + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(patient); + + _contextMock.Setup(c => c.Patients).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePatientCommandHandler(_contextMock.Object); + var command = new DeletePatientCommand { Id = 1 }; + + // Act + await handler.Handle(command, CancellationToken.None); + + // Assert + mockDbSet.Verify(x => x.Remove(patient), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Patient_Not_Found() + { + // Arrange + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 99 }, It.IsAny())) + .ReturnsAsync((Patient)null); + + _contextMock.Setup(c => c.Patients).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePatientCommandHandler(_contextMock.Object); + var command = new DeletePatientCommand { Id = 99 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Patient with id 99 not found"); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Patient_Has_Appointments() + { + // Arrange + var patient = new Patient { Id = 1, FirstName = "Test", LastName = "Patient" }; + var appointments = new List { new Appointment { PatientId = 1 } }; + + var mockPatientDbSet = new Mock>(); + mockPatientDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(patient); + + _contextMock.Setup(c => c.Patients).Returns(mockPatientDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var handler = new DeletePatientCommandHandler(_contextMock.Object); + var command = new DeletePatientCommand { Id = 1 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Cannot delete patient with active appointments."); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs new file mode 100644 index 0000000..9bf865d --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs @@ -0,0 +1,49 @@ +using Chipsoft.Assignments.EPDConsole.Application.Patients.Queries; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentAssertions; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Queries +{ + public class GetPatientsListQueryHandlerTests + { + private readonly Mock _contextMock; + private readonly GetPatientsListQueryHandler _handler; + + public GetPatientsListQueryHandlerTests() + { + _contextMock = new Mock(); + _handler = new GetPatientsListQueryHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Return_All_Patients_As_Dtos() + { + // Arrange + var patients = new List + { + new Patient { Id = 1, FirstName = "John", LastName = "Doe", BSN = "123456789" }, + new Patient { Id = 2, FirstName = "Jane", LastName = "Smith", BSN = "987654321" } + }; + + _contextMock.Setup(c => c.Patients).ReturnsDbSet(patients); + + var query = new GetPatientsListQuery(); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result[0].Name.Should().Be("John Doe"); + result[1].BSN.Should().Be("987654321"); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs new file mode 100644 index 0000000..9f88683 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs @@ -0,0 +1,66 @@ +using Chipsoft.Assignments.EPDConsole.Application.Patients.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentValidation.TestHelper; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Validators +{ + public class AddPatientCommandValidatorTests + { + private readonly Mock _contextMock; + private readonly AddPatientCommandValidator _validator; + + public AddPatientCommandValidatorTests() + { + _contextMock = new Mock(); + // Setup the Patients DbSet mock + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List()); + _validator = new AddPatientCommandValidator(_contextMock.Object); + } + + [Fact] + public async Task Should_Have_Error_When_FirstName_Is_Empty() + { + var command = new AddPatientCommand { FirstName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.FirstName); + } + + [Fact] + public async Task Should_Have_Error_When_LastName_Is_Empty() + { + var command = new AddPatientCommand { LastName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.LastName); + } + + [Fact] + public async Task Should_Have_Error_When_BSN_Is_Invalid() + { + var command = new AddPatientCommand { BSN = "123" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.BSN); + } + + [Fact] + public async Task Should_Not_Have_Error_When_Command_Is_Valid() + { + var command = new AddPatientCommand + { + FirstName = "John", + LastName = "Doe", + BSN = "123456789", + Email = "john.doe@test.com", + DateOfBirth = new System.DateTime(1990, 1, 1), + PhoneNumber = "0612345678" + }; + var result = await _validator.TestValidateAsync(command); + result.ShouldNotHaveAnyValidationErrors(); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs new file mode 100644 index 0000000..672b6b8 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs @@ -0,0 +1,45 @@ +using Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Commands +{ + public class AddPhysicianCommandHandlerTests + { + private readonly Mock _contextMock; + private readonly AddPhysicianCommandHandler _handler; + + public AddPhysicianCommandHandlerTests() + { + _contextMock = new Mock(); + _handler = new AddPhysicianCommandHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Add_Physician_And_Save_Changes() + { + // Arrange + var physicians = new List(); + _contextMock.Setup(c => c.Physicians).ReturnsDbSet(physicians); + + var command = new AddPhysicianCommand + { + FirstName = "Test", + LastName = "Physician" + }; + + // Act + await _handler.Handle(command, CancellationToken.None); + + // Assert + _contextMock.Verify(x => x.Physicians.Add(It.IsAny()), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs new file mode 100644 index 0000000..3023959 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs @@ -0,0 +1,94 @@ +using Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using FluentAssertions; +using System; +using Microsoft.EntityFrameworkCore; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Commands +{ + public class DeletePhysicianCommandHandlerTests + { + private readonly Mock _contextMock; + + public DeletePhysicianCommandHandlerTests() + { + _contextMock = new Mock(); + } + + [Fact] + public async Task Handle_Should_Remove_Physician_When_Found() + { + // Arrange + var physician = new Physician { Id = 1, FirstName = "Test", LastName = "Physician" }; + var physicians = new List { physician }; + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(physician); + + _contextMock.Setup(c => c.Physicians).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePhysicianCommandHandler(_contextMock.Object); + var command = new DeletePhysicianCommand { Id = 1 }; + + // Act + await handler.Handle(command, CancellationToken.None); + + // Assert + mockDbSet.Verify(x => x.Remove(physician), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Physician_Not_Found() + { + // Arrange + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 99 }, It.IsAny())) + .ReturnsAsync((Physician)null); + + _contextMock.Setup(c => c.Physicians).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePhysicianCommandHandler(_contextMock.Object); + var command = new DeletePhysicianCommand { Id = 99 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Physician with id 99 not found"); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Physician_Has_Appointments() + { + // Arrange + var physician = new Physician { Id = 1, FirstName = "Test", LastName = "Physician" }; + var appointments = new List { new Appointment { PhysicianId = 1 } }; + + var mockPhysicianDbSet = new Mock>(); + mockPhysicianDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(physician); + + _contextMock.Setup(c => c.Physicians).Returns(mockPhysicianDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var handler = new DeletePhysicianCommandHandler(_contextMock.Object); + var command = new DeletePhysicianCommand { Id = 1 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Cannot delete physician with active appointments."); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs new file mode 100644 index 0000000..815bebd --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs @@ -0,0 +1,49 @@ +using Chipsoft.Assignments.EPDConsole.Application.Physicians.Queries; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentAssertions; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Queries +{ + public class GetPhysiciansListQueryHandlerTests + { + private readonly Mock _contextMock; + private readonly GetPhysiciansListQueryHandler _handler; + + public GetPhysiciansListQueryHandlerTests() + { + _contextMock = new Mock(); + _handler = new GetPhysiciansListQueryHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Return_All_Physicians_As_Dtos() + { + // Arrange + var physicians = new List + { + new Physician { Id = 1, FirstName = "Dr.", LastName = "Who" }, + new Physician { Id = 2, FirstName = "Dr.", LastName = "Strange" } + }; + + _contextMock.Setup(c => c.Physicians).ReturnsDbSet(physicians); + + var query = new GetPhysiciansListQuery(); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result[0].Name.Should().Be("Dr. Who"); + result[1].Name.Should().Be("Dr. Strange"); + } + } +} \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs new file mode 100644 index 0000000..05b592c --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs @@ -0,0 +1,61 @@ +using Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands; +using Chipsoft.Assignments.EPDConsole.Core.Entities; +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using FluentValidation.TestHelper; +using Moq; +using Moq.EntityFrameworkCore; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; +using FluentAssertions; + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Validators +{ + public class AddPhysicianCommandValidatorTests + { + private readonly Mock _contextMock; + private readonly AddPhysicianCommandValidator _validator; + + public AddPhysicianCommandValidatorTests() + { + _contextMock = new Mock(); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List()); + _validator = new AddPhysicianCommandValidator(_contextMock.Object); + } + + [Fact] + public async Task Should_Have_Error_When_FirstName_Is_Empty() + { + var command = new AddPhysicianCommand { FirstName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.FirstName); + } + + [Fact] + public async Task Should_Not_Have_Error_When_Name_Is_Unique() + { + var command = new AddPhysicianCommand { FirstName = "Test", LastName = "Physician" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldNotHaveAnyValidationErrors(); + } + + [Fact] + public async Task Should_Have_Error_When_Name_Is_Not_Unique() + { + // Arrange + var existingPhysicians = new List + { + new Physician { FirstName = "Test", LastName = "Physician" } + }; + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(existingPhysicians); + var validator = new AddPhysicianCommandValidator(_contextMock.Object); + var command = new AddPhysicianCommand { FirstName = "Test", LastName = "Physician" }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.Errors.Should().Contain(e => e.ErrorMessage == "Een arts met deze naam bestaat al."); + } + } +} \ No newline at end of file From b5274deaecd4425867022580f6a0294e58e28263 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:40:52 +0200 Subject: [PATCH 15/23] Herstructureer entiteiten Appointment, Patient en Physician door de namespace en klasse-definities te vereenvoudigen; pas de initialisatie van de Appointments-lijsten aan naar een lege array. --- .../Entities/Appointment.cs | 25 ++++++++--------- .../Entities/Patient.cs | 28 ++++++++----------- .../Entities/Physician.cs | 17 +++++------ .../Interfaces/IApplicationDbContext.cs | 19 ++++++------- 4 files changed, 39 insertions(+), 50 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs b/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs index 8a66382..b58d027 100644 --- a/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs +++ b/Chipsoft.Assignments.EPDConsole.Core/Entities/Appointment.cs @@ -1,16 +1,13 @@ -using System; +namespace Chipsoft.Assignments.EPDConsole.Core.Entities; -namespace Chipsoft.Assignments.EPDConsole.Core.Entities +public class Appointment { - public class Appointment - { - public int Id { get; set; } - public DateTime AppointmentDateTime { get; set; } - - public int PatientId { get; set; } - public Patient? Patient { get; set; } - - public int PhysicianId { get; set; } - public Physician? Physician { get; set; } - } -} \ No newline at end of file + public int Id { get; set; } + public DateTime AppointmentDateTime { get; set; } + + public int PatientId { get; set; } + public Patient? Patient { get; set; } + + public int PhysicianId { get; set; } + public Physician? Physician { get; set; } +} diff --git a/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs b/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs index 681fa4c..bb12c6e 100644 --- a/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs +++ b/Chipsoft.Assignments.EPDConsole.Core/Entities/Patient.cs @@ -1,18 +1,14 @@ -using System; -using System.Collections.Generic; +namespace Chipsoft.Assignments.EPDConsole.Core.Entities; -namespace Chipsoft.Assignments.EPDConsole.Core.Entities +public class Patient { - public class Patient - { - public int Id { get; set; } - public string? FirstName { get; set; } - public string? LastName { get; set; } - public string? BSN { get; set; } // Burger Service Nummer - public string? Address { get; set; } - public string? PhoneNumber { get; set; } - public string? Email { get; set; } - public DateTime DateOfBirth { get; set; } - public ICollection Appointments { get; set; } = new List(); - } -} \ No newline at end of file + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? BSN { get; set; } // Burger Service Nummer + public string? Address { get; set; } + public string? PhoneNumber { get; set; } + public string? Email { get; set; } + public DateTime DateOfBirth { get; set; } + public ICollection Appointments { get; set; } = []; +} diff --git a/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs b/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs index cd8ad0d..eb1d596 100644 --- a/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs +++ b/Chipsoft.Assignments.EPDConsole.Core/Entities/Physician.cs @@ -1,12 +1,9 @@ -using System.Collections.Generic; +namespace Chipsoft.Assignments.EPDConsole.Core.Entities; -namespace Chipsoft.Assignments.EPDConsole.Core.Entities +public class Physician { - public class Physician - { - public int Id { get; set; } - public string? FirstName { get; set; } - public string? LastName { get; set; } - public ICollection Appointments { get; set; } = new List(); - } -} \ No newline at end of file + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public ICollection Appointments { get; set; } = []; +} diff --git a/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs b/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs index 9b44200..1ddc11f 100644 --- a/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs +++ b/Chipsoft.Assignments.EPDConsole.Core/Interfaces/IApplicationDbContext.cs @@ -2,14 +2,13 @@ using Microsoft.EntityFrameworkCore; -namespace Chipsoft.Assignments.EPDConsole.Core.Interfaces +namespace Chipsoft.Assignments.EPDConsole.Core.Interfaces; + +public interface IApplicationDbContext { - public interface IApplicationDbContext - { - DbSet Patients { get; set; } - DbSet Physicians { get; set; } - DbSet Appointments { get; set; } - - Task SaveChangesAsync(CancellationToken cancellationToken); - } -} \ No newline at end of file + DbSet Patients { get; set; } + DbSet Physicians { get; set; } + DbSet Appointments { get; set; } + + Task SaveChangesAsync(CancellationToken cancellationToken); +} From 69ef4d4863f1a3f984fa3a11a9e109cde2f4b0e6 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:43:08 +0200 Subject: [PATCH 16/23] Verbeter de structuur van de EPDDbContext klasse door de namespace en klasse-definitie te vereenvoudigen; behoud de DbSet-eigenschappen en de override van SaveChangesAsync voor asynchrone opslag. --- .../Persistence/EPDDbContext.cs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs b/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs index 9e4af8b..b7b2650 100644 --- a/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs +++ b/Chipsoft.Assignments.EPDConsole.Infrastructure/Persistence/EPDDbContext.cs @@ -2,21 +2,16 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Microsoft.EntityFrameworkCore; -namespace Chipsoft.Assignments.EPDConsole.Infrastructure.Persistence -{ - public class EPDDbContext : DbContext, IApplicationDbContext - { - public EPDDbContext(DbContextOptions options) : base(options) - { - } +namespace Chipsoft.Assignments.EPDConsole.Infrastructure.Persistence; - public DbSet Patients { get; set; } - public DbSet Physicians { get; set; } - public DbSet Appointments { get; set; } +public class EPDDbContext(DbContextOptions options) : DbContext(options), IApplicationDbContext +{ + public DbSet Patients { get; set; } + public DbSet Physicians { get; set; } + public DbSet Appointments { get; set; } - public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) - { - return await base.SaveChangesAsync(cancellationToken); - } + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return await base.SaveChangesAsync(cancellationToken); } -} \ No newline at end of file +} From df046b5b057932763b0be5a1ea0b7ddc4a48946c Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 13:54:51 +0200 Subject: [PATCH 17/23] Herstructureer de Program.cs door de afhankelijkheden te scheiden in nieuwe DependencyInjection klassen voor Application en Infrastructure; implementeer methoden voor het registreren van services en verbeter de leesbaarheid van de code. --- .../DependencyInjection.cs | 23 + .../DependencyInjection.cs | 19 + Chipsoft.Assignments.EPDConsole/Program.cs | 773 +++++++++--------- 3 files changed, 420 insertions(+), 395 deletions(-) create mode 100644 Chipsoft.Assignments.EPDConsole.Application/DependencyInjection.cs create mode 100644 Chipsoft.Assignments.EPDConsole.Infrastructure/DependencyInjection.cs diff --git a/Chipsoft.Assignments.EPDConsole.Application/DependencyInjection.cs b/Chipsoft.Assignments.EPDConsole.Application/DependencyInjection.cs new file mode 100644 index 0000000..666a59f --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Application/DependencyInjection.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using MediatR; +using FluentValidation; +using System.Reflection; +using Chipsoft.Assignments.EPDConsole.Application.Common.Behaviors; + +namespace Chipsoft.Assignments.EPDConsole.Application; + +public static class DependencyInjection +{ + public static IServiceCollection AddApplicationServices(this IServiceCollection services) + { + var applicationAssembly = Assembly.GetExecutingAssembly(); + + services.AddValidatorsFromAssembly(applicationAssembly); + services.AddMediatR(cfg => { + cfg.RegisterServicesFromAssembly(applicationAssembly); + cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); + }); + + return services; + } +} diff --git a/Chipsoft.Assignments.EPDConsole.Infrastructure/DependencyInjection.cs b/Chipsoft.Assignments.EPDConsole.Infrastructure/DependencyInjection.cs new file mode 100644 index 0000000..971bd76 --- /dev/null +++ b/Chipsoft.Assignments.EPDConsole.Infrastructure/DependencyInjection.cs @@ -0,0 +1,19 @@ +using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Chipsoft.Assignments.EPDConsole.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Chipsoft.Assignments.EPDConsole.Infrastructure; + +public static class DependencyInjection +{ + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services) + { + services.AddDbContext(options => + options.UseSqlite("Data Source=epd.db")); + + services.AddScoped(provider => provider.GetService()); + + return services; + } +} diff --git a/Chipsoft.Assignments.EPDConsole/Program.cs b/Chipsoft.Assignments.EPDConsole/Program.cs index 79e0b51..9dfd702 100644 --- a/Chipsoft.Assignments.EPDConsole/Program.cs +++ b/Chipsoft.Assignments.EPDConsole/Program.cs @@ -1,491 +1,474 @@ -using Chipsoft.Assignments.EPDConsole.Application.Common.Behaviors; -using Chipsoft.Assignments.EPDConsole.Core.Entities; -using Chipsoft.Assignments.EPDConsole.Core.Interfaces; +using Chipsoft.Assignments.EPDConsole.Application; +using Chipsoft.Assignments.EPDConsole.Infrastructure; using Chipsoft.Assignments.EPDConsole.Infrastructure.Persistence; -using FluentValidation; using MediatR; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using System; -using System.IO; -using System.Reflection; -using System.Threading.Tasks; using ValidationException = Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions.ValidationException; -namespace Chipsoft.Assignments.EPDConsole -{ - public class Program - { - private static IServiceProvider _serviceProvider; - private static IMediator _mediator; +namespace Chipsoft.Assignments.EPDConsole; - static async Task Main(string[] args) - { - RegisterServices(); - _mediator = _serviceProvider.GetRequiredService(); +public class Program +{ + private static IServiceProvider _serviceProvider; + private static IMediator _mediator; - await ShowMenu(); - - DisposeServices(); - } + static async Task Main(string[] args) + { + RegisterServices(); + _mediator = _serviceProvider.GetRequiredService(); - private static void RegisterServices() - { - var services = new ServiceCollection(); - var applicationAssembly = typeof(Application.Appointments.Queries.GetAppointmentsListQuery).Assembly; + await ShowMenu(); + + DisposeServices(); + } - services.AddDbContext(options => - options.UseSqlite("Data Source=epd.db")); - - services.AddScoped(provider => provider.GetService()); + private static void RegisterServices() + { + var services = new ServiceCollection(); - services.AddValidatorsFromAssembly(applicationAssembly); - services.AddMediatR(cfg => { - cfg.RegisterServicesFromAssembly(applicationAssembly); - cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); - }); + services.AddApplicationServices(); + services.AddInfrastructureServices(); + + _serviceProvider = services.BuildServiceProvider(); + } - _serviceProvider = services.BuildServiceProvider(); + private static void DisposeServices() + { + if (_serviceProvider == null) + { + return; + } + if (_serviceProvider is IDisposable) + { + ((IDisposable)_serviceProvider).Dispose(); } + } - private static void DisposeServices() + public static async Task ShowMenu() + { + bool continueRunning = true; + while (continueRunning) { - if (_serviceProvider == null) + Console.Clear(); + foreach (var line in File.ReadAllLines("logo.txt")) { - return; + Console.WriteLine(line); } - if (_serviceProvider is IDisposable) + Console.WriteLine(""); + Console.WriteLine("1 - Patient toevoegen"); + Console.WriteLine("2 - Patienten verwijderen"); + Console.WriteLine("3 - Arts toevoegen"); + Console.WriteLine("4 - Arts verwijderen"); + Console.WriteLine("5 - Afspraak toevoegen"); + Console.WriteLine("6 - Afspraken inzien"); + Console.WriteLine("7 - Sluiten"); + Console.WriteLine("8 - Reset db"); + + if (int.TryParse(Console.ReadLine(), out int option)) { - ((IDisposable)_serviceProvider).Dispose(); + switch (option) + { + case 1: + await AddPatient(); + break; + case 2: + await DeletePatient(); + break; + case 3: + await AddPhysician(); + break; + case 4: + await DeletePhysician(); + break; + case 5: + await AddAppointment(); + break; + case 6: + await ShowAppointments(); + break; + case 7: + continueRunning = false; + break; + case 8: + ResetDatabase(); + break; + } } } + } - public static async Task ShowMenu() + private static async Task AddPatient() + { + var command = new Application.Patients.Commands.AddPatientCommand(); + + Console.WriteLine("--- Nieuwe patiënt toevoegen ---"); + Console.Write("Voornaam: "); + command.FirstName = Console.ReadLine(); + Console.Write("Achternaam: "); + command.LastName = Console.ReadLine(); + Console.Write("BSN: "); + command.BSN = Console.ReadLine(); + Console.Write("Adres: "); + command.Address = Console.ReadLine(); + Console.Write("Telefoonnummer: "); + command.PhoneNumber = Console.ReadLine(); + Console.Write("E-mail: "); + command.Email = Console.ReadLine(); + + while (true) { - bool continueRunning = true; - while (continueRunning) + Console.Write("Geboortedatum (dd-mm-jjjj): "); + if (DateTime.TryParse(Console.ReadLine(), out DateTime dob)) { - Console.Clear(); - foreach (var line in File.ReadAllLines("logo.txt")) - { - Console.WriteLine(line); - } - Console.WriteLine(""); - Console.WriteLine("1 - Patient toevoegen"); - Console.WriteLine("2 - Patienten verwijderen"); - Console.WriteLine("3 - Arts toevoegen"); - Console.WriteLine("4 - Arts verwijderen"); - Console.WriteLine("5 - Afspraak toevoegen"); - Console.WriteLine("6 - Afspraken inzien"); - Console.WriteLine("7 - Sluiten"); - Console.WriteLine("8 - Reset db"); - - if (int.TryParse(Console.ReadLine(), out int option)) - { - switch (option) - { - case 1: - await AddPatient(); - break; - case 2: - await DeletePatient(); - break; - case 3: - await AddPhysician(); - break; - case 4: - await DeletePhysician(); - break; - case 5: - await AddAppointment(); - break; - case 6: - await ShowAppointments(); - break; - case 7: - continueRunning = false; - break; - case 8: - ResetDatabase(); - break; - } - } + command.DateOfBirth = dob; + break; } + Console.WriteLine("Ongeldige datum, probeer opnieuw."); } - private static async Task AddPatient() + try { - var command = new Application.Patients.Commands.AddPatientCommand(); - - Console.WriteLine("--- Nieuwe patiënt toevoegen ---"); - Console.Write("Voornaam: "); - command.FirstName = Console.ReadLine(); - Console.Write("Achternaam: "); - command.LastName = Console.ReadLine(); - Console.Write("BSN: "); - command.BSN = Console.ReadLine(); - Console.Write("Adres: "); - command.Address = Console.ReadLine(); - Console.Write("Telefoonnummer: "); - command.PhoneNumber = Console.ReadLine(); - Console.Write("E-mail: "); - command.Email = Console.ReadLine(); - - while (true) + var patientId = await _mediator.Send(command); + Console.WriteLine($"Patiënt succesvol toegevoegd met ID: {patientId}."); + } + catch (ValidationException ex) + { + Console.WriteLine("\nValidatie mislukt:"); + foreach (var error in ex.Errors) { - Console.Write("Geboortedatum (dd-mm-jjjj): "); - if (DateTime.TryParse(Console.ReadLine(), out DateTime dob)) - { - command.DateOfBirth = dob; - break; - } - Console.WriteLine("Ongeldige datum, probeer opnieuw."); + Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); } + } + catch (Exception ex) + { + Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + } + + WaitForKeyPress(); + } + + private static async Task DeletePatient() + { + Console.WriteLine("--- Patiënt verwijderen ---"); + + var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); + if (!patients.Any()) + { + Console.WriteLine("Er zijn geen patiënten om te verwijderen."); + WaitForKeyPress(); + return; + } + + Console.WriteLine("Huidige patiënten:"); + foreach (var p in patients) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}, BSN: {p.BSN}"); + } + Console.Write("\nVoer het ID in van de patiënt die u wilt verwijderen: "); + if (int.TryParse(Console.ReadLine(), out int id)) + { try { - var patientId = await _mediator.Send(command); - Console.WriteLine($"Patiënt succesvol toegevoegd met ID: {patientId}."); - } - catch (ValidationException ex) - { - Console.WriteLine("\nValidatie mislukt:"); - foreach (var error in ex.Errors) - { - Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); - } + var command = new Application.Patients.Commands.DeletePatientCommand { Id = id }; + await _mediator.Send(command); + Console.WriteLine("Patiënt succesvol verwijderd."); } catch (Exception ex) { - Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + Console.WriteLine($"Fout bij verwijderen: {ex.Message}"); } - - WaitForKeyPress(); } - - private static async Task DeletePatient() + else { - Console.WriteLine("--- Patiënt verwijderen ---"); - - var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); - if (!patients.Any()) - { - Console.WriteLine("Er zijn geen patiënten om te verwijderen."); - WaitForKeyPress(); - return; - } + Console.WriteLine("Ongeldige invoer."); + } + WaitForKeyPress(); + } - Console.WriteLine("Huidige patiënten:"); - foreach (var p in patients) - { - Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}, BSN: {p.BSN}"); - } + private static async Task AddPhysician() + { + Console.WriteLine("--- Nieuwe arts toevoegen ---"); + Console.Write("Voornaam: "); + var firstName = Console.ReadLine(); - Console.Write("\nVoer het ID in van de patiënt die u wilt verwijderen: "); - if (int.TryParse(Console.ReadLine(), out int id)) + Console.Write("Achternaam: "); + var lastName = Console.ReadLine(); + + try + { + var command = new Application.Physicians.Commands.AddPhysicianCommand { - try - { - var command = new Application.Patients.Commands.DeletePatientCommand { Id = id }; - await _mediator.Send(command); - Console.WriteLine("Patiënt succesvol verwijderd."); - } - catch (Exception ex) - { - Console.WriteLine($"Fout bij verwijderen: {ex.Message}"); - } - } - else + FirstName = firstName, + LastName = lastName + }; + var physicianId = await _mediator.Send(command); + Console.WriteLine($"Arts succesvol toegevoegd met ID: {physicianId}."); + } + catch (ValidationException ex) + { + Console.WriteLine("\nValidatie mislukt:"); + foreach (var error in ex.Errors) { - Console.WriteLine("Ongeldige invoer."); + Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); } + } + catch (Exception ex) + { + Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + } + + WaitForKeyPress(); + } + + private static async Task DeletePhysician() + { + Console.WriteLine("--- Arts verwijderen ---"); + + var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); + if (!physicians.Any()) + { + Console.WriteLine("Er zijn geen artsen om te verwijderen."); WaitForKeyPress(); + return; } - private static async Task AddPhysician() + Console.WriteLine("Huidige artsen:"); + foreach (var p in physicians) { - Console.WriteLine("--- Nieuwe arts toevoegen ---"); - Console.Write("Voornaam: "); - var firstName = Console.ReadLine(); + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } - Console.Write("Achternaam: "); - var lastName = Console.ReadLine(); - + Console.Write("\nVoer het ID in van de arts die u wilt verwijderen: "); + if (int.TryParse(Console.ReadLine(), out int id)) + { try { - var command = new Application.Physicians.Commands.AddPhysicianCommand - { - FirstName = firstName, - LastName = lastName - }; - var physicianId = await _mediator.Send(command); - Console.WriteLine($"Arts succesvol toegevoegd met ID: {physicianId}."); - } - catch (ValidationException ex) - { - Console.WriteLine("\nValidatie mislukt:"); - foreach (var error in ex.Errors) - { - Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); - } + var command = new Application.Physicians.Commands.DeletePhysicianCommand { Id = id }; + await _mediator.Send(command); + Console.WriteLine("Arts succesvol verwijderd."); } catch (Exception ex) { - Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + Console.WriteLine($"Fout bij verwijderen: {ex.Message}"); } - - WaitForKeyPress(); } - - private static async Task DeletePhysician() + else { - Console.WriteLine("--- Arts verwijderen ---"); - - var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); - if (!physicians.Any()) - { - Console.WriteLine("Er zijn geen artsen om te verwijderen."); - WaitForKeyPress(); - return; - } + Console.WriteLine("Ongeldige invoer."); + } + WaitForKeyPress(); + } - Console.WriteLine("Huidige artsen:"); - foreach (var p in physicians) - { - Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); - } + private static async Task AddAppointment() + { + Console.WriteLine("--- Nieuwe afspraak toevoegen ---"); - Console.Write("\nVoer het ID in van de arts die u wilt verwijderen: "); - if (int.TryParse(Console.ReadLine(), out int id)) - { - try - { - var command = new Application.Physicians.Commands.DeletePhysicianCommand { Id = id }; - await _mediator.Send(command); - Console.WriteLine("Arts succesvol verwijderd."); - } - catch (Exception ex) - { - Console.WriteLine($"Fout bij verwijderen: {ex.Message}"); - } - } - else - { - Console.WriteLine("Ongeldige invoer."); - } + var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); + if (!patients.Any()) + { + Console.WriteLine("Er zijn geen patiënten beschikbaar. Voeg eerst een patiënt toe."); WaitForKeyPress(); + return; } - private static async Task AddAppointment() + var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); + if (!physicians.Any()) { - Console.WriteLine("--- Nieuwe afspraak toevoegen ---"); + Console.WriteLine("Er zijn geen artsen beschikbaar. Voeg eerst een arts toe."); + WaitForKeyPress(); + return; + } - var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); - if (!patients.Any()) - { - Console.WriteLine("Er zijn geen patiënten beschikbaar. Voeg eerst een patiënt toe."); - WaitForKeyPress(); - return; - } + Console.WriteLine("\nBeschikbare Patiënten:"); + foreach (var p in patients) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + Console.Write("Kies een patiënt ID: "); + if (!int.TryParse(Console.ReadLine(), out int patientId) || !patients.Any(p => p.Id == patientId)) + { + Console.WriteLine("Ongeldig patiënt ID."); + WaitForKeyPress(); + return; + } - var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); - if (!physicians.Any()) + Console.WriteLine("\nBeschikbare Artsen:"); + foreach (var p in physicians) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); + } + Console.Write("Kies een arts ID: "); + if (!int.TryParse(Console.ReadLine(), out int physicianId) || !physicians.Any(p => p.Id == physicianId)) + { + Console.WriteLine("Ongeldig arts ID."); + WaitForKeyPress(); + return; + } + + DateTime appointmentDateTime; + while (true) + { + Console.Write("Voer de datum en tijd in (dd-mm-jjjj uu:mm): "); + if (DateTime.TryParseExact(Console.ReadLine(), "dd-MM-yyyy HH:mm", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out appointmentDateTime)) { - Console.WriteLine("Er zijn geen artsen beschikbaar. Voeg eerst een arts toe."); - WaitForKeyPress(); - return; + break; } + Console.WriteLine("Ongeldige datum/tijd, probeer opnieuw."); + } - Console.WriteLine("\nBeschikbare Patiënten:"); - foreach (var p in patients) - { - Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); - } - Console.Write("Kies een patiënt ID: "); - if (!int.TryParse(Console.ReadLine(), out int patientId) || !patients.Any(p => p.Id == patientId)) + try + { + var command = new Application.Appointments.Commands.AddAppointmentCommand { - Console.WriteLine("Ongeldig patiënt ID."); - WaitForKeyPress(); - return; - } + PatientId = patientId, + PhysicianId = physicianId, + AppointmentDateTime = appointmentDateTime + }; - Console.WriteLine("\nBeschikbare Artsen:"); - foreach (var p in physicians) - { - Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); - } - Console.Write("Kies een arts ID: "); - if (!int.TryParse(Console.ReadLine(), out int physicianId) || !physicians.Any(p => p.Id == physicianId)) + var appointmentId = await _mediator.Send(command); + Console.WriteLine($"Afspraak succesvol toegevoegd met ID: {appointmentId}."); + } + catch (ValidationException ex) + { + Console.WriteLine("\nValidatie mislukt:"); + foreach (var error in ex.Errors) { - Console.WriteLine("Ongeldig arts ID."); - WaitForKeyPress(); - return; + Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); } - - DateTime appointmentDateTime; - while (true) + } + catch (Exception ex) + { + Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); + } + WaitForKeyPress(); + } + + private static async Task ShowAppointments() + { + Console.Clear(); + Console.WriteLine("--- Afspraken Inzien ---"); + Console.WriteLine("1 - Toon alle afspraken"); + Console.WriteLine("2 - Filter op patiënt"); + Console.WriteLine("3 - Filter op arts"); + Console.Write("Kies een optie: "); + + if (int.TryParse(Console.ReadLine(), out int option)) + { + switch (option) { - Console.Write("Voer de datum en tijd in (dd-mm-jjjj uu:mm): "); - if (DateTime.TryParseExact(Console.ReadLine(), "dd-MM-yyyy HH:mm", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out appointmentDateTime)) - { + case 1: + var allAppointments = await _mediator.Send(new Application.Appointments.Queries.GetAppointmentsListQuery()); + await DisplayAppointments(allAppointments); + break; + case 2: + await ShowAppointmentsByPatient(); + break; + case 3: + await ShowAppointmentsByPhysician(); + break; + default: + Console.WriteLine("Ongeldige optie."); break; - } - Console.WriteLine("Ongeldige datum/tijd, probeer opnieuw."); - } - - try - { - var command = new Application.Appointments.Commands.AddAppointmentCommand - { - PatientId = patientId, - PhysicianId = physicianId, - AppointmentDateTime = appointmentDateTime - }; - - var appointmentId = await _mediator.Send(command); - Console.WriteLine($"Afspraak succesvol toegevoegd met ID: {appointmentId}."); - } - catch (ValidationException ex) - { - Console.WriteLine("\nValidatie mislukt:"); - foreach (var error in ex.Errors) - { - Console.WriteLine($"- {error.Key}: {string.Join(", ", error.Value)}"); - } - } - catch (Exception ex) - { - Console.WriteLine($"\nEr is een onverwachte fout opgetreden: {ex.Message}"); } - WaitForKeyPress(); } - - private static async Task ShowAppointments() + else { - Console.Clear(); - Console.WriteLine("--- Afspraken Inzien ---"); - Console.WriteLine("1 - Toon alle afspraken"); - Console.WriteLine("2 - Filter op patiënt"); - Console.WriteLine("3 - Filter op arts"); - Console.Write("Kies een optie: "); + Console.WriteLine("Ongeldige invoer."); + } - if (int.TryParse(Console.ReadLine(), out int option)) - { - switch (option) - { - case 1: - var allAppointments = await _mediator.Send(new Application.Appointments.Queries.GetAppointmentsListQuery()); - await DisplayAppointments(allAppointments); - break; - case 2: - await ShowAppointmentsByPatient(); - break; - case 3: - await ShowAppointmentsByPhysician(); - break; - default: - Console.WriteLine("Ongeldige optie."); - break; - } - } - else - { - Console.WriteLine("Ongeldige invoer."); - } + WaitForKeyPress(); + } - WaitForKeyPress(); + private static async Task ShowAppointmentsByPatient() + { + var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); + if (!patients.Any()) + { + Console.WriteLine("\nEr zijn geen patiënten om op te filteren."); + return; } - private static async Task ShowAppointmentsByPatient() + Console.WriteLine("\nKies een patiënt:"); + foreach (var p in patients) { - var patients = await _mediator.Send(new Application.Patients.Queries.GetPatientsListQuery()); - if (!patients.Any()) - { - Console.WriteLine("\nEr zijn geen patiënten om op te filteren."); - return; - } - - Console.WriteLine("\nKies een patiënt:"); - foreach (var p in patients) - { - Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); - } - - Console.Write("Voer patiënt ID in: "); - if (int.TryParse(Console.ReadLine(), out int patientId) && patients.Any(p => p.Id == patientId)) - { - var query = new Application.Appointments.Queries.GetAppointmentsByPatientQuery { PatientId = patientId }; - var appointments = await _mediator.Send(query); - await DisplayAppointments(appointments); - } - else - { - Console.WriteLine("Ongeldig patiënt ID."); - } + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); } - private static async Task ShowAppointmentsByPhysician() + Console.Write("Voer patiënt ID in: "); + if (int.TryParse(Console.ReadLine(), out int patientId) && patients.Any(p => p.Id == patientId)) { - var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); - if (!physicians.Any()) - { - Console.WriteLine("\nEr zijn geen artsen om op te filteren."); - return; - } + var query = new Application.Appointments.Queries.GetAppointmentsByPatientQuery { PatientId = patientId }; + var appointments = await _mediator.Send(query); + await DisplayAppointments(appointments); + } + else + { + Console.WriteLine("Ongeldig patiënt ID."); + } + } - Console.WriteLine("\nKies een arts:"); - foreach (var p in physicians) - { - Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); - } + private static async Task ShowAppointmentsByPhysician() + { + var physicians = await _mediator.Send(new Application.Physicians.Queries.GetPhysiciansListQuery()); + if (!physicians.Any()) + { + Console.WriteLine("\nEr zijn geen artsen om op te filteren."); + return; + } - Console.Write("Voer arts ID in: "); - if (int.TryParse(Console.ReadLine(), out int physicianId) && physicians.Any(p => p.Id == physicianId)) - { - var query = new Application.Appointments.Queries.GetAppointmentsByPhysicianQuery { PhysicianId = physicianId }; - var appointments = await _mediator.Send(query); - await DisplayAppointments(appointments); - } - else - { - Console.WriteLine("Ongeldig arts ID."); - } + Console.WriteLine("\nKies een arts:"); + foreach (var p in physicians) + { + Console.WriteLine($"ID: {p.Id}, Naam: {p.Name}"); } - private static Task DisplayAppointments(System.Collections.Generic.List appointments) + Console.Write("Voer arts ID in: "); + if (int.TryParse(Console.ReadLine(), out int physicianId) && physicians.Any(p => p.Id == physicianId)) { - Console.WriteLine("\n--- Overzicht afspraken ---"); - if (!appointments.Any()) - { - Console.WriteLine("Er zijn geen afspraken gevonden voor deze selectie."); - } - else - { - foreach (var app in appointments) - { - Console.WriteLine($"Datum/Tijd: {app.AppointmentDateTime:dd-MM-yyyy HH:mm}"); - Console.WriteLine($" Patiënt: {app.PatientName}"); - Console.WriteLine($" Arts: {app.PhysicianName}"); - Console.WriteLine(new string('-', 20)); - } - } - return Task.CompletedTask; + var query = new Application.Appointments.Queries.GetAppointmentsByPhysicianQuery { PhysicianId = physicianId }; + var appointments = await _mediator.Send(query); + await DisplayAppointments(appointments); + } + else + { + Console.WriteLine("Ongeldig arts ID."); } + } - private static void ResetDatabase() + private static Task DisplayAppointments(List appointments) + { + Console.WriteLine("\n--- Overzicht afspraken ---"); + if (!appointments.Any()) { - using var scope = _serviceProvider.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Database.EnsureDeleted(); - dbContext.Database.EnsureCreated(); - Console.WriteLine("Database is gereset."); - WaitForKeyPress(); + Console.WriteLine("Er zijn geen afspraken gevonden voor deze selectie."); } - - private static void WaitForKeyPress() + else { - Console.WriteLine("\nDruk op een toets om terug te keren naar het menu..."); - Console.ReadKey(); + foreach (var app in appointments) + { + Console.WriteLine($"Datum/Tijd: {app.AppointmentDateTime:dd-MM-yyyy HH:mm}"); + Console.WriteLine($" Patiënt: {app.PatientName}"); + Console.WriteLine($" Arts: {app.PhysicianName}"); + Console.WriteLine(new string('-', 20)); + } } + return Task.CompletedTask; + } + + private static void ResetDatabase() + { + using var scope = _serviceProvider.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Database.EnsureDeleted(); + dbContext.Database.EnsureCreated(); + Console.WriteLine("Database is gereset."); + WaitForKeyPress(); + } + + private static void WaitForKeyPress() + { + Console.WriteLine("\nDruk op een toets om terug te keren naar het menu..."); + Console.ReadKey(); } } \ No newline at end of file From b658c2094dc2786c83324d4adf4b7cebaae2df69 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 14:05:30 +0200 Subject: [PATCH 18/23] Verbeter validatieregels in AddPatientCommandValidator en voeg nieuwe tests toe voor adres, telefoonnummer, e-mail en geboortedatum; pas de geboortedatumvalidatie aan om toekomstige datums te voorkomen en implementeer exacte datum parsing in Program.cs. --- .../Commands/AddPatientCommandHandlerTests.cs | 18 ++++++++-- .../AddPatientCommandValidatorTests.cs | 33 +++++++++++++++++++ .../Commands/AddPatientCommandValidator.cs | 15 +++++---- Chipsoft.Assignments.EPDConsole/Program.cs | 2 +- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs index 11ccae4..426e12c 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs @@ -33,14 +33,28 @@ public async Task Handle_Should_Add_Patient_And_Save_Changes() FirstName = "John", LastName = "Doe", BSN = "123456789", - Email = "john.doe@test.com" + Address = "123 Main St", + PhoneNumber = "555-1234", + Email = "john.doe@test.com", + DateOfBirth = new System.DateTime(1990, 1, 1) }; // Act await _handler.Handle(command, CancellationToken.None); // Assert - _contextMock.Verify(x => x.Patients.Add(It.Is(p => p.FirstName == "John")), Times.Once); + _contextMock.Verify( + x => x.Patients.Add(It.Is(p => + p.FirstName == command.FirstName && + p.LastName == command.LastName && + p.BSN == command.BSN && + p.Address == command.Address && + p.PhoneNumber == command.PhoneNumber && + p.Email == command.Email && + p.DateOfBirth == command.DateOfBirth + )), + Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); } } diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs index 9f88683..57b419d 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs @@ -47,6 +47,38 @@ public async Task Should_Have_Error_When_BSN_Is_Invalid() result.ShouldHaveValidationErrorFor(x => x.BSN); } + [Fact] + public async Task Should_Have_Error_When_Email_Is_Invalid() + { + var command = new AddPatientCommand { Email = "invalid-email" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.Email); + } + + [Fact] + public async Task Should_Have_Error_When_PhoneNumber_Is_Too_Short() + { + var command = new AddPatientCommand { PhoneNumber = "123" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.PhoneNumber); + } + + [Fact] + public async Task Should_Have_Error_When_DateOfBirth_Is_In_Future() + { + var command = new AddPatientCommand { DateOfBirth = DateTime.Now.AddDays(1) }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.DateOfBirth); + } + + [Fact] + public async Task Should_Have_Error_When_Address_Is_Empty() + { + var command = new AddPatientCommand { Address = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.Address); + } + [Fact] public async Task Should_Not_Have_Error_When_Command_Is_Valid() { @@ -55,6 +87,7 @@ public async Task Should_Not_Have_Error_When_Command_Is_Valid() FirstName = "John", LastName = "Doe", BSN = "123456789", + Address = "123 Main St", Email = "john.doe@test.com", DateOfBirth = new System.DateTime(1990, 1, 1), PhoneNumber = "0612345678" diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs index d13a778..82c6b4f 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs @@ -24,12 +24,18 @@ public AddPatientCommandValidator(IApplicationDbContext context) .NotEmpty().WithMessage("Achternaam is een verplicht veld.") .MaximumLength(200); + RuleFor(v => v.Address) + .NotEmpty().WithMessage("Adres is een verplicht veld."); + RuleFor(v => v.BSN) .NotEmpty().WithMessage("BSN is een verplicht veld.") - .Length(9).WithMessage("BSN moet 9 karakters lang zijn.") + .Length(9).WithMessage("BSN moet exact 9 cijfers bevatten.") + .Matches("^[0-9]*$").WithMessage("BSN mag alleen cijfers bevatten.") .MustAsync(BeUniqueBsn).WithMessage("Een patiënt met dit BSN bestaat al."); RuleFor(v => v.PhoneNumber) + .NotEmpty().WithMessage("Telefoonnummer is een verplicht veld.") + .MinimumLength(10).WithMessage("Telefoonnummer moet minimaal 10 tekens lang zijn.") .Matches(new Regex(@"^[\d\s\(\)\+\-]+$")).WithMessage("Telefoonnummer mag alleen nummers en gebruikelijke tekens (+, -, (, )) bevatten."); RuleFor(v => v.Email) @@ -38,12 +44,7 @@ public AddPatientCommandValidator(IApplicationDbContext context) RuleFor(v => v.DateOfBirth) .NotEmpty() - .Must(BeAValidDate).WithMessage("Geboortedatum moet in het verleden liggen."); - } - - private bool BeAValidDate(DateTime date) - { - return date < DateTime.Now; + .LessThan(DateTime.Now).WithMessage("Geboortedatum kan niet in de toekomst liggen."); } private async Task BeUniqueBsn(string bsn, CancellationToken cancellationToken) diff --git a/Chipsoft.Assignments.EPDConsole/Program.cs b/Chipsoft.Assignments.EPDConsole/Program.cs index 9dfd702..5dd49c5 100644 --- a/Chipsoft.Assignments.EPDConsole/Program.cs +++ b/Chipsoft.Assignments.EPDConsole/Program.cs @@ -118,7 +118,7 @@ private static async Task AddPatient() while (true) { Console.Write("Geboortedatum (dd-mm-jjjj): "); - if (DateTime.TryParse(Console.ReadLine(), out DateTime dob)) + if (DateTime.TryParseExact(Console.ReadLine(), "dd-MM-yyyy", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime dob)) { command.DateOfBirth = dob; break; From ec5f81fe810237ac01f0108d5321d0e818c49380 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 14:13:32 +0200 Subject: [PATCH 19/23] =?UTF-8?q?Voeg=20validatieregels=20toe=20voor=20Pat?= =?UTF-8?q?ientId=20en=20PhysicianId=20in=20AddAppointmentCommandValidator?= =?UTF-8?q?;=20implementeer=20asynchrone=20methoden=20om=20de=20bestaan=20?= =?UTF-8?q?van=20pati=C3=ABnten=20en=20artsen=20te=20controleren.=20Breid?= =?UTF-8?q?=20unit=20tests=20uit=20om=20validatiefouten=20voor=20verleden?= =?UTF-8?q?=20datums=20en=20niet-bestaande=20pati=C3=ABnten=20en=20artsen?= =?UTF-8?q?=20te=20verifi=C3=ABren.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddAppointmentCommandValidatorTests.cs | 53 ++++++ .../Commands/AddPatientCommandHandlerTests.cs | 5 - .../AddPatientCommandValidatorTests.cs | 158 +++++++++--------- .../AddAppointmentCommandValidator.cs | 18 ++ 4 files changed, 148 insertions(+), 86 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs index fc03437..da85d0f 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs @@ -86,5 +86,58 @@ public async Task Should_Have_Error_When_Patient_Has_Conflict() // Assert result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); } + + [Fact] + public async Task Should_Have_Error_When_Date_Is_In_The_Past() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(-1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldHaveValidationErrorFor(cmd => cmd.AppointmentDateTime); + } + + [Fact] + public async Task Should_Have_Error_When_PatientId_Does_Not_Exist() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List()); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 99, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldHaveValidationErrorFor(cmd => cmd.PatientId) + .WithErrorMessage("De geselecteerde patiënt bestaat niet."); + } + + [Fact] + public async Task Should_Have_Error_When_PhysicianId_Does_Not_Exist() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List()); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 99, AppointmentDateTime = DateTime.Now.AddDays(1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldHaveValidationErrorFor(cmd => cmd.PhysicianId) + .WithErrorMessage("De geselecteerde arts bestaat niet."); + } } } \ No newline at end of file diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs index 426e12c..85bc50a 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs @@ -3,11 +3,6 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands { public class AddPatientCommandHandlerTests diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs index 57b419d..dfc7b68 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Validators/AddPatientCommandValidatorTests.cs @@ -4,96 +4,92 @@ using FluentValidation.TestHelper; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Validators +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Validators; + +public class AddPatientCommandValidatorTests { - public class AddPatientCommandValidatorTests - { - private readonly Mock _contextMock; - private readonly AddPatientCommandValidator _validator; + private readonly Mock _contextMock; + private readonly AddPatientCommandValidator _validator; - public AddPatientCommandValidatorTests() - { - _contextMock = new Mock(); - // Setup the Patients DbSet mock - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List()); - _validator = new AddPatientCommandValidator(_contextMock.Object); - } + public AddPatientCommandValidatorTests() + { + _contextMock = new Mock(); + // Setup the Patients DbSet mock + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List()); + _validator = new AddPatientCommandValidator(_contextMock.Object); + } - [Fact] - public async Task Should_Have_Error_When_FirstName_Is_Empty() - { - var command = new AddPatientCommand { FirstName = string.Empty }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.FirstName); - } + [Fact] + public async Task Should_Have_Error_When_FirstName_Is_Empty() + { + var command = new AddPatientCommand { FirstName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.FirstName); + } - [Fact] - public async Task Should_Have_Error_When_LastName_Is_Empty() - { - var command = new AddPatientCommand { LastName = string.Empty }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.LastName); - } - - [Fact] - public async Task Should_Have_Error_When_BSN_Is_Invalid() - { - var command = new AddPatientCommand { BSN = "123" }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.BSN); - } + [Fact] + public async Task Should_Have_Error_When_LastName_Is_Empty() + { + var command = new AddPatientCommand { LastName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.LastName); + } + + [Fact] + public async Task Should_Have_Error_When_BSN_Is_Invalid() + { + var command = new AddPatientCommand { BSN = "123" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.BSN); + } - [Fact] - public async Task Should_Have_Error_When_Email_Is_Invalid() - { - var command = new AddPatientCommand { Email = "invalid-email" }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.Email); - } + [Fact] + public async Task Should_Have_Error_When_Email_Is_Invalid() + { + var command = new AddPatientCommand { Email = "invalid-email" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.Email); + } - [Fact] - public async Task Should_Have_Error_When_PhoneNumber_Is_Too_Short() - { - var command = new AddPatientCommand { PhoneNumber = "123" }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.PhoneNumber); - } + [Fact] + public async Task Should_Have_Error_When_PhoneNumber_Is_Too_Short() + { + var command = new AddPatientCommand { PhoneNumber = "123" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.PhoneNumber); + } - [Fact] - public async Task Should_Have_Error_When_DateOfBirth_Is_In_Future() - { - var command = new AddPatientCommand { DateOfBirth = DateTime.Now.AddDays(1) }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.DateOfBirth); - } + [Fact] + public async Task Should_Have_Error_When_DateOfBirth_Is_In_Future() + { + var command = new AddPatientCommand { DateOfBirth = DateTime.Now.AddDays(1) }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.DateOfBirth); + } - [Fact] - public async Task Should_Have_Error_When_Address_Is_Empty() - { - var command = new AddPatientCommand { Address = string.Empty }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.Address); - } + [Fact] + public async Task Should_Have_Error_When_Address_Is_Empty() + { + var command = new AddPatientCommand { Address = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.Address); + } - [Fact] - public async Task Should_Not_Have_Error_When_Command_Is_Valid() + [Fact] + public async Task Should_Not_Have_Error_When_Command_Is_Valid() + { + var command = new AddPatientCommand { - var command = new AddPatientCommand - { - FirstName = "John", - LastName = "Doe", - BSN = "123456789", - Address = "123 Main St", - Email = "john.doe@test.com", - DateOfBirth = new System.DateTime(1990, 1, 1), - PhoneNumber = "0612345678" - }; - var result = await _validator.TestValidateAsync(command); - result.ShouldNotHaveAnyValidationErrors(); - } + FirstName = "John", + LastName = "Doe", + BSN = "123456789", + Address = "123 Main St", + Email = "john.doe@test.com", + DateOfBirth = new System.DateTime(1990, 1, 1), + PhoneNumber = "0612345678" + }; + var result = await _validator.TestValidateAsync(command); + result.ShouldNotHaveAnyValidationErrors(); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs index b71f70a..a9912e0 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs @@ -18,10 +18,28 @@ public AddAppointmentCommandValidator(IApplicationDbContext context) RuleFor(v => v.AppointmentDateTime) .GreaterThan(DateTime.Now).WithMessage("Een afspraak moet in de toekomst worden gepland."); + RuleFor(v => v.PatientId) + .NotEmpty().WithMessage("Patiënt ID is verplicht.") + .MustAsync(PatientExists).WithMessage("De geselecteerde patiënt bestaat niet."); + + RuleFor(v => v.PhysicianId) + .NotEmpty().WithMessage("Arts ID is verplicht.") + .MustAsync(PhysicianExists).WithMessage("De geselecteerde arts bestaat niet."); + RuleFor(v => v) .MustAsync(BeAvailable).WithMessage("De arts of patiënt heeft op dit tijdstip al een andere afspraak."); } + private async Task PatientExists(int id, CancellationToken cancellationToken) + { + return await _context.Patients.AnyAsync(p => p.Id == id, cancellationToken); + } + + private async Task PhysicianExists(int id, CancellationToken cancellationToken) + { + return await _context.Physicians.AnyAsync(p => p.Id == id, cancellationToken); + } + private async Task BeAvailable(AddAppointmentCommand command, CancellationToken cancellationToken) { var appointmentTime = command.AppointmentDateTime; From bf802b1e915ddca7b1637564a315703475946e0d Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 14:22:31 +0200 Subject: [PATCH 20/23] Voeg extra validatieregels en bijbehorende unit tests toe voor de AddPhysicianCommandValidator; controleer op lege voor- en achternamen en maximale lengte van de naam. --- .../AddPhysicianCommandValidatorTests.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs index 05b592c..5f56f25 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs @@ -26,11 +26,35 @@ public AddPhysicianCommandValidatorTests() [Fact] public async Task Should_Have_Error_When_FirstName_Is_Empty() { - var command = new AddPhysicianCommand { FirstName = string.Empty }; + var command = new AddPhysicianCommand { FirstName = string.Empty, LastName = "Test" }; var result = await _validator.TestValidateAsync(command); result.ShouldHaveValidationErrorFor(x => x.FirstName); } + [Fact] + public async Task Should_Have_Error_When_LastName_Is_Empty() + { + var command = new AddPhysicianCommand { FirstName = "Test", LastName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.LastName); + } + + [Fact] + public async Task Should_Have_Error_When_FirstName_Exceeds_MaxLength() + { + var command = new AddPhysicianCommand { FirstName = new string('a', 201), LastName = "Test" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.FirstName); + } + + [Fact] + public async Task Should_Have_Error_When_LastName_Exceeds_MaxLength() + { + var command = new AddPhysicianCommand { FirstName = "Test", LastName = new string('a', 201) }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.LastName); + } + [Fact] public async Task Should_Not_Have_Error_When_Name_Is_Unique() { From cb7d95818749c7ff584668a3022e5a1772d9e3d3 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 14:28:45 +0200 Subject: [PATCH 21/23] =?UTF-8?q?Herstructureer=20en=20vereenvoudig=20de?= =?UTF-8?q?=20testklassen=20voor=20de=20command=20handlers=20en=20validato?= =?UTF-8?q?rs=20van=20pati=C3=ABnten=20en=20artsen;=20verwijder=20overbodi?= =?UTF-8?q?ge=20code=20en=20verbeter=20de=20leesbaarheid=20van=20de=20test?= =?UTF-8?q?s.=20Voeg=20nieuwe=20tests=20toe=20voor=20het=20toevoegen,=20ve?= =?UTF-8?q?rwijderen=20en=20valideren=20van=20pati=C3=ABnten=20en=20artsen?= =?UTF-8?q?,=20inclusief=20foutafhandeling=20voor=20bestaande=20records.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AddAppointmentCommandHandlerTests.cs | 63 ++--- ...tAppointmentsByPatientQueryHandlerTests.cs | 79 +++--- ...ppointmentsByPhysicianQueryHandlerTests.cs | 73 +++-- .../GetAppointmentsListQueryHandlerTests.cs | 71 +++-- .../AddAppointmentCommandValidatorTests.cs | 251 +++++++++--------- .../Commands/AddPatientCommandHandlerTests.cs | 83 +++--- .../DeletePatientCommandHandlerTests.cs | 156 ++++++----- .../GetPatientsListQueryHandlerTests.cs | 67 +++-- .../AddPhysicianCommandHandlerTests.cs | 57 ++-- .../DeletePhysicianCommandHandlerTests.cs | 158 ++++++----- .../GetPhysiciansListQueryHandlerTests.cs | 67 +++-- .../AddPhysicianCommandValidatorTests.cs | 126 +++++---- 12 files changed, 595 insertions(+), 656 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs index bf5ab55..e6312d7 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Commands/AddAppointmentCommandHandlerTests.cs @@ -3,44 +3,39 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Moq; using Moq.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Commands + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Commands; + +public class AddAppointmentCommandHandlerTests { - public class AddAppointmentCommandHandlerTests + private readonly Mock _contextMock; + private readonly AddAppointmentCommandHandler _handler; + + public AddAppointmentCommandHandlerTests() + { + _contextMock = new Mock(); + _handler = new AddAppointmentCommandHandler(_contextMock.Object); + } + + [Fact] + public async Task Handle_Should_Add_Appointment_And_Save_Changes() { - private readonly Mock _contextMock; - private readonly AddAppointmentCommandHandler _handler; + // Arrange + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); - public AddAppointmentCommandHandlerTests() + var command = new AddAppointmentCommand { - _contextMock = new Mock(); - _handler = new AddAppointmentCommandHandler(_contextMock.Object); - } + PatientId = 1, + PhysicianId = 1, + AppointmentDateTime = DateTime.Now.AddHours(1) + }; - [Fact] - public async Task Handle_Should_Add_Appointment_And_Save_Changes() - { - // Arrange - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); - - var command = new AddAppointmentCommand - { - PatientId = 1, - PhysicianId = 1, - AppointmentDateTime = DateTime.Now.AddHours(1) - }; - - // Act - await _handler.Handle(command, CancellationToken.None); - - // Assert - _contextMock.Verify(x => x.Appointments.Add(It.IsAny()), Times.Once); - _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); - } + // Act + await _handler.Handle(command, CancellationToken.None); + + // Assert + _contextMock.Verify(x => x.Appointments.Add(It.IsAny()), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs index 058eb95..5e44135 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPatientQueryHandlerTests.cs @@ -4,52 +4,45 @@ using FluentAssertions; using Moq; using Moq.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries; + +public class GetAppointmentsByPatientQueryHandlerTests { - public class GetAppointmentsByPatientQueryHandlerTests + private readonly Mock _contextMock; + private readonly GetAppointmentsByPatientQueryHandler _handler; + + public GetAppointmentsByPatientQueryHandlerTests() { - private readonly Mock _contextMock; - private readonly GetAppointmentsByPatientQueryHandler _handler; + _contextMock = new Mock(); + _handler = new GetAppointmentsByPatientQueryHandler(_contextMock.Object); + } - public GetAppointmentsByPatientQueryHandlerTests() - { - _contextMock = new Mock(); - _handler = new GetAppointmentsByPatientQueryHandler(_contextMock.Object); - } + [Fact] + public async Task Handle_Should_Return_Only_Appointments_For_Given_Patient() + { + // Arrange + var patient1 = new Patient { Id = 1, FirstName = "John", LastName = "Doe" }; + var patient2 = new Patient { Id = 2, FirstName = "Jane", LastName = "Doe" }; + var physician = new Physician { Id = 1, FirstName = "Dr.", LastName = "Smith" }; - [Fact] - public async Task Handle_Should_Return_Only_Appointments_For_Given_Patient() + var appointments = new List { - // Arrange - var patient1 = new Patient { Id = 1, FirstName = "John", LastName = "Doe" }; - var patient2 = new Patient { Id = 2, FirstName = "Jane", LastName = "Doe" }; - var physician = new Physician { Id = 1, FirstName = "Dr.", LastName = "Smith" }; - - var appointments = new List - { - new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient1, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(1) }, - new Appointment { PatientId = 2, PhysicianId = 1, Patient = patient2, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(2) }, - new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient1, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(3) } - }; - - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); - - var query = new GetAppointmentsByPatientQuery { PatientId = 1 }; - - // Act - var result = await _handler.Handle(query, CancellationToken.None); - - // Assert - result.Should().NotBeNull(); - result.Should().HaveCount(2); - result.All(a => a.PatientName == "John Doe").Should().BeTrue(); - } + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient1, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(1) }, + new Appointment { PatientId = 2, PhysicianId = 1, Patient = patient2, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(2) }, + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient1, Physician = physician, AppointmentDateTime = DateTime.Now.AddDays(3) } + }; + + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var query = new GetAppointmentsByPatientQuery { PatientId = 1 }; + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result.All(a => a.PatientName == "John Doe").Should().BeTrue(); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs index 37daf41..2e95604 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsByPhysicianQueryHandlerTests.cs @@ -4,53 +4,46 @@ using FluentAssertions; using Moq; using Moq.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries; + +public class GetAppointmentsByPhysicianQueryHandlerTests { - public class GetAppointmentsByPhysicianQueryHandlerTests - { - private readonly Mock _contextMock; - private readonly GetAppointmentsByPhysicianQueryHandler _handler; + private readonly Mock _contextMock; + private readonly GetAppointmentsByPhysicianQueryHandler _handler; - public GetAppointmentsByPhysicianQueryHandlerTests() - { - _contextMock = new Mock(); - _handler = new GetAppointmentsByPhysicianQueryHandler(_contextMock.Object); - } + public GetAppointmentsByPhysicianQueryHandlerTests() + { + _contextMock = new Mock(); + _handler = new GetAppointmentsByPhysicianQueryHandler(_contextMock.Object); + } - [Fact] - public async Task Handle_Should_Return_Only_Appointments_For_Given_Physician() - { - // Arrange - var patient = new Patient { Id = 1, FirstName = "John", LastName = "Doe" }; - var physician1 = new Physician { Id = 1, FirstName = "Dr.", LastName = "Smith" }; - var physician2 = new Physician { Id = 2, FirstName = "Dr.", LastName = "Who" }; + [Fact] + public async Task Handle_Should_Return_Only_Appointments_For_Given_Physician() + { + // Arrange + var patient = new Patient { Id = 1, FirstName = "John", LastName = "Doe" }; + var physician1 = new Physician { Id = 1, FirstName = "Dr.", LastName = "Smith" }; + var physician2 = new Physician { Id = 2, FirstName = "Dr.", LastName = "Who" }; - var appointments = new List - { - new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient, Physician = physician1, AppointmentDateTime = DateTime.Now.AddDays(1) }, - new Appointment { PatientId = 1, PhysicianId = 2, Patient = patient, Physician = physician2, AppointmentDateTime = DateTime.Now.AddDays(2) }, - new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient, Physician = physician1, AppointmentDateTime = DateTime.Now.AddDays(3) } - }; + var appointments = new List + { + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient, Physician = physician1, AppointmentDateTime = DateTime.Now.AddDays(1) }, + new Appointment { PatientId = 1, PhysicianId = 2, Patient = patient, Physician = physician2, AppointmentDateTime = DateTime.Now.AddDays(2) }, + new Appointment { PatientId = 1, PhysicianId = 1, Patient = patient, Physician = physician1, AppointmentDateTime = DateTime.Now.AddDays(3) } + }; - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); - var query = new GetAppointmentsByPhysicianQuery { PhysicianId = 1 }; + var query = new GetAppointmentsByPhysicianQuery { PhysicianId = 1 }; - // Act - var result = await _handler.Handle(query, CancellationToken.None); + // Act + var result = await _handler.Handle(query, CancellationToken.None); - // Assert - result.Should().NotBeNull(); - result.Should().HaveCount(2); - result.All(a => a.PhysicianName == "Dr. Smith").Should().BeTrue(); - } + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result.All(a => a.PhysicianName == "Dr. Smith").Should().BeTrue(); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs index 9f2fcba..ca12214 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Queries/GetAppointmentsListQueryHandlerTests.cs @@ -4,48 +4,43 @@ using FluentAssertions; using Moq; using Moq.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries + +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Queries; + +public class GetAppointmentsListQueryHandlerTests { - public class GetAppointmentsListQueryHandlerTests + private readonly Mock _contextMock; + private readonly GetAppointmentsListQueryHandler _handler; + + public GetAppointmentsListQueryHandlerTests() { - private readonly Mock _contextMock; - private readonly GetAppointmentsListQueryHandler _handler; + _contextMock = new Mock(); + _handler = new GetAppointmentsListQueryHandler(_contextMock.Object); + } - public GetAppointmentsListQueryHandlerTests() + [Fact] + public async Task Handle_Should_Return_All_Appointments_As_Dtos() + { + // Arrange + var patients = new List { new Patient { Id = 1, FirstName = "John", LastName = "Doe" } }; + var physicians = new List { new Physician { Id = 1, FirstName = "Jane", LastName = "Smith" } }; + var appointments = new List { - _contextMock = new Mock(); - _handler = new GetAppointmentsListQueryHandler(_contextMock.Object); - } + new Appointment { Id = 1, PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1), Patient = patients[0], Physician = physicians[0] } + }; - [Fact] - public async Task Handle_Should_Return_All_Appointments_As_Dtos() - { - // Arrange - var patients = new List { new Patient { Id = 1, FirstName = "John", LastName = "Doe" } }; - var physicians = new List { new Physician { Id = 1, FirstName = "Jane", LastName = "Smith" } }; - var appointments = new List - { - new Appointment { Id = 1, PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1), Patient = patients[0], Physician = physicians[0] } - }; - - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); - - var query = new GetAppointmentsListQuery(); - - // Act - var result = await _handler.Handle(query, CancellationToken.None); - - // Assert - result.Should().NotBeNull(); - result.Should().HaveCount(1); - result[0].PatientName.Should().Be("John Doe"); - result[0].PhysicianName.Should().Be("Jane Smith"); - } + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var query = new GetAppointmentsListQuery(); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(1); + result[0].PatientName.Should().Be("John Doe"); + result[0].PhysicianName.Should().Be("Jane Smith"); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs index da85d0f..b3014d9 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Appointments/Validators/AddAppointmentCommandValidatorTests.cs @@ -4,140 +4,135 @@ using FluentValidation.TestHelper; using Moq; using Moq.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; using FluentAssertions; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Validators +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Appointments.Validators; + +public class AddAppointmentCommandValidatorTests { - public class AddAppointmentCommandValidatorTests + private readonly Mock _contextMock; + + public AddAppointmentCommandValidatorTests() { - private readonly Mock _contextMock; + _contextMock = new Mock(); + } - public AddAppointmentCommandValidatorTests() - { - _contextMock = new Mock(); - } + [Fact] + public async Task Should_Not_Have_Error_When_Appointment_Is_Valid() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldNotHaveAnyValidationErrors(); + } - [Fact] - public async Task Should_Not_Have_Error_When_Appointment_Is_Valid() - { - // Arrange - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); - _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); - - var validator = new AddAppointmentCommandValidator(_contextMock.Object); - var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1) }; - - // Act - var result = await validator.TestValidateAsync(command); - - // Assert - result.ShouldNotHaveAnyValidationErrors(); - } - - [Fact] - public async Task Should_Have_Error_When_Physician_Has_Conflict() - { - // Arrange - var appointmentTime = DateTime.Now.AddDays(1); - var existingAppointments = new List - { - new Appointment { PhysicianId = 1, AppointmentDateTime = appointmentTime } - }; - - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); - _contextMock.Setup(x => x.Appointments).ReturnsDbSet(existingAppointments); - - var validator = new AddAppointmentCommandValidator(_contextMock.Object); - var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = appointmentTime }; - - // Act - var result = await validator.TestValidateAsync(command); - - // Assert - result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); - } - - [Fact] - public async Task Should_Have_Error_When_Patient_Has_Conflict() - { - // Arrange - var appointmentTime = DateTime.Now.AddDays(1); - var existingAppointments = new List - { - new Appointment { PatientId = 1, AppointmentDateTime = appointmentTime } - }; - - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); - _contextMock.Setup(x => x.Appointments).ReturnsDbSet(existingAppointments); - - var validator = new AddAppointmentCommandValidator(_contextMock.Object); - var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = appointmentTime }; - - // Act - var result = await validator.TestValidateAsync(command); - - // Assert - result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); - } - - [Fact] - public async Task Should_Have_Error_When_Date_Is_In_The_Past() - { - // Arrange - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); - _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); - var validator = new AddAppointmentCommandValidator(_contextMock.Object); - var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(-1) }; - - // Act - var result = await validator.TestValidateAsync(command); - - // Assert - result.ShouldHaveValidationErrorFor(cmd => cmd.AppointmentDateTime); - } - - [Fact] - public async Task Should_Have_Error_When_PatientId_Does_Not_Exist() + [Fact] + public async Task Should_Have_Error_When_Physician_Has_Conflict() + { + // Arrange + var appointmentTime = DateTime.Now.AddDays(1); + var existingAppointments = new List { - // Arrange - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List()); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); - _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); - var validator = new AddAppointmentCommandValidator(_contextMock.Object); - var command = new AddAppointmentCommand { PatientId = 99, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1) }; - - // Act - var result = await validator.TestValidateAsync(command); - - // Assert - result.ShouldHaveValidationErrorFor(cmd => cmd.PatientId) - .WithErrorMessage("De geselecteerde patiënt bestaat niet."); - } - - [Fact] - public async Task Should_Have_Error_When_PhysicianId_Does_Not_Exist() + new Appointment { PhysicianId = 1, AppointmentDateTime = appointmentTime } + }; + + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(existingAppointments); + + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = appointmentTime }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); + } + + [Fact] + public async Task Should_Have_Error_When_Patient_Has_Conflict() + { + // Arrange + var appointmentTime = DateTime.Now.AddDays(1); + var existingAppointments = new List { - // Arrange - _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List()); - _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); - var validator = new AddAppointmentCommandValidator(_contextMock.Object); - var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 99, AppointmentDateTime = DateTime.Now.AddDays(1) }; - - // Act - var result = await validator.TestValidateAsync(command); - - // Assert - result.ShouldHaveValidationErrorFor(cmd => cmd.PhysicianId) - .WithErrorMessage("De geselecteerde arts bestaat niet."); - } + new Appointment { PatientId = 1, AppointmentDateTime = appointmentTime } + }; + + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(existingAppointments); + + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = appointmentTime }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.Errors.Should().Contain(e => e.ErrorMessage == "De arts of patiënt heeft op dit tijdstip al een andere afspraak."); + } + + [Fact] + public async Task Should_Have_Error_When_Date_Is_In_The_Past() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(-1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldHaveValidationErrorFor(cmd => cmd.AppointmentDateTime); + } + + [Fact] + public async Task Should_Have_Error_When_PatientId_Does_Not_Exist() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List()); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List { new Physician { Id = 1 } }); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 99, PhysicianId = 1, AppointmentDateTime = DateTime.Now.AddDays(1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldHaveValidationErrorFor(cmd => cmd.PatientId) + .WithErrorMessage("De geselecteerde patiënt bestaat niet."); + } + + [Fact] + public async Task Should_Have_Error_When_PhysicianId_Does_Not_Exist() + { + // Arrange + _contextMock.Setup(x => x.Patients).ReturnsDbSet(new List { new Patient { Id = 1 } }); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List()); + _contextMock.Setup(x => x.Appointments).ReturnsDbSet(new List()); + var validator = new AddAppointmentCommandValidator(_contextMock.Object); + var command = new AddAppointmentCommand { PatientId = 1, PhysicianId = 99, AppointmentDateTime = DateTime.Now.AddDays(1) }; + + // Act + var result = await validator.TestValidateAsync(command); + + // Assert + result.ShouldHaveValidationErrorFor(cmd => cmd.PhysicianId) + .WithErrorMessage("De geselecteerde arts bestaat niet."); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs index 85bc50a..db75536 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/AddPatientCommandHandlerTests.cs @@ -3,54 +3,53 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Moq; using Moq.EntityFrameworkCore; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands; + +public class AddPatientCommandHandlerTests { - public class AddPatientCommandHandlerTests + private readonly Mock _contextMock; + private readonly AddPatientCommandHandler _handler; + + public AddPatientCommandHandlerTests() { - private readonly Mock _contextMock; - private readonly AddPatientCommandHandler _handler; + _contextMock = new Mock(); + _handler = new AddPatientCommandHandler(_contextMock.Object); + } - public AddPatientCommandHandlerTests() - { - _contextMock = new Mock(); - _handler = new AddPatientCommandHandler(_contextMock.Object); - } + [Fact] + public async Task Handle_Should_Add_Patient_And_Save_Changes() + { + // Arrange + var patients = new List(); + _contextMock.Setup(c => c.Patients).ReturnsDbSet(patients); - [Fact] - public async Task Handle_Should_Add_Patient_And_Save_Changes() + var command = new AddPatientCommand { - // Arrange - var patients = new List(); - _contextMock.Setup(c => c.Patients).ReturnsDbSet(patients); - - var command = new AddPatientCommand - { - FirstName = "John", - LastName = "Doe", - BSN = "123456789", - Address = "123 Main St", - PhoneNumber = "555-1234", - Email = "john.doe@test.com", - DateOfBirth = new System.DateTime(1990, 1, 1) - }; + FirstName = "John", + LastName = "Doe", + BSN = "123456789", + Address = "123 Main St", + PhoneNumber = "555-1234", + Email = "john.doe@test.com", + DateOfBirth = new System.DateTime(1990, 1, 1) + }; - // Act - await _handler.Handle(command, CancellationToken.None); + // Act + await _handler.Handle(command, CancellationToken.None); - // Assert - _contextMock.Verify( - x => x.Patients.Add(It.Is(p => - p.FirstName == command.FirstName && - p.LastName == command.LastName && - p.BSN == command.BSN && - p.Address == command.Address && - p.PhoneNumber == command.PhoneNumber && - p.Email == command.Email && - p.DateOfBirth == command.DateOfBirth - )), - Times.Once); + // Assert + _contextMock.Verify( + x => x.Patients.Add(It.Is(p => + p.FirstName == command.FirstName && + p.LastName == command.LastName && + p.BSN == command.BSN && + p.Address == command.Address && + p.PhoneNumber == command.PhoneNumber && + p.Email == command.Email && + p.DateOfBirth == command.DateOfBirth + )), + Times.Once); - _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); - } + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs index 0c2ea51..a437d7f 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Commands/DeletePatientCommandHandlerTests.cs @@ -3,91 +3,85 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; using FluentAssertions; -using System; using Microsoft.EntityFrameworkCore; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Commands; + +public class DeletePatientCommandHandlerTests { - public class DeletePatientCommandHandlerTests + private readonly Mock _contextMock; + + public DeletePatientCommandHandlerTests() + { + _contextMock = new Mock(); + } + + [Fact] + public async Task Handle_Should_Remove_Patient_When_Found() + { + // Arrange + var patient = new Patient { Id = 1, FirstName = "Test", LastName = "Patient" }; + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(patient); + + _contextMock.Setup(c => c.Patients).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePatientCommandHandler(_contextMock.Object); + var command = new DeletePatientCommand { Id = 1 }; + + // Act + await handler.Handle(command, CancellationToken.None); + + // Assert + mockDbSet.Verify(x => x.Remove(patient), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Patient_Not_Found() { - private readonly Mock _contextMock; + // Arrange + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 99 }, It.IsAny())) + .ReturnsAsync((Patient)null); + + _contextMock.Setup(c => c.Patients).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePatientCommandHandler(_contextMock.Object); + var command = new DeletePatientCommand { Id = 99 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Patient with id 99 not found"); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Patient_Has_Appointments() + { + // Arrange + var patient = new Patient { Id = 1, FirstName = "Test", LastName = "Patient" }; + var appointments = new List { new Appointment { PatientId = 1 } }; - public DeletePatientCommandHandlerTests() - { - _contextMock = new Mock(); - } - - [Fact] - public async Task Handle_Should_Remove_Patient_When_Found() - { - // Arrange - var patient = new Patient { Id = 1, FirstName = "Test", LastName = "Patient" }; - var mockDbSet = new Mock>(); - mockDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) - .ReturnsAsync(patient); - - _contextMock.Setup(c => c.Patients).Returns(mockDbSet.Object); - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); - - var handler = new DeletePatientCommandHandler(_contextMock.Object); - var command = new DeletePatientCommand { Id = 1 }; - - // Act - await handler.Handle(command, CancellationToken.None); - - // Assert - mockDbSet.Verify(x => x.Remove(patient), Times.Once); - _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); - } - - [Fact] - public async Task Handle_Should_Throw_Exception_When_Patient_Not_Found() - { - // Arrange - var mockDbSet = new Mock>(); - mockDbSet.Setup(m => m.FindAsync(new object[] { 99 }, It.IsAny())) - .ReturnsAsync((Patient)null); - - _contextMock.Setup(c => c.Patients).Returns(mockDbSet.Object); - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); - - var handler = new DeletePatientCommandHandler(_contextMock.Object); - var command = new DeletePatientCommand { Id = 99 }; - - // Act - Func act = async () => await handler.Handle(command, CancellationToken.None); - - // Assert - await act.Should().ThrowAsync().WithMessage("Patient with id 99 not found"); - } - - [Fact] - public async Task Handle_Should_Throw_Exception_When_Patient_Has_Appointments() - { - // Arrange - var patient = new Patient { Id = 1, FirstName = "Test", LastName = "Patient" }; - var appointments = new List { new Appointment { PatientId = 1 } }; - - var mockPatientDbSet = new Mock>(); - mockPatientDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) - .ReturnsAsync(patient); - - _contextMock.Setup(c => c.Patients).Returns(mockPatientDbSet.Object); - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); - - var handler = new DeletePatientCommandHandler(_contextMock.Object); - var command = new DeletePatientCommand { Id = 1 }; - - // Act - Func act = async () => await handler.Handle(command, CancellationToken.None); - - // Assert - await act.Should().ThrowAsync().WithMessage("Cannot delete patient with active appointments."); - } + var mockPatientDbSet = new Mock>(); + mockPatientDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(patient); + + _contextMock.Setup(c => c.Patients).Returns(mockPatientDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var handler = new DeletePatientCommandHandler(_contextMock.Object); + var command = new DeletePatientCommand { Id = 1 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Cannot delete patient with active appointments."); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs index 9bf865d..ac6711f 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Patients/Queries/GetPatientsListQueryHandlerTests.cs @@ -4,46 +4,41 @@ using FluentAssertions; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Queries +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Patients.Queries; + +public class GetPatientsListQueryHandlerTests { - public class GetPatientsListQueryHandlerTests + private readonly Mock _contextMock; + private readonly GetPatientsListQueryHandler _handler; + + public GetPatientsListQueryHandlerTests() { - private readonly Mock _contextMock; - private readonly GetPatientsListQueryHandler _handler; + _contextMock = new Mock(); + _handler = new GetPatientsListQueryHandler(_contextMock.Object); + } - public GetPatientsListQueryHandlerTests() + [Fact] + public async Task Handle_Should_Return_All_Patients_As_Dtos() + { + // Arrange + var patients = new List { - _contextMock = new Mock(); - _handler = new GetPatientsListQueryHandler(_contextMock.Object); - } + new Patient { Id = 1, FirstName = "John", LastName = "Doe", BSN = "123456789" }, + new Patient { Id = 2, FirstName = "Jane", LastName = "Smith", BSN = "987654321" } + }; - [Fact] - public async Task Handle_Should_Return_All_Patients_As_Dtos() - { - // Arrange - var patients = new List - { - new Patient { Id = 1, FirstName = "John", LastName = "Doe", BSN = "123456789" }, - new Patient { Id = 2, FirstName = "Jane", LastName = "Smith", BSN = "987654321" } - }; - - _contextMock.Setup(c => c.Patients).ReturnsDbSet(patients); - - var query = new GetPatientsListQuery(); - - // Act - var result = await _handler.Handle(query, CancellationToken.None); - - // Assert - result.Should().NotBeNull(); - result.Should().HaveCount(2); - result[0].Name.Should().Be("John Doe"); - result[1].BSN.Should().Be("987654321"); - } + _contextMock.Setup(c => c.Patients).ReturnsDbSet(patients); + + var query = new GetPatientsListQuery(); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result[0].Name.Should().Be("John Doe"); + result[1].BSN.Should().Be("987654321"); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs index 672b6b8..773dc69 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/AddPhysicianCommandHandlerTests.cs @@ -3,43 +3,38 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Commands; + +public class AddPhysicianCommandHandlerTests { - public class AddPhysicianCommandHandlerTests + private readonly Mock _contextMock; + private readonly AddPhysicianCommandHandler _handler; + + public AddPhysicianCommandHandlerTests() { - private readonly Mock _contextMock; - private readonly AddPhysicianCommandHandler _handler; + _contextMock = new Mock(); + _handler = new AddPhysicianCommandHandler(_contextMock.Object); + } - public AddPhysicianCommandHandlerTests() - { - _contextMock = new Mock(); - _handler = new AddPhysicianCommandHandler(_contextMock.Object); - } + [Fact] + public async Task Handle_Should_Add_Physician_And_Save_Changes() + { + // Arrange + var physicians = new List(); + _contextMock.Setup(c => c.Physicians).ReturnsDbSet(physicians); - [Fact] - public async Task Handle_Should_Add_Physician_And_Save_Changes() + var command = new AddPhysicianCommand { - // Arrange - var physicians = new List(); - _contextMock.Setup(c => c.Physicians).ReturnsDbSet(physicians); - - var command = new AddPhysicianCommand - { - FirstName = "Test", - LastName = "Physician" - }; + FirstName = "Test", + LastName = "Physician" + }; - // Act - await _handler.Handle(command, CancellationToken.None); + // Act + await _handler.Handle(command, CancellationToken.None); - // Assert - _contextMock.Verify(x => x.Physicians.Add(It.IsAny()), Times.Once); - _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); - } + // Assert + _contextMock.Verify(x => x.Physicians.Add(It.IsAny()), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs index 3023959..dc21b5b 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Commands/DeletePhysicianCommandHandlerTests.cs @@ -3,92 +3,86 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; using FluentAssertions; -using System; using Microsoft.EntityFrameworkCore; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Commands; + +public class DeletePhysicianCommandHandlerTests { - public class DeletePhysicianCommandHandlerTests + private readonly Mock _contextMock; + + public DeletePhysicianCommandHandlerTests() + { + _contextMock = new Mock(); + } + + [Fact] + public async Task Handle_Should_Remove_Physician_When_Found() + { + // Arrange + var physician = new Physician { Id = 1, FirstName = "Test", LastName = "Physician" }; + var physicians = new List { physician }; + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(physician); + + _contextMock.Setup(c => c.Physicians).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePhysicianCommandHandler(_contextMock.Object); + var command = new DeletePhysicianCommand { Id = 1 }; + + // Act + await handler.Handle(command, CancellationToken.None); + + // Assert + mockDbSet.Verify(x => x.Remove(physician), Times.Once); + _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Physician_Not_Found() { - private readonly Mock _contextMock; + // Arrange + var mockDbSet = new Mock>(); + mockDbSet.Setup(m => m.FindAsync(new object[] { 99 }, It.IsAny())) + .ReturnsAsync((Physician)null); + + _contextMock.Setup(c => c.Physicians).Returns(mockDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); + + var handler = new DeletePhysicianCommandHandler(_contextMock.Object); + var command = new DeletePhysicianCommand { Id = 99 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Physician with id 99 not found"); + } + + [Fact] + public async Task Handle_Should_Throw_Exception_When_Physician_Has_Appointments() + { + // Arrange + var physician = new Physician { Id = 1, FirstName = "Test", LastName = "Physician" }; + var appointments = new List { new Appointment { PhysicianId = 1 } }; - public DeletePhysicianCommandHandlerTests() - { - _contextMock = new Mock(); - } - - [Fact] - public async Task Handle_Should_Remove_Physician_When_Found() - { - // Arrange - var physician = new Physician { Id = 1, FirstName = "Test", LastName = "Physician" }; - var physicians = new List { physician }; - var mockDbSet = new Mock>(); - mockDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) - .ReturnsAsync(physician); - - _contextMock.Setup(c => c.Physicians).Returns(mockDbSet.Object); - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); - - var handler = new DeletePhysicianCommandHandler(_contextMock.Object); - var command = new DeletePhysicianCommand { Id = 1 }; - - // Act - await handler.Handle(command, CancellationToken.None); - - // Assert - mockDbSet.Verify(x => x.Remove(physician), Times.Once); - _contextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Once); - } - - [Fact] - public async Task Handle_Should_Throw_Exception_When_Physician_Not_Found() - { - // Arrange - var mockDbSet = new Mock>(); - mockDbSet.Setup(m => m.FindAsync(new object[] { 99 }, It.IsAny())) - .ReturnsAsync((Physician)null); - - _contextMock.Setup(c => c.Physicians).Returns(mockDbSet.Object); - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(new List()); - - var handler = new DeletePhysicianCommandHandler(_contextMock.Object); - var command = new DeletePhysicianCommand { Id = 99 }; - - // Act - Func act = async () => await handler.Handle(command, CancellationToken.None); - - // Assert - await act.Should().ThrowAsync().WithMessage("Physician with id 99 not found"); - } - - [Fact] - public async Task Handle_Should_Throw_Exception_When_Physician_Has_Appointments() - { - // Arrange - var physician = new Physician { Id = 1, FirstName = "Test", LastName = "Physician" }; - var appointments = new List { new Appointment { PhysicianId = 1 } }; - - var mockPhysicianDbSet = new Mock>(); - mockPhysicianDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) - .ReturnsAsync(physician); - - _contextMock.Setup(c => c.Physicians).Returns(mockPhysicianDbSet.Object); - _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); - - var handler = new DeletePhysicianCommandHandler(_contextMock.Object); - var command = new DeletePhysicianCommand { Id = 1 }; - - // Act - Func act = async () => await handler.Handle(command, CancellationToken.None); - - // Assert - await act.Should().ThrowAsync().WithMessage("Cannot delete physician with active appointments."); - } + var mockPhysicianDbSet = new Mock>(); + mockPhysicianDbSet.Setup(m => m.FindAsync(new object[] { 1 }, It.IsAny())) + .ReturnsAsync(physician); + + _contextMock.Setup(c => c.Physicians).Returns(mockPhysicianDbSet.Object); + _contextMock.Setup(c => c.Appointments).ReturnsDbSet(appointments); + + var handler = new DeletePhysicianCommandHandler(_contextMock.Object); + var command = new DeletePhysicianCommand { Id = 1 }; + + // Act + Func act = async () => await handler.Handle(command, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync().WithMessage("Cannot delete physician with active appointments."); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs index 815bebd..7c5a50b 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Queries/GetPhysiciansListQueryHandlerTests.cs @@ -4,46 +4,41 @@ using FluentAssertions; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Queries +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Queries; + +public class GetPhysiciansListQueryHandlerTests { - public class GetPhysiciansListQueryHandlerTests + private readonly Mock _contextMock; + private readonly GetPhysiciansListQueryHandler _handler; + + public GetPhysiciansListQueryHandlerTests() { - private readonly Mock _contextMock; - private readonly GetPhysiciansListQueryHandler _handler; + _contextMock = new Mock(); + _handler = new GetPhysiciansListQueryHandler(_contextMock.Object); + } - public GetPhysiciansListQueryHandlerTests() + [Fact] + public async Task Handle_Should_Return_All_Physicians_As_Dtos() + { + // Arrange + var physicians = new List { - _contextMock = new Mock(); - _handler = new GetPhysiciansListQueryHandler(_contextMock.Object); - } + new Physician { Id = 1, FirstName = "Dr.", LastName = "Who" }, + new Physician { Id = 2, FirstName = "Dr.", LastName = "Strange" } + }; - [Fact] - public async Task Handle_Should_Return_All_Physicians_As_Dtos() - { - // Arrange - var physicians = new List - { - new Physician { Id = 1, FirstName = "Dr.", LastName = "Who" }, - new Physician { Id = 2, FirstName = "Dr.", LastName = "Strange" } - }; - - _contextMock.Setup(c => c.Physicians).ReturnsDbSet(physicians); - - var query = new GetPhysiciansListQuery(); - - // Act - var result = await _handler.Handle(query, CancellationToken.None); - - // Assert - result.Should().NotBeNull(); - result.Should().HaveCount(2); - result[0].Name.Should().Be("Dr. Who"); - result[1].Name.Should().Be("Dr. Strange"); - } + _contextMock.Setup(c => c.Physicians).ReturnsDbSet(physicians); + + var query = new GetPhysiciansListQuery(); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.Should().NotBeNull(); + result.Should().HaveCount(2); + result[0].Name.Should().Be("Dr. Who"); + result[1].Name.Should().Be("Dr. Strange"); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs index 5f56f25..601a0e7 100644 --- a/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs +++ b/Chipsoft.Assignments.EPDConsole.Application.Tests/Physicians/Validators/AddPhysicianCommandValidatorTests.cs @@ -4,82 +4,78 @@ using FluentValidation.TestHelper; using Moq; using Moq.EntityFrameworkCore; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; using FluentAssertions; -namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Validators +namespace Chipsoft.Assignments.EPDConsole.Application.Tests.Physicians.Validators; + +public class AddPhysicianCommandValidatorTests { - public class AddPhysicianCommandValidatorTests - { - private readonly Mock _contextMock; - private readonly AddPhysicianCommandValidator _validator; + private readonly Mock _contextMock; + private readonly AddPhysicianCommandValidator _validator; - public AddPhysicianCommandValidatorTests() - { - _contextMock = new Mock(); - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List()); - _validator = new AddPhysicianCommandValidator(_contextMock.Object); - } + public AddPhysicianCommandValidatorTests() + { + _contextMock = new Mock(); + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(new List()); + _validator = new AddPhysicianCommandValidator(_contextMock.Object); + } - [Fact] - public async Task Should_Have_Error_When_FirstName_Is_Empty() - { - var command = new AddPhysicianCommand { FirstName = string.Empty, LastName = "Test" }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.FirstName); - } + [Fact] + public async Task Should_Have_Error_When_FirstName_Is_Empty() + { + var command = new AddPhysicianCommand { FirstName = string.Empty, LastName = "Test" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.FirstName); + } - [Fact] - public async Task Should_Have_Error_When_LastName_Is_Empty() - { - var command = new AddPhysicianCommand { FirstName = "Test", LastName = string.Empty }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.LastName); - } + [Fact] + public async Task Should_Have_Error_When_LastName_Is_Empty() + { + var command = new AddPhysicianCommand { FirstName = "Test", LastName = string.Empty }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.LastName); + } - [Fact] - public async Task Should_Have_Error_When_FirstName_Exceeds_MaxLength() - { - var command = new AddPhysicianCommand { FirstName = new string('a', 201), LastName = "Test" }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.FirstName); - } + [Fact] + public async Task Should_Have_Error_When_FirstName_Exceeds_MaxLength() + { + var command = new AddPhysicianCommand { FirstName = new string('a', 201), LastName = "Test" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.FirstName); + } - [Fact] - public async Task Should_Have_Error_When_LastName_Exceeds_MaxLength() - { - var command = new AddPhysicianCommand { FirstName = "Test", LastName = new string('a', 201) }; - var result = await _validator.TestValidateAsync(command); - result.ShouldHaveValidationErrorFor(x => x.LastName); - } + [Fact] + public async Task Should_Have_Error_When_LastName_Exceeds_MaxLength() + { + var command = new AddPhysicianCommand { FirstName = "Test", LastName = new string('a', 201) }; + var result = await _validator.TestValidateAsync(command); + result.ShouldHaveValidationErrorFor(x => x.LastName); + } - [Fact] - public async Task Should_Not_Have_Error_When_Name_Is_Unique() - { - var command = new AddPhysicianCommand { FirstName = "Test", LastName = "Physician" }; - var result = await _validator.TestValidateAsync(command); - result.ShouldNotHaveAnyValidationErrors(); - } + [Fact] + public async Task Should_Not_Have_Error_When_Name_Is_Unique() + { + var command = new AddPhysicianCommand { FirstName = "Test", LastName = "Physician" }; + var result = await _validator.TestValidateAsync(command); + result.ShouldNotHaveAnyValidationErrors(); + } - [Fact] - public async Task Should_Have_Error_When_Name_Is_Not_Unique() + [Fact] + public async Task Should_Have_Error_When_Name_Is_Not_Unique() + { + // Arrange + var existingPhysicians = new List { - // Arrange - var existingPhysicians = new List - { - new Physician { FirstName = "Test", LastName = "Physician" } - }; - _contextMock.Setup(x => x.Physicians).ReturnsDbSet(existingPhysicians); - var validator = new AddPhysicianCommandValidator(_contextMock.Object); - var command = new AddPhysicianCommand { FirstName = "Test", LastName = "Physician" }; + new Physician { FirstName = "Test", LastName = "Physician" } + }; + _contextMock.Setup(x => x.Physicians).ReturnsDbSet(existingPhysicians); + var validator = new AddPhysicianCommandValidator(_contextMock.Object); + var command = new AddPhysicianCommand { FirstName = "Test", LastName = "Physician" }; - // Act - var result = await validator.TestValidateAsync(command); + // Act + var result = await validator.TestValidateAsync(command); - // Assert - result.Errors.Should().Contain(e => e.ErrorMessage == "Een arts met deze naam bestaat al."); - } + // Assert + result.Errors.Should().Contain(e => e.ErrorMessage == "Een arts met deze naam bestaat al."); } -} \ No newline at end of file +} From 04f72ab42a193ea042b9b048cb7a3ea566018423 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 14:36:52 +0200 Subject: [PATCH 22/23] =?UTF-8?q?Herstructureer=20en=20vereenvoudig=20de?= =?UTF-8?q?=20command=20handlers=20en=20validators=20voor=20afspraken,=20p?= =?UTF-8?q?ati=C3=ABnten=20en=20artsen;=20verwijder=20overbodige=20code=20?= =?UTF-8?q?en=20verbeter=20de=20leesbaarheid.=20Voeg=20validatieregels=20t?= =?UTF-8?q?oe=20voor=20het=20toevoegen=20van=20afspraken=20en=20pati=C3=AB?= =?UTF-8?q?nten,=20inclusief=20controles=20op=20bestaande=20records=20en?= =?UTF-8?q?=20toekomstige=20datums.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/AddAppointmentCommand.cs | 52 ++++----- .../AddAppointmentCommandValidator.cs | 108 +++++++++--------- .../Appointments/Dtos/AppointmentDto.cs | 15 ++- .../Queries/GetAppointmentsByPatientQuery.cs | 57 +++++---- .../GetAppointmentsByPhysicianQuery.cs | 57 +++++---- .../Queries/GetAppointmentsListQuery.cs | 53 ++++----- .../Common/Behaviors/ValidationBehavior.cs | 50 ++++---- .../Common/Exceptions/ValidationException.cs | 36 +++--- .../Patients/Commands/AddPatientCommand.cs | 68 ++++++----- .../Commands/AddPatientCommandValidator.cs | 92 +++++++-------- .../Patients/Commands/DeletePatientCommand.cs | 51 +++------ .../Patients/Queries/GetPatientsListQuery.cs | 58 ++++------ .../Commands/AddPhysicianCommand.cs | 46 +++----- .../Commands/AddPhysicianCommandValidator.cs | 47 ++++---- .../Commands/DeletePhysicianCommand.cs | 53 ++++----- .../Queries/GetPhysiciansListQuery.cs | 54 ++++----- 16 files changed, 396 insertions(+), 501 deletions(-) diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs index 7d9f577..ccb6ed9 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommand.cs @@ -1,40 +1,36 @@ using MediatR; -using System; -using System.Threading; -using System.Threading.Tasks; using Chipsoft.Assignments.EPDConsole.Core.Entities; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; -namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands; + +public class AddAppointmentCommand : IRequest +{ + public int PatientId { get; set; } + public int PhysicianId { get; set; } + public DateTime AppointmentDateTime { get; set; } +} + +public class AddAppointmentCommandHandler : IRequestHandler { - public class AddAppointmentCommand : IRequest + private readonly IApplicationDbContext _context; + + public AddAppointmentCommandHandler(IApplicationDbContext context) { - public int PatientId { get; set; } - public int PhysicianId { get; set; } - public DateTime AppointmentDateTime { get; set; } + _context = context; } - public class AddAppointmentCommandHandler : IRequestHandler + public async Task Handle(AddAppointmentCommand request, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public AddAppointmentCommandHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task Handle(AddAppointmentCommand request, CancellationToken cancellationToken) + var appointment = new Appointment { - var appointment = new Appointment - { - PatientId = request.PatientId, - PhysicianId = request.PhysicianId, - AppointmentDateTime = request.AppointmentDateTime - }; + PatientId = request.PatientId, + PhysicianId = request.PhysicianId, + AppointmentDateTime = request.AppointmentDateTime + }; - _context.Appointments.Add(appointment); - await _context.SaveChangesAsync(cancellationToken); - return appointment.Id; - } + _context.Appointments.Add(appointment); + await _context.SaveChangesAsync(cancellationToken); + return appointment.Id; } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs index a9912e0..4085fc6 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Commands/AddAppointmentCommandValidator.cs @@ -1,63 +1,59 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using FluentValidation; using Microsoft.EntityFrameworkCore; -using System; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Commands; + +public class AddAppointmentCommandValidator : AbstractValidator { - public class AddAppointmentCommandValidator : AbstractValidator + private readonly IApplicationDbContext _context; + + public AddAppointmentCommandValidator(IApplicationDbContext context) + { + _context = context; + + RuleFor(v => v.AppointmentDateTime) + .GreaterThan(DateTime.Now).WithMessage("Een afspraak moet in de toekomst worden gepland."); + + RuleFor(v => v.PatientId) + .NotEmpty().WithMessage("Patiënt ID is verplicht.") + .MustAsync(PatientExists).WithMessage("De geselecteerde patiënt bestaat niet."); + + RuleFor(v => v.PhysicianId) + .NotEmpty().WithMessage("Arts ID is verplicht.") + .MustAsync(PhysicianExists).WithMessage("De geselecteerde arts bestaat niet."); + + RuleFor(v => v) + .MustAsync(BeAvailable).WithMessage("De arts of patiënt heeft op dit tijdstip al een andere afspraak."); + } + + private async Task PatientExists(int id, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public AddAppointmentCommandValidator(IApplicationDbContext context) - { - _context = context; - - RuleFor(v => v.AppointmentDateTime) - .GreaterThan(DateTime.Now).WithMessage("Een afspraak moet in de toekomst worden gepland."); - - RuleFor(v => v.PatientId) - .NotEmpty().WithMessage("Patiënt ID is verplicht.") - .MustAsync(PatientExists).WithMessage("De geselecteerde patiënt bestaat niet."); - - RuleFor(v => v.PhysicianId) - .NotEmpty().WithMessage("Arts ID is verplicht.") - .MustAsync(PhysicianExists).WithMessage("De geselecteerde arts bestaat niet."); - - RuleFor(v => v) - .MustAsync(BeAvailable).WithMessage("De arts of patiënt heeft op dit tijdstip al een andere afspraak."); - } - - private async Task PatientExists(int id, CancellationToken cancellationToken) - { - return await _context.Patients.AnyAsync(p => p.Id == id, cancellationToken); - } - - private async Task PhysicianExists(int id, CancellationToken cancellationToken) - { - return await _context.Physicians.AnyAsync(p => p.Id == id, cancellationToken); - } - - private async Task BeAvailable(AddAppointmentCommand command, CancellationToken cancellationToken) - { - var appointmentTime = command.AppointmentDateTime; - var appointmentEndTime = appointmentTime.AddMinutes(30); // Assuming 30 minute slots - - var physicianHasConflict = await _context.Appointments - .AnyAsync(a => a.PhysicianId == command.PhysicianId && - appointmentTime < a.AppointmentDateTime.AddMinutes(30) && - appointmentEndTime > a.AppointmentDateTime, cancellationToken); - - if (physicianHasConflict) return false; - - var patientHasConflict = await _context.Appointments - .AnyAsync(a => a.PatientId == command.PatientId && - appointmentTime < a.AppointmentDateTime.AddMinutes(30) && - appointmentEndTime > a.AppointmentDateTime, cancellationToken); - - return !patientHasConflict; - } + return await _context.Patients.AnyAsync(p => p.Id == id, cancellationToken); + } + + private async Task PhysicianExists(int id, CancellationToken cancellationToken) + { + return await _context.Physicians.AnyAsync(p => p.Id == id, cancellationToken); + } + + private async Task BeAvailable(AddAppointmentCommand command, CancellationToken cancellationToken) + { + var appointmentTime = command.AppointmentDateTime; + var appointmentEndTime = appointmentTime.AddMinutes(30); // Assuming 30 minute slots + + var physicianHasConflict = await _context.Appointments + .AnyAsync(a => a.PhysicianId == command.PhysicianId && + appointmentTime < a.AppointmentDateTime.AddMinutes(30) && + appointmentEndTime > a.AppointmentDateTime, cancellationToken); + + if (physicianHasConflict) return false; + + var patientHasConflict = await _context.Appointments + .AnyAsync(a => a.PatientId == command.PatientId && + appointmentTime < a.AppointmentDateTime.AddMinutes(30) && + appointmentEndTime > a.AppointmentDateTime, cancellationToken); + + return !patientHasConflict; } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs index 89465c0..0908fe7 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Dtos/AppointmentDto.cs @@ -1,9 +1,8 @@ -namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Dtos +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Dtos; + +public class AppointmentDto { - public class AppointmentDto - { - public DateTime AppointmentDateTime { get; set; } - public string PatientName { get; set; } = string.Empty; - public string PhysicianName { get; set; } = string.Empty; - } -} \ No newline at end of file + public DateTime AppointmentDateTime { get; set; } + public string PatientName { get; set; } = string.Empty; + public string PhysicianName { get; set; } = string.Empty; +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs index df4f11f..65a3d1e 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPatientQuery.cs @@ -2,41 +2,36 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries; + +public class GetAppointmentsByPatientQuery : IRequest> +{ + public int PatientId { get; set; } +} + +public class GetAppointmentsByPatientQueryHandler : IRequestHandler> { - public class GetAppointmentsByPatientQuery : IRequest> + private readonly IApplicationDbContext _context; + + public GetAppointmentsByPatientQueryHandler(IApplicationDbContext context) { - public int PatientId { get; set; } + _context = context; } - public class GetAppointmentsByPatientQueryHandler : IRequestHandler> + public async Task> Handle(GetAppointmentsByPatientQuery request, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public GetAppointmentsByPatientQueryHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task> Handle(GetAppointmentsByPatientQuery request, CancellationToken cancellationToken) - { - return await _context.Appointments - .Where(a => a.PatientId == request.PatientId) - .Include(a => a.Patient) - .Include(a => a.Physician) - .OrderBy(a => a.AppointmentDateTime) - .Select(a => new AppointmentDto - { - AppointmentDateTime = a.AppointmentDateTime, - PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", - PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" - }) - .ToListAsync(cancellationToken); - } + return await _context.Appointments + .Where(a => a.PatientId == request.PatientId) + .Include(a => a.Patient) + .Include(a => a.Physician) + .OrderBy(a => a.AppointmentDateTime) + .Select(a => new AppointmentDto + { + AppointmentDateTime = a.AppointmentDateTime, + PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", + PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" + }) + .ToListAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs index f3286ed..6a6ef49 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsByPhysicianQuery.cs @@ -2,41 +2,36 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries; + +public class GetAppointmentsByPhysicianQuery : IRequest> +{ + public int PhysicianId { get; set; } +} + +public class GetAppointmentsByPhysicianQueryHandler : IRequestHandler> { - public class GetAppointmentsByPhysicianQuery : IRequest> + private readonly IApplicationDbContext _context; + + public GetAppointmentsByPhysicianQueryHandler(IApplicationDbContext context) { - public int PhysicianId { get; set; } + _context = context; } - public class GetAppointmentsByPhysicianQueryHandler : IRequestHandler> + public async Task> Handle(GetAppointmentsByPhysicianQuery request, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public GetAppointmentsByPhysicianQueryHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task> Handle(GetAppointmentsByPhysicianQuery request, CancellationToken cancellationToken) - { - return await _context.Appointments - .Where(a => a.PhysicianId == request.PhysicianId) - .Include(a => a.Patient) - .Include(a => a.Physician) - .OrderBy(a => a.AppointmentDateTime) - .Select(a => new AppointmentDto - { - AppointmentDateTime = a.AppointmentDateTime, - PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", - PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" - }) - .ToListAsync(cancellationToken); - } + return await _context.Appointments + .Where(a => a.PhysicianId == request.PhysicianId) + .Include(a => a.Patient) + .Include(a => a.Physician) + .OrderBy(a => a.AppointmentDateTime) + .Select(a => new AppointmentDto + { + AppointmentDateTime = a.AppointmentDateTime, + PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", + PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" + }) + .ToListAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs index df5d50d..d39deef 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Appointments/Queries/GetAppointmentsListQuery.cs @@ -2,39 +2,34 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries +namespace Chipsoft.Assignments.EPDConsole.Application.Appointments.Queries; + +public class GetAppointmentsListQuery : IRequest> +{ +} + +public class GetAppointmentsListQueryHandler : IRequestHandler> { - public class GetAppointmentsListQuery : IRequest> + private readonly IApplicationDbContext _context; + + public GetAppointmentsListQueryHandler(IApplicationDbContext context) { + _context = context; } - public class GetAppointmentsListQueryHandler : IRequestHandler> + public async Task> Handle(GetAppointmentsListQuery request, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public GetAppointmentsListQueryHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task> Handle(GetAppointmentsListQuery request, CancellationToken cancellationToken) - { - return await _context.Appointments - .Include(a => a.Patient) - .Include(a => a.Physician) - .OrderBy(a => a.AppointmentDateTime) - .Select(a => new AppointmentDto - { - AppointmentDateTime = a.AppointmentDateTime, - PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", - PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" - }) - .ToListAsync(cancellationToken); - } + return await _context.Appointments + .Include(a => a.Patient) + .Include(a => a.Physician) + .OrderBy(a => a.AppointmentDateTime) + .Select(a => new AppointmentDto + { + AppointmentDateTime = a.AppointmentDateTime, + PatientName = $"{a.Patient.FirstName} {a.Patient.LastName}", + PhysicianName = $"{a.Physician.FirstName} {a.Physician.LastName}" + }) + .ToListAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs b/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs index 54ae5f7..44cb4bd 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Common/Behaviors/ValidationBehavior.cs @@ -1,43 +1,33 @@ using FluentValidation; using MediatR; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using ValidationException = Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions.ValidationException; -namespace Chipsoft.Assignments.EPDConsole.Application.Common.Behaviors -{ - public class ValidationBehavior : IPipelineBehavior - where TRequest : IRequest - { - private readonly IEnumerable> _validators; +namespace Chipsoft.Assignments.EPDConsole.Application.Common.Behaviors; - public ValidationBehavior(IEnumerable> validators) - { - _validators = validators; - } +public class ValidationBehavior(IEnumerable> validators) : IPipelineBehavior + where TRequest : IRequest +{ + private readonly IEnumerable> _validators = validators; - public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + if (_validators.Any()) { - if (_validators.Any()) - { - var context = new ValidationContext(request); + var context = new ValidationContext(request); - var validationResults = await Task.WhenAll( - _validators.Select(v => - v.ValidateAsync(context, cancellationToken))); + var validationResults = await Task.WhenAll( + _validators.Select(v => + v.ValidateAsync(context, cancellationToken))); - var failures = validationResults - .SelectMany(r => r.Errors) - .Where(f => f != null) - .ToList(); + var failures = validationResults + .SelectMany(r => r.Errors) + .Where(f => f != null) + .ToList(); - if (failures.Count != 0) - throw new ValidationException(failures); - } - return await next(); + if (failures.Count != 0) + throw new ValidationException(failures); } + return await next(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs b/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs index 3f7a80c..7fad952 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Common/Exceptions/ValidationException.cs @@ -1,26 +1,22 @@ using FluentValidation.Results; -using System; -using System.Collections.Generic; -using System.Linq; -namespace Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions +namespace Chipsoft.Assignments.EPDConsole.Application.Common.Exceptions; + +public class ValidationException : Exception { - public class ValidationException : Exception + public ValidationException() + : base("One or more validation failures have occurred.") { - public ValidationException() - : base("One or more validation failures have occurred.") - { - Errors = new Dictionary(); - } - - public ValidationException(IEnumerable failures) - : this() - { - Errors = failures - .GroupBy(e => e.PropertyName, e => e.ErrorMessage) - .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); - } + Errors = new Dictionary(); + } - public IDictionary Errors { get; } + public ValidationException(IEnumerable failures) + : this() + { + Errors = failures + .GroupBy(e => e.PropertyName, e => e.ErrorMessage) + .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); } -} \ No newline at end of file + + public IDictionary Errors { get; } +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs index 81716db..c473fbc 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommand.cs @@ -1,48 +1,44 @@ using Chipsoft.Assignments.EPDConsole.Core.Entities; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using MediatR; -using System; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands; + +public class AddPatientCommand : IRequest +{ + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string BSN { get; set; } = string.Empty; + public string Address { get; set; } = string.Empty; + public string PhoneNumber { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public DateTime DateOfBirth { get; set; } +} + +public class AddPatientCommandHandler : IRequestHandler { - public class AddPatientCommand : IRequest + private readonly IApplicationDbContext _context; + + public AddPatientCommandHandler(IApplicationDbContext context) { - public string FirstName { get; set; } = string.Empty; - public string LastName { get; set; } = string.Empty; - public string BSN { get; set; } = string.Empty; - public string Address { get; set; } = string.Empty; - public string PhoneNumber { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public DateTime DateOfBirth { get; set; } + _context = context; } - public class AddPatientCommandHandler : IRequestHandler + public async Task Handle(AddPatientCommand request, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public AddPatientCommandHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task Handle(AddPatientCommand request, CancellationToken cancellationToken) + var patient = new Patient { - var patient = new Patient - { - FirstName = request.FirstName, - LastName = request.LastName, - BSN = request.BSN, - Address = request.Address, - PhoneNumber = request.PhoneNumber, - Email = request.Email, - DateOfBirth = request.DateOfBirth - }; + FirstName = request.FirstName, + LastName = request.LastName, + BSN = request.BSN, + Address = request.Address, + PhoneNumber = request.PhoneNumber, + Email = request.Email, + DateOfBirth = request.DateOfBirth + }; - _context.Patients.Add(patient); - await _context.SaveChangesAsync(cancellationToken); - return patient.Id; - } + _context.Patients.Add(patient); + await _context.SaveChangesAsync(cancellationToken); + return patient.Id; } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs index 82c6b4f..1e245b4 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/AddPatientCommandValidator.cs @@ -1,56 +1,52 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using FluentValidation; using Microsoft.EntityFrameworkCore; -using System; using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands; + +public class AddPatientCommandValidator : AbstractValidator { - public class AddPatientCommandValidator : AbstractValidator + private readonly IApplicationDbContext _context; + + public AddPatientCommandValidator(IApplicationDbContext context) + { + _context = context; + + RuleFor(v => v.FirstName) + .NotEmpty().WithMessage("Voornaam is een verplicht veld.") + .MaximumLength(200); + + RuleFor(v => v.LastName) + .NotEmpty().WithMessage("Achternaam is een verplicht veld.") + .MaximumLength(200); + + RuleFor(v => v.Address) + .NotEmpty().WithMessage("Adres is een verplicht veld."); + + RuleFor(v => v.BSN) + .NotEmpty().WithMessage("BSN is een verplicht veld.") + .Length(9).WithMessage("BSN moet exact 9 cijfers bevatten.") + .Matches("^[0-9]*$").WithMessage("BSN mag alleen cijfers bevatten.") + .MustAsync(BeUniqueBsn).WithMessage("Een patiënt met dit BSN bestaat al."); + + RuleFor(v => v.PhoneNumber) + .NotEmpty().WithMessage("Telefoonnummer is een verplicht veld.") + .MinimumLength(10).WithMessage("Telefoonnummer moet minimaal 10 tekens lang zijn.") + .Matches(new Regex(@"^[\d\s\(\)\+\-]+$")).WithMessage("Telefoonnummer mag alleen nummers en gebruikelijke tekens (+, -, (, )) bevatten."); + + RuleFor(v => v.Email) + .NotEmpty() + .EmailAddress(); + + RuleFor(v => v.DateOfBirth) + .NotEmpty() + .LessThan(DateTime.Now).WithMessage("Geboortedatum kan niet in de toekomst liggen."); + } + + private async Task BeUniqueBsn(string bsn, CancellationToken cancellationToken) { - private readonly IApplicationDbContext _context; - - public AddPatientCommandValidator(IApplicationDbContext context) - { - _context = context; - - RuleFor(v => v.FirstName) - .NotEmpty().WithMessage("Voornaam is een verplicht veld.") - .MaximumLength(200); - - RuleFor(v => v.LastName) - .NotEmpty().WithMessage("Achternaam is een verplicht veld.") - .MaximumLength(200); - - RuleFor(v => v.Address) - .NotEmpty().WithMessage("Adres is een verplicht veld."); - - RuleFor(v => v.BSN) - .NotEmpty().WithMessage("BSN is een verplicht veld.") - .Length(9).WithMessage("BSN moet exact 9 cijfers bevatten.") - .Matches("^[0-9]*$").WithMessage("BSN mag alleen cijfers bevatten.") - .MustAsync(BeUniqueBsn).WithMessage("Een patiënt met dit BSN bestaat al."); - - RuleFor(v => v.PhoneNumber) - .NotEmpty().WithMessage("Telefoonnummer is een verplicht veld.") - .MinimumLength(10).WithMessage("Telefoonnummer moet minimaal 10 tekens lang zijn.") - .Matches(new Regex(@"^[\d\s\(\)\+\-]+$")).WithMessage("Telefoonnummer mag alleen nummers en gebruikelijke tekens (+, -, (, )) bevatten."); - - RuleFor(v => v.Email) - .NotEmpty() - .EmailAddress(); - - RuleFor(v => v.DateOfBirth) - .NotEmpty() - .LessThan(DateTime.Now).WithMessage("Geboortedatum kan niet in de toekomst liggen."); - } - - private async Task BeUniqueBsn(string bsn, CancellationToken cancellationToken) - { - return await _context.Patients - .AllAsync(p => p.BSN != bsn, cancellationToken); - } + return await _context.Patients + .AllAsync(p => p.BSN != bsn, cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs index b06e6b6..ef9f31b 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Commands/DeletePatientCommand.cs @@ -1,45 +1,30 @@ using MediatR; -using System.Threading; -using System.Threading.Tasks; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Microsoft.EntityFrameworkCore; -using System; -namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Commands; + +public class DeletePatientCommand : IRequest { - public class DeletePatientCommand : IRequest - { - public int Id { get; set; } - } + public int Id { get; set; } +} - public class DeletePatientCommandHandler : IRequestHandler - { - private readonly IApplicationDbContext _context; +public class DeletePatientCommandHandler(IApplicationDbContext context) : IRequestHandler +{ + private readonly IApplicationDbContext _context = context; - public DeletePatientCommandHandler(IApplicationDbContext context) + public async Task Handle(DeletePatientCommand request, CancellationToken cancellationToken) + { + var entity = await _context.Patients + .FindAsync([request.Id], cancellationToken) ?? throw new Exception($"Patient with id {request.Id} not found"); + var hasAppointments = await _context.Appointments.AnyAsync(a => a.PatientId == request.Id, cancellationToken); + if(hasAppointments) { - _context = context; + throw new Exception("Cannot delete patient with active appointments."); } - public async Task Handle(DeletePatientCommand request, CancellationToken cancellationToken) - { - var entity = await _context.Patients - .FindAsync(new object[] { request.Id }, cancellationToken); - - if (entity == null) - { - throw new Exception($"Patient with id {request.Id} not found"); - } - - var hasAppointments = await _context.Appointments.AnyAsync(a => a.PatientId == request.Id, cancellationToken); - if(hasAppointments) - { - throw new Exception("Cannot delete patient with active appointments."); - } + _context.Patients.Remove(entity); - _context.Patients.Remove(entity); - - await _context.SaveChangesAsync(cancellationToken); - } + await _context.SaveChangesAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs index 01d73ac..d4a7993 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Patients/Queries/GetPatientsListQuery.cs @@ -1,43 +1,33 @@ using MediatR; -using System.Collections.Generic; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; -using System.Threading.Tasks; -using System.Threading; using Microsoft.EntityFrameworkCore; -using System.Linq; -namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Queries -{ - public class PatientDto - { - public int Id { get; set; } - public string Name { get; set; } = string.Empty; - public string BSN { get; set; } = string.Empty; - } +namespace Chipsoft.Assignments.EPDConsole.Application.Patients.Queries; - public class GetPatientsListQuery : IRequest> - { - } +public class PatientDto +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public string BSN { get; set; } = string.Empty; +} - public class GetPatientsListQueryHandler : IRequestHandler> - { - private readonly IApplicationDbContext _context; +public class GetPatientsListQuery : IRequest> +{ +} - public GetPatientsListQueryHandler(IApplicationDbContext context) - { - _context = context; - } +public class GetPatientsListQueryHandler(IApplicationDbContext context) : IRequestHandler> +{ + private readonly IApplicationDbContext _context = context; - public async Task> Handle(GetPatientsListQuery request, CancellationToken cancellationToken) - { - return await _context.Patients - .Select(p => new PatientDto - { - Id = p.Id, - Name = $"{p.FirstName} {p.LastName}", - BSN = p.BSN - }) - .ToListAsync(cancellationToken); - } + public async Task> Handle(GetPatientsListQuery request, CancellationToken cancellationToken) + { + return await _context.Patients + .Select(p => new PatientDto + { + Id = p.Id, + Name = $"{p.FirstName} {p.LastName}", + BSN = p.BSN + }) + .ToListAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs index 672fdf3..016bbdf 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommand.cs @@ -1,39 +1,31 @@ using Chipsoft.Assignments.EPDConsole.Core.Entities; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using MediatR; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands -{ - public class AddPhysicianCommand : IRequest - { - public string FirstName { get; set; } = string.Empty; - public string LastName { get; set; } = string.Empty; - } +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands; - public class AddPhysicianCommandHandler : IRequestHandler - { - private readonly IApplicationDbContext _context; +public class AddPhysicianCommand : IRequest +{ + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; +} - public AddPhysicianCommandHandler(IApplicationDbContext context) - { - _context = context; - } +public class AddPhysicianCommandHandler(IApplicationDbContext context) : IRequestHandler +{ + private readonly IApplicationDbContext _context = context; - public async Task Handle(AddPhysicianCommand request, CancellationToken cancellationToken) + public async Task Handle(AddPhysicianCommand request, CancellationToken cancellationToken) + { + var physician = new Physician { - var physician = new Physician - { - FirstName = request.FirstName, - LastName = request.LastName - }; + FirstName = request.FirstName, + LastName = request.LastName + }; - _context.Physicians.Add(physician); + _context.Physicians.Add(physician); - await _context.SaveChangesAsync(cancellationToken); + await _context.SaveChangesAsync(cancellationToken); - return physician.Id; - } + return physician.Id; } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs index 8410138..8bdcc79 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/AddPhysicianCommandValidator.cs @@ -1,35 +1,32 @@ using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using FluentValidation; using Microsoft.EntityFrameworkCore; -using System.Threading; -using System.Threading.Tasks; -namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands; + +public class AddPhysicianCommandValidator : AbstractValidator { - public class AddPhysicianCommandValidator : AbstractValidator - { - private readonly IApplicationDbContext _context; + private readonly IApplicationDbContext _context; - public AddPhysicianCommandValidator(IApplicationDbContext context) - { - _context = context; + public AddPhysicianCommandValidator(IApplicationDbContext context) + { + _context = context; - RuleFor(v => v.FirstName) - .NotEmpty().WithMessage("Voornaam is een verplicht veld.") - .MaximumLength(200).WithMessage("Voornaam mag niet meer dan 200 karakters bevatten."); + RuleFor(v => v.FirstName) + .NotEmpty().WithMessage("Voornaam is een verplicht veld.") + .MaximumLength(200).WithMessage("Voornaam mag niet meer dan 200 karakters bevatten."); - RuleFor(v => v.LastName) - .NotEmpty().WithMessage("Achternaam is een verplicht veld.") - .MaximumLength(200).WithMessage("Achternaam mag niet meer dan 200 karakters bevatten."); - - RuleFor(x => x) - .MustAsync(BeUniqueName).WithMessage("Een arts met deze naam bestaat al."); - } + RuleFor(v => v.LastName) + .NotEmpty().WithMessage("Achternaam is een verplicht veld.") + .MaximumLength(200).WithMessage("Achternaam mag niet meer dan 200 karakters bevatten."); + + RuleFor(x => x) + .MustAsync(BeUniqueName).WithMessage("Een arts met deze naam bestaat al."); + } - private async Task BeUniqueName(AddPhysicianCommand command, CancellationToken cancellationToken) - { - return await _context.Physicians - .AllAsync(p => p.FirstName != command.FirstName || p.LastName != command.LastName, cancellationToken); - } + private async Task BeUniqueName(AddPhysicianCommand command, CancellationToken cancellationToken) + { + return await _context.Physicians + .AllAsync(p => p.FirstName != command.FirstName || p.LastName != command.LastName, cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs index 28bd2e7..846044a 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Commands/DeletePhysicianCommand.cs @@ -1,45 +1,32 @@ using MediatR; -using System.Threading; -using System.Threading.Tasks; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; using Microsoft.EntityFrameworkCore; -using System; -namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Commands; + +public class DeletePhysicianCommand : IRequest { - public class DeletePhysicianCommand : IRequest - { - public int Id { get; set; } - } + public int Id { get; set; } +} - public class DeletePhysicianCommandHandler : IRequestHandler - { - private readonly IApplicationDbContext _context; +public class DeletePhysicianCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; - public DeletePhysicianCommandHandler(IApplicationDbContext context) - { - _context = context; - } + public DeletePhysicianCommandHandler(IApplicationDbContext context) => _context = context; - public async Task Handle(DeletePhysicianCommand request, CancellationToken cancellationToken) + public async Task Handle(DeletePhysicianCommand request, CancellationToken cancellationToken) + { + var entity = await _context.Physicians + .FindAsync(new object[] { request.Id }, cancellationToken) ?? throw new Exception($"Physician with id {request.Id} not found"); + var hasAppointments = await _context.Appointments.AnyAsync(a => a.PhysicianId == request.Id, cancellationToken); + if(hasAppointments) { - var entity = await _context.Physicians - .FindAsync(new object[] { request.Id }, cancellationToken); - - if (entity == null) - { - throw new Exception($"Physician with id {request.Id} not found"); - } - - var hasAppointments = await _context.Appointments.AnyAsync(a => a.PhysicianId == request.Id, cancellationToken); - if(hasAppointments) - { - throw new Exception("Cannot delete physician with active appointments."); - } + throw new Exception("Cannot delete physician with active appointments."); + } - _context.Physicians.Remove(entity); + _context.Physicians.Remove(entity); - await _context.SaveChangesAsync(cancellationToken); - } + await _context.SaveChangesAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs index b76971c..ded8b1b 100644 --- a/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs +++ b/Chipsoft.Assignments.EPDConsole.Application/Physicians/Queries/GetPhysiciansListQuery.cs @@ -1,41 +1,31 @@ using MediatR; -using System.Collections.Generic; using Chipsoft.Assignments.EPDConsole.Core.Interfaces; -using System.Threading.Tasks; -using System.Threading; using Microsoft.EntityFrameworkCore; -using System.Linq; -namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Queries -{ - public class PhysicianDto - { - public int Id { get; set; } - public string Name { get; set; } = string.Empty; - } +namespace Chipsoft.Assignments.EPDConsole.Application.Physicians.Queries; - public class GetPhysiciansListQuery : IRequest> - { - } +public class PhysicianDto +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; +} - public class GetPhysiciansListQueryHandler : IRequestHandler> - { - private readonly IApplicationDbContext _context; +public class GetPhysiciansListQuery : IRequest> +{ +} - public GetPhysiciansListQueryHandler(IApplicationDbContext context) - { - _context = context; - } +public class GetPhysiciansListQueryHandler(IApplicationDbContext context) : IRequestHandler> +{ + private readonly IApplicationDbContext _context = context; - public async Task> Handle(GetPhysiciansListQuery request, CancellationToken cancellationToken) - { - return await _context.Physicians - .Select(p => new PhysicianDto - { - Id = p.Id, - Name = $"{p.FirstName} {p.LastName}" - }) - .ToListAsync(cancellationToken); - } + public async Task> Handle(GetPhysiciansListQuery request, CancellationToken cancellationToken) + { + return await _context.Physicians + .Select(p => new PhysicianDto + { + Id = p.Id, + Name = $"{p.FirstName} {p.LastName}" + }) + .ToListAsync(cancellationToken); } -} \ No newline at end of file +} From 6c9a7286d3df85b84eab0fa152bcef84c6a99172 Mon Sep 17 00:00:00 2001 From: Wael Orraby Date: Sun, 22 Jun 2025 15:05:03 +0200 Subject: [PATCH 23/23] Voeg een nieuw document toe met ontwerpkeuzes voor EPDConsole, inclusief uitleg over Clean Architecture, CQRS, validatie met FluentValidation, Dependency Injection, SOLID-principes, testen en gebruik van .NET 8. Dit document biedt een overzicht van de architecturale beslissingen en richtlijnen voor de ontwikkeling van de applicatie. --- DESIGN.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 DESIGN.md diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..3350421 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,36 @@ +# Designkeuzes EPDConsole + +## Clean Architecture +Het project is opgezet volgens het Clean Architecture-principe. Dit zorgt voor een duidelijke scheiding tussen domein, infrastructuur, applicatielogica en presentatie. Hierdoor is de code onderhoudbaar, testbaar en eenvoudig uitbreidbaar. + +- **Core**: bevat domein-entiteiten en interfaces. +- **Infrastructure**: bevat de database-implementatie (Entity Framework). +- **Application**: bevat businesslogica, CQRS-handlers en validatie. +- **EPDConsole**: bevat de gebruikersinterface (console). + +## CQRS & MediatR +Voor alle mutaties en queries wordt het CQRS-patroon toegepast, ondersteund door MediatR. Commands en Queries worden afgehandeld door aparte handlers. Dit maakt de logica overzichtelijk en testbaar. + +## Validatie met FluentValidation +Alle input wordt gevalideerd met FluentValidation. Validatieregels zijn per command gescheiden en worden automatisch uitgevoerd via een MediatR pipeline. Dit voorkomt duplicatie en zorgt voor consistente foutafhandeling. + +## Dependency Injection +Alle afhankelijkheden worden via Dependency Injection aangeboden. Dit maakt het mogelijk om eenvoudig te testen met mocks en zorgt voor een flexibele, uitbreidbare architectuur. + +## SOLID-principes +De code volgt de SOLID-principes: +- **Single Responsibility**: elke klasse heeft één duidelijke verantwoordelijkheid. +- **Open/Closed**: logica is uitbreidbaar via nieuwe handlers/validators zonder bestaande code te wijzigen. +- **Liskov Substitution**: alle implementaties van interfaces (zoals IApplicationDbContext) zijn uitwisselbaar en gedragen zich zoals verwacht, zowel in productie als in tests. +- **Interface Segregation**: interfaces zijn klein en doelgericht gehouden; er zijn geen onnodig brede interfaces. Voor deze schaal is IApplicationDbContext overzichtelijk en functioneel. +- **Dependency Inversion**: afhankelijkheden worden via interfaces aangeboden. + +## Testen +De Application-laag is volledig afgedekt met unittests (xUnit, Moq, FluentAssertions). Hierdoor is regressie snel zichtbaar en blijft de code betrouwbaar bij refactoring. + +## .NET 8 & Moderne Pakketten +Het project is geüpdatet naar .NET 8 en maakt gebruik van moderne, goed ondersteunde pakketten zoals MediatR, FluentValidation en Moq. + +--- + +Deze keuzes zorgen samen voor een toekomstbestendige, onderhoudbare en goed geteste applicatie. \ No newline at end of file