From 68d0a76a1d3f3f6ed91ca1f56a86ccf1a789faff Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Mon, 27 Oct 2025 20:27:41 -0400 Subject: [PATCH 01/12] Refactor mappers and enhance view models Refactor code to replace mapper classes with extension methods, simplifying mapping between models and view models. Remove `IMapper` interface and its implementations, moving logic to static methods in `BowlerMappers`, `PersonMappers`, and `WeekMappers`. Update view models to use object initializers and property initializers, removing constructors. Enhance `Stepper` control with command support. Update `CurrentWeekOverviewPage.xaml` to bind `Stepper` commands. Modify `MauiProgram` to remove mapper service registrations. Update `Person` class with `IsDeleted` property. Adjust package references in `HangTab.csproj`. --- .../Extensions/ListItemExtensionsTests.cs | 6 +- .../BowlerListItemViewModelMapperTests.cs | 26 +---- HangTab.Tests/Mappers/BowlerMapperTests.cs | 30 +++-- ...CurrentWeekListItemViewModelMapperTests.cs | 7 +- HangTab.Tests/Mappers/PersonMapperTests.cs | 24 ++-- .../SubListItemViewModelMapperTests.cs | 7 +- .../WeekListItemViewModelMapperTests.cs | 15 +-- HangTab.sln | 2 +- HangTab/HangTab.csproj | 5 +- .../Mappers/BowlerListItemViewModelMapper.cs | 48 -------- HangTab/Mappers/BowlerMapper.cs | 22 ---- HangTab/Mappers/BowlerMappers.cs | 55 +++++++++ .../CurrentWeekListItemViewModelMapper.cs | 31 ----- HangTab/Mappers/IMapper.cs | 5 - HangTab/Mappers/PersonMapper.cs | 20 ---- HangTab/Mappers/PersonMappers.cs | 48 ++++++++ HangTab/Mappers/SubListItemViewModelMapper.cs | 24 ---- HangTab/Mappers/ViewModelMappers.cs | 34 ++++++ .../Mappers/WeekListItemViewModelMapper.cs | 24 ---- HangTab/Mappers/WeekMappers.cs | 26 +++++ HangTab/MauiProgram.cs | 20 +--- HangTab/Models/Person.cs | 5 + .../ViewModels/BowlerSelectSubViewModel.cs | 5 +- .../CurrentWeekOverviewViewModel.cs | 107 +++++++++--------- .../Items/BowlerListItemViewModel.cs | 42 ++----- .../Items/CurrentWeekItemViewModel.cs | 43 +------ .../ViewModels/Items/SubListItemViewModel.cs | 18 +-- .../ViewModels/Items/WeekListItemViewModel.cs | 14 +-- HangTab/ViewModels/PersonAddEditViewModel.cs | 5 + .../ViewModels/PersonListOverviewViewModel.cs | 33 +++--- HangTab/ViewModels/SeasonSummaryViewModel.cs | 22 ++-- HangTab/ViewModels/WeekDetailsViewModel.cs | 5 +- .../ViewModels/WeekListOverviewViewModel.cs | 6 +- HangTab/Views/Controls/Stepper.xaml.cs | 30 +++++ HangTab/Views/CurrentWeekOverviewPage.xaml | 4 +- 35 files changed, 351 insertions(+), 467 deletions(-) delete mode 100644 HangTab/Mappers/BowlerListItemViewModelMapper.cs delete mode 100644 HangTab/Mappers/BowlerMapper.cs create mode 100644 HangTab/Mappers/BowlerMappers.cs delete mode 100644 HangTab/Mappers/CurrentWeekListItemViewModelMapper.cs delete mode 100644 HangTab/Mappers/IMapper.cs delete mode 100644 HangTab/Mappers/PersonMapper.cs create mode 100644 HangTab/Mappers/PersonMappers.cs delete mode 100644 HangTab/Mappers/SubListItemViewModelMapper.cs create mode 100644 HangTab/Mappers/ViewModelMappers.cs delete mode 100644 HangTab/Mappers/WeekListItemViewModelMapper.cs create mode 100644 HangTab/Mappers/WeekMappers.cs diff --git a/HangTab.Tests/Extensions/ListItemExtensionsTests.cs b/HangTab.Tests/Extensions/ListItemExtensionsTests.cs index b1ed770..4232ef5 100644 --- a/HangTab.Tests/Extensions/ListItemExtensionsTests.cs +++ b/HangTab.Tests/Extensions/ListItemExtensionsTests.cs @@ -53,8 +53,8 @@ public void SetLowestBowlerHangCount_EmptyList_DoesNotThrow() public void SetBowlerHangSumByWeeks_SetsHangCountCorrectly() { // Arrange - var bowler1 = new BowlerListItemViewModel(id: 1, name: "A", isSub: false); - var bowler2 = new BowlerListItemViewModel(id: 2, name: "B", isSub: false); + var bowler1 = new BowlerListItemViewModel{ Id = 1, Name = "A", IsSub = true }; + var bowler2 = new BowlerListItemViewModel{ Id = 2, Name = "B", IsSub = false }; var weeks = new List { new() @@ -90,7 +90,7 @@ public void SetBowlerHangSumByWeeks_WithSubs_SumsCorrectly() // Arrange var subId = 10; var hangCount = 5; - var bowler = new BowlerListItemViewModel(id:subId, name: "Super Sub", isSub: true); + var bowler = new BowlerListItemViewModel{ Id = subId, Name = "Super Sub", IsSub = true }; var weeks = new List { new() diff --git a/HangTab.Tests/Mappers/BowlerListItemViewModelMapperTests.cs b/HangTab.Tests/Mappers/BowlerListItemViewModelMapperTests.cs index 01c38c1..92c1530 100644 --- a/HangTab.Tests/Mappers/BowlerListItemViewModelMapperTests.cs +++ b/HangTab.Tests/Mappers/BowlerListItemViewModelMapperTests.cs @@ -10,7 +10,6 @@ public class BowlerListItemViewModelMapperTests public void Map_FromPersonList_ReturnsCorrectViewModel() { // Arrange - var mapper = new BowlerListItemViewModelMapper(); var people = new List { new() { Id = 1, Name = "Alice", IsSub = false, ImageUrl = "img1" }, @@ -18,7 +17,7 @@ public void Map_FromPersonList_ReturnsCorrectViewModel() }; // Act - var actual = mapper.Map(people).ToList(); + var actual = people.ToBowlerListItemViewModelList().ToList(); // Assert Assert.Equal(2, actual.Count); @@ -38,7 +37,6 @@ public void Map_FromPersonList_ReturnsCorrectViewModel() public void Map_FromBowlerList_ReturnsCorrectViewModel() { // Arrange - var mapper = new BowlerListItemViewModelMapper(); var bowlers = new List { new() { @@ -51,7 +49,7 @@ public void Map_FromBowlerList_ReturnsCorrectViewModel() }; // Act - var vm = mapper.Map(bowlers).ToList(); + var vm = bowlers.ToBowlerListItemViewModelList().ToList(); // Assert Assert.Single(vm); @@ -64,24 +62,4 @@ public void Map_FromBowlerList_ReturnsCorrectViewModel() Assert.Equal("img3", actual.ImageUrl); Assert.Equal(Status.UsingSub, actual.Status); } - - [Fact] - public void Map_NullPerson_ThrowsArgumentNullException() - { - // Arrange - var mapper = new BowlerListItemViewModelMapper(); - - // Act & Assert - Assert.Throws(() => mapper.Map((IEnumerable)null!)); - } - - [Fact] - public void Map_NullBowler_ThrowsArgumentNullException() - { - // Arrange - var mapper = new BowlerListItemViewModelMapper(); - - // Act & Assert - Assert.Throws(() => mapper.Map((IEnumerable)null!)); - } } \ No newline at end of file diff --git a/HangTab.Tests/Mappers/BowlerMapperTests.cs b/HangTab.Tests/Mappers/BowlerMapperTests.cs index 1e54301..8f2b4ab 100644 --- a/HangTab.Tests/Mappers/BowlerMapperTests.cs +++ b/HangTab.Tests/Mappers/BowlerMapperTests.cs @@ -10,22 +10,21 @@ public class BowlerMapperTests public void Map_ValidCurrentWeekListItemViewModel_MapsToBowler() { // Arrange - var mapper = new BowlerMapper(); - var vm = new CurrentWeekListItemViewModel( - weekId: 2, - bowlerId: 10, - personId: 5, - subId: 7, - status: Status.UsingSub, - hangCount: 3, - name: "Test Bowler", - isSub: false, - initials: "TB", - imageUrl: "img.png" - ); + var vm = new CurrentWeekListItemViewModel + { + WeekId = 2, + BowlerId = 10, + PersonId = 5, + SubId = 7, + Status = Status.UsingSub, + HangCount = 3, + Name = "Test Bowler", + IsSub = false, + ImageUrl = "img.png" + }; // Act - var actual = mapper.Map(vm); + var actual = vm.ToBowler(); // Assert Assert.Equal(10, actual.Id); @@ -38,10 +37,9 @@ public void Map_ValidCurrentWeekListItemViewModel_MapsToBowler() public void Map_NullViewModel_ThrowsArgumentNullException() { // Arrange - var mapper = new BowlerMapper(); CurrentWeekListItemViewModel? vm = null; // Act & Assert - Assert.Throws(() => mapper.Map(vm!)); + Assert.Throws(() => vm.ToBowler()); } } \ No newline at end of file diff --git a/HangTab.Tests/Mappers/CurrentWeekListItemViewModelMapperTests.cs b/HangTab.Tests/Mappers/CurrentWeekListItemViewModelMapperTests.cs index acca1bd..b79d803 100644 --- a/HangTab.Tests/Mappers/CurrentWeekListItemViewModelMapperTests.cs +++ b/HangTab.Tests/Mappers/CurrentWeekListItemViewModelMapperTests.cs @@ -10,7 +10,6 @@ public class CurrentWeekListItemViewModelMapperTests public void Map_ValidBowlerList_MapsToViewModels() { // Arrange - var mapper = new CurrentWeekListItemViewModelMapper(); var bowlers = new List { new() { @@ -46,7 +45,7 @@ public void Map_ValidBowlerList_MapsToViewModels() }; // Act - var actual = mapper.Map(bowlers).ToList(); + var actual = bowlers.ToCurrentWeekListItemViewModelList().ToList(); // Assert Assert.Equal(2, actual.Count); @@ -78,9 +77,9 @@ public void Map_ValidBowlerList_MapsToViewModels() public void Map_NullBowlerList_ThrowsArgumentNullException() { // Arrange - var mapper = new CurrentWeekListItemViewModelMapper(); + List bowlers = null!; // Act & Assert - Assert.Throws(() => mapper.Map(null!)); + Assert.Throws(() => bowlers.ToCurrentWeekListItemViewModelList()); } } \ No newline at end of file diff --git a/HangTab.Tests/Mappers/PersonMapperTests.cs b/HangTab.Tests/Mappers/PersonMapperTests.cs index 9208134..45b07c3 100644 --- a/HangTab.Tests/Mappers/PersonMapperTests.cs +++ b/HangTab.Tests/Mappers/PersonMapperTests.cs @@ -1,4 +1,3 @@ -using HangTab.Enums; using HangTab.Mappers; using HangTab.ViewModels.Items; @@ -10,19 +9,16 @@ public class PersonMapperTests public void Map_ValidBowlerListItemViewModel_MapsToPerson() { // Arrange - var mapper = new PersonMapper(); - var vm = new BowlerListItemViewModel( - id: 42, - name: "Jane Doe", - isSub: true, - bowlerId: 7, - hangCount: 3, - imageUrl: "img.png", - status: Status.UsingSub - ); + var vm = new BowlerListItemViewModel + { + Id = 42, + Name = "Jane Doe", + IsSub = true, + ImageUrl = "img.png" + }; // Act - var person = mapper.Map(vm); + var person = vm.ToPerson(); // Assert Assert.Equal(42, person.Id); @@ -35,9 +31,9 @@ public void Map_ValidBowlerListItemViewModel_MapsToPerson() public void Map_NullViewModel_ThrowsArgumentNullException() { // Arrange - var mapper = new PersonMapper(); + BowlerListItemViewModel vm = null!; // Act & Assert - Assert.Throws(() => mapper.Map(null!)); + Assert.Throws(() => vm.ToPerson()); } } \ No newline at end of file diff --git a/HangTab.Tests/Mappers/SubListItemViewModelMapperTests.cs b/HangTab.Tests/Mappers/SubListItemViewModelMapperTests.cs index 406a5bd..daf8ca8 100644 --- a/HangTab.Tests/Mappers/SubListItemViewModelMapperTests.cs +++ b/HangTab.Tests/Mappers/SubListItemViewModelMapperTests.cs @@ -9,7 +9,6 @@ public class SubListItemViewModelMapperTests public void Map_ValidPersonList_MapsToViewModels() { // Arrange - var mapper = new SubListItemViewModelMapper(); var people = new List { new() { Id = 1, Name = "Alice", IsSub = true, ImageUrl = "img1.png" }, @@ -17,7 +16,7 @@ public void Map_ValidPersonList_MapsToViewModels() }; // Act - var actual = mapper.Map(people).ToList(); + var actual = people.ToSubListItemViewModelList().ToList(); // Assert Assert.Equal(2, actual.Count); @@ -37,9 +36,9 @@ public void Map_ValidPersonList_MapsToViewModels() public void Map_NullPeopleList_ThrowsArgumentNullException() { // Arrange - var mapper = new SubListItemViewModelMapper(); + List people = null!; // Act & Assert - Assert.Throws(() => mapper.Map(null!)); + Assert.Throws(() => people.ToSubListItemViewModelList()); } } \ No newline at end of file diff --git a/HangTab.Tests/Mappers/WeekListItemViewModelMapperTests.cs b/HangTab.Tests/Mappers/WeekListItemViewModelMapperTests.cs index c523195..1a6e893 100644 --- a/HangTab.Tests/Mappers/WeekListItemViewModelMapperTests.cs +++ b/HangTab.Tests/Mappers/WeekListItemViewModelMapperTests.cs @@ -10,7 +10,6 @@ public class WeekListItemViewModelMapperTests public void Map_ValidWeeks_MapsToViewModels() { // Arrange - var mapper = new WeekListItemViewModelMapper(); var weeks = new List { new() @@ -41,7 +40,7 @@ public void Map_ValidWeeks_MapsToViewModels() }; // Act - var actual = mapper.Map(weeks).ToList(); + var actual = weeks.ToWeekListItemViewModelList().ToList(); // Assert Assert.Single(actual); @@ -49,18 +48,17 @@ public void Map_ValidWeeks_MapsToViewModels() Assert.Equal(1, vm.Id); Assert.Equal(2, vm.Number); Assert.Equal(3, vm.BusRides); - Assert.Equal(7, vm.HangCount); // 5 + 2 + Assert.Equal(7, vm.TotalHangCount); // 5 + 2 } [Fact] public void Map_EmptyWeeks_ReturnsEmpty() { // Arrange - var mapper = new WeekListItemViewModelMapper(); var weeks = new List(); // Act - var actual = mapper.Map(weeks).ToList(); + var actual = weeks.ToWeekListItemViewModelList().ToList(); // Assert Assert.Empty(actual); @@ -70,17 +68,16 @@ public void Map_EmptyWeeks_ReturnsEmpty() public void Map_NullWeeks_ThrowsArgumentNullException() { // Arrange - var mapper = new WeekListItemViewModelMapper(); + List? weeks = null!; // Act & Assert - Assert.Throws(() => mapper.Map(null!)); + Assert.Throws(() => weeks.ToWeekListItemViewModelList()); } [Fact] public void Map_WeekWithNullBowlers_ThrowsArgumentNullException() { // Arrange - var mapper = new WeekListItemViewModelMapper(); var weeks = new List { new() @@ -93,6 +90,6 @@ public void Map_WeekWithNullBowlers_ThrowsArgumentNullException() }; // Act & Assert - Assert.Throws(() => mapper.Map(weeks).ToList()); + Assert.Throws(() => weeks.ToWeekListItemViewModelList().ToList()); } } \ No newline at end of file diff --git a/HangTab.sln b/HangTab.sln index 6b62b5f..a5a8e51 100644 --- a/HangTab.sln +++ b/HangTab.sln @@ -36,4 +36,4 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8D36CA26-B7D0-45CC-8227-FE6D99F8123C} EndGlobalSection -EndGlobal +EndGlobal \ No newline at end of file diff --git a/HangTab/HangTab.csproj b/HangTab/HangTab.csproj index c3b3884..9b5d405 100644 --- a/HangTab/HangTab.csproj +++ b/HangTab/HangTab.csproj @@ -59,11 +59,10 @@ - - + - + diff --git a/HangTab/Mappers/BowlerListItemViewModelMapper.cs b/HangTab/Mappers/BowlerListItemViewModelMapper.cs deleted file mode 100644 index 3917a9e..0000000 --- a/HangTab/Mappers/BowlerListItemViewModelMapper.cs +++ /dev/null @@ -1,48 +0,0 @@ -using HangTab.Models; -using HangTab.ViewModels.Items; - -namespace HangTab.Mappers; -public class BowlerListItemViewModelMapper : IMapper, IEnumerable>, - IMapper, IEnumerable> -{ - public IEnumerable Map(IEnumerable people) - { - return people is null - ? throw new ArgumentNullException(nameof(people)) - : people.Select(Map); - } - - public IEnumerable Map(IEnumerable bowlers) - { - return bowlers is null - ? throw new ArgumentNullException(nameof(bowlers)) - : bowlers.Select(Map); - } - - private static BowlerListItemViewModel Map(Bowler bowler) - { - return bowler is null - ? throw new ArgumentNullException(nameof(bowler)) - : new BowlerListItemViewModel( - bowler.PersonId, - bowler.Person.Name, - bowler.Person.IsSub, - bowler.Id, - bowler.HangCount, - bowler.Person.ImageUrl, - bowler.Status); - } - - private static BowlerListItemViewModel Map(Person person) - { - return person is null - ? throw new ArgumentNullException(nameof(person)) - : new BowlerListItemViewModel( - person.Id, - person.Name, - person.IsSub, - default, - default, - person.ImageUrl); - } -} diff --git a/HangTab/Mappers/BowlerMapper.cs b/HangTab/Mappers/BowlerMapper.cs deleted file mode 100644 index 6dfeaa7..0000000 --- a/HangTab/Mappers/BowlerMapper.cs +++ /dev/null @@ -1,22 +0,0 @@ -using HangTab.Models; -using HangTab.ViewModels.Items; - -namespace HangTab.Mappers; - -public class BowlerMapper : IMapper -{ - public Bowler Map(CurrentWeekListItemViewModel vm) - { - return vm is null - ? throw new ArgumentNullException(nameof(vm)) - : new Bowler - { - Id = vm.BowlerId, - PersonId = vm.PersonId, - Status = vm.Status, - HangCount = vm.HangCount, - WeekId = vm.WeekId, - SubId = vm.SubId - }; - } -} diff --git a/HangTab/Mappers/BowlerMappers.cs b/HangTab/Mappers/BowlerMappers.cs new file mode 100644 index 0000000..ab458f5 --- /dev/null +++ b/HangTab/Mappers/BowlerMappers.cs @@ -0,0 +1,55 @@ +using HangTab.Models; +using HangTab.ViewModels.Items; + +namespace HangTab.Mappers; +public static class BowlerMappers +{ + public static IEnumerable ToCurrentWeekListItemViewModelList(this IEnumerable bowlers) + { + return bowlers is null + ? throw new ArgumentNullException(nameof(bowlers)) + : bowlers.Select(ToCurrentWeekListItemViewModel); + } + + public static IEnumerable ToBowlerListItemViewModelList(this IEnumerable bowlers) + { + return bowlers is null + ? throw new ArgumentNullException(nameof(bowlers)) + : bowlers.Select(ToBowlerListItemViewModel); + } + + private static BowlerListItemViewModel ToBowlerListItemViewModel(Bowler bowler) + { + return bowler is null + ? throw new ArgumentNullException(nameof(bowler)) + : new BowlerListItemViewModel + { + Id = bowler.PersonId, + Name = bowler.Person.Name, + IsSub = bowler.Person.IsSub, + IsDeleted = bowler.Person.IsDeleted, + BowlerId = bowler.Id, + HangCount = bowler.HangCount, + ImageUrl = bowler.Person.ImageUrl, + Status = bowler.Status + }; + } + + private static CurrentWeekListItemViewModel ToCurrentWeekListItemViewModel(Bowler bowler) + { + return bowler is null + ? throw new ArgumentNullException(nameof(bowler)) + : new CurrentWeekListItemViewModel + { + WeekId = bowler.WeekId, + BowlerId = bowler.Id, + PersonId = bowler.PersonId, + SubId = bowler.SubId, + Status = bowler.Status, + HangCount = bowler.HangCount, + Name = bowler.Person.Name, + IsSub = bowler.Person.IsSub, + ImageUrl = bowler.Person.ImageUrl + }; + } +} diff --git a/HangTab/Mappers/CurrentWeekListItemViewModelMapper.cs b/HangTab/Mappers/CurrentWeekListItemViewModelMapper.cs deleted file mode 100644 index 0dd9b43..0000000 --- a/HangTab/Mappers/CurrentWeekListItemViewModelMapper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using HangTab.Extensions; -using HangTab.Models; -using HangTab.ViewModels.Items; - -namespace HangTab.Mappers; -public class CurrentWeekListItemViewModelMapper : IMapper, IEnumerable> -{ - public IEnumerable Map(IEnumerable bowlers) - { - return bowlers is null - ? throw new ArgumentNullException(nameof(bowlers)) - : bowlers.Select(Map); - } - - private static CurrentWeekListItemViewModel Map(Bowler bowler) - { - return bowler is null - ? throw new ArgumentNullException(nameof(bowler)) - : new CurrentWeekListItemViewModel( - bowler.WeekId, - bowler.Id, - bowler.PersonId, - bowler.SubId, - bowler.Status, - bowler.HangCount, - bowler.Person.Name, - bowler.Person.IsSub, - bowler.Person.Name.GetInitials(), - bowler.Person.ImageUrl); - } -} diff --git a/HangTab/Mappers/IMapper.cs b/HangTab/Mappers/IMapper.cs deleted file mode 100644 index 08aef32..0000000 --- a/HangTab/Mappers/IMapper.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace HangTab.Mappers; -public interface IMapper -{ - TDestination Map(TSource source); -} diff --git a/HangTab/Mappers/PersonMapper.cs b/HangTab/Mappers/PersonMapper.cs deleted file mode 100644 index debc90b..0000000 --- a/HangTab/Mappers/PersonMapper.cs +++ /dev/null @@ -1,20 +0,0 @@ -using HangTab.Models; -using HangTab.ViewModels.Items; - -namespace HangTab.Mappers; - -public class PersonMapper : IMapper -{ - public Person Map(BowlerListItemViewModel vm) - { - return vm is null - ? throw new ArgumentNullException(nameof(vm)) - : new Person - { - Id = vm.Id, - Name = vm.Name, - IsSub = vm.IsSub, - ImageUrl = vm.ImageUrl, - }; - } -} diff --git a/HangTab/Mappers/PersonMappers.cs b/HangTab/Mappers/PersonMappers.cs new file mode 100644 index 0000000..41fa993 --- /dev/null +++ b/HangTab/Mappers/PersonMappers.cs @@ -0,0 +1,48 @@ +using HangTab.Models; +using HangTab.ViewModels.Items; + +namespace HangTab.Mappers; +public static class PersonMappers +{ + public static IEnumerable ToBowlerListItemViewModelList(this IEnumerable people) + { + return people is null + ? throw new ArgumentNullException(nameof(people)) + : people.Select(ToBowlerListItemViewModel); + } + + public static IEnumerable ToSubListItemViewModelList(this IEnumerable people) + { + return people is null + ? throw new ArgumentNullException(nameof(people)) + : people.Select(ToSubListItemViewModel); + } + + private static SubListItemViewModel ToSubListItemViewModel(Person person) + { + return person is null + ? throw new ArgumentNullException(nameof(person)) + : new SubListItemViewModel + { + Id = person.Id, + Name = person.Name, + IsSub = person.IsSub, + IsDeleted = person.IsDeleted, + ImageUrl = person.ImageUrl + }; + } + + private static BowlerListItemViewModel ToBowlerListItemViewModel(Person person) + { + return person is null + ? throw new ArgumentNullException(nameof(person)) + : new BowlerListItemViewModel + { + Id = person.Id, + Name = person.Name, + IsSub = person.IsSub, + IsDeleted = person.IsDeleted, + ImageUrl = person.ImageUrl + }; + } +} diff --git a/HangTab/Mappers/SubListItemViewModelMapper.cs b/HangTab/Mappers/SubListItemViewModelMapper.cs deleted file mode 100644 index 5c4b7a3..0000000 --- a/HangTab/Mappers/SubListItemViewModelMapper.cs +++ /dev/null @@ -1,24 +0,0 @@ -using HangTab.Models; -using HangTab.ViewModels.Items; - -namespace HangTab.Mappers; -public class SubListItemViewModelMapper : IMapper, IEnumerable> -{ - public IEnumerable Map(IEnumerable people) - { - return people is null - ? throw new ArgumentNullException(nameof(people)) - : people.Select(Map); - } - - private static SubListItemViewModel Map(Person person) - { - return person is null - ? throw new ArgumentNullException(nameof(person)) - : new SubListItemViewModel( - person.Id, - person.Name, - person.IsSub, - person.ImageUrl); - } -} diff --git a/HangTab/Mappers/ViewModelMappers.cs b/HangTab/Mappers/ViewModelMappers.cs new file mode 100644 index 0000000..6c79366 --- /dev/null +++ b/HangTab/Mappers/ViewModelMappers.cs @@ -0,0 +1,34 @@ +using HangTab.Models; +using HangTab.ViewModels.Items; + +namespace HangTab.Mappers; +public static class ViewModelMappers +{ + public static Person ToPerson(this BowlerListItemViewModel vm) + { + return vm is null + ? throw new ArgumentNullException(nameof(vm)) + : new Person + { + Id = vm.Id, + Name = vm.Name, + IsSub = vm.IsSub, + ImageUrl = vm.ImageUrl, + }; + } + + public static Bowler ToBowler(this CurrentWeekListItemViewModel vm) + { + return vm is null + ? throw new ArgumentNullException(nameof(vm)) + : new Bowler + { + Id = vm.BowlerId, + PersonId = vm.PersonId, + Status = vm.Status, + HangCount = vm.HangCount, + WeekId = vm.WeekId, + SubId = vm.SubId + }; + } +} diff --git a/HangTab/Mappers/WeekListItemViewModelMapper.cs b/HangTab/Mappers/WeekListItemViewModelMapper.cs deleted file mode 100644 index 975ee45..0000000 --- a/HangTab/Mappers/WeekListItemViewModelMapper.cs +++ /dev/null @@ -1,24 +0,0 @@ -using HangTab.Models; -using HangTab.ViewModels.Items; - -namespace HangTab.Mappers; -public class WeekListItemViewModelMapper : IMapper, IEnumerable> -{ - public IEnumerable Map(IEnumerable weeks) - { - return weeks is null - ? throw new ArgumentNullException(nameof(weeks)) - : weeks.Select(Map); - } - - private static WeekListItemViewModel Map(Week week) - { - return week is null - ? throw new ArgumentNullException(nameof(week)) - : new WeekListItemViewModel( - week.Id, - week.Number, - week.BusRides, - week.Bowlers.Sum(b => b.HangCount)); - } -} diff --git a/HangTab/Mappers/WeekMappers.cs b/HangTab/Mappers/WeekMappers.cs new file mode 100644 index 0000000..e3de3c1 --- /dev/null +++ b/HangTab/Mappers/WeekMappers.cs @@ -0,0 +1,26 @@ +using HangTab.Models; +using HangTab.ViewModels.Items; + +namespace HangTab.Mappers; +public static class WeekMappers +{ + public static IEnumerable ToWeekListItemViewModelList(this IEnumerable weeks) + { + return weeks is null + ? throw new ArgumentNullException(nameof(weeks)) + : weeks.Select(ToWeekListItemViewModel); + } + + private static WeekListItemViewModel ToWeekListItemViewModel(Week week) + { + return week is null + ? throw new ArgumentNullException(nameof(week)) + : new WeekListItemViewModel + { + Id = week.Id, + Number = week.Number, + BusRides = week.BusRides, + TotalHangCount = week.Bowlers.Sum(b => b.HangCount) + }; + } +} diff --git a/HangTab/MauiProgram.cs b/HangTab/MauiProgram.cs index 49589fe..8902600 100644 --- a/HangTab/MauiProgram.cs +++ b/HangTab/MauiProgram.cs @@ -1,17 +1,15 @@ using CommunityToolkit.Maui; +using CommunityToolkit.Mvvm.Messaging; using HangTab.Data; using HangTab.Data.Impl; using HangTab.Handlers.SearchBar; -using HangTab.Mappers; -using HangTab.Models; using HangTab.Repositories; using HangTab.Repositories.Impl; using HangTab.Services; using HangTab.Services.Impl; using HangTab.ViewModels; using HangTab.ViewModels.BottomSheets; -using HangTab.ViewModels.Items; using HangTab.Views; using HangTab.Views.BottomSheets; @@ -54,8 +52,7 @@ public static MauiApp CreateMauiApp() .RegisterServices() .RegisterViewModels() .RegisterViews() - .RegisterBottomSheets() - .RegisterMappers(); + .RegisterBottomSheets(); #if DEBUG builder.Logging.AddDebug(); @@ -88,6 +85,7 @@ private static MauiAppBuilder RegisterRepositories(this MauiAppBuilder builder) private static MauiAppBuilder RegisterServices(this MauiAppBuilder builder) { builder.Services.AddSingleton(new SettingsService(Preferences.Default)); + builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -139,16 +137,4 @@ private static MauiAppBuilder RegisterBottomSheets(this MauiAppBuilder builder) builder.Services.AddBottomSheet(nameof(AvatarSelectBottomSheet)); return builder; } - - private static MauiAppBuilder RegisterMappers(this MauiAppBuilder builder) - { - builder.Services.AddSingleton, BowlerMapper>(); - builder.Services.AddSingleton, PersonMapper>(); - builder.Services.AddSingleton, IEnumerable>, BowlerListItemViewModelMapper>(); - builder.Services.AddSingleton, IEnumerable>, BowlerListItemViewModelMapper>(); - builder.Services.AddSingleton, IEnumerable>, CurrentWeekListItemViewModelMapper>(); - builder.Services.AddSingleton, IEnumerable>, SubListItemViewModelMapper>(); - builder.Services.AddSingleton, IEnumerable>, WeekListItemViewModelMapper>(); - return builder; - } } \ No newline at end of file diff --git a/HangTab/Models/Person.cs b/HangTab/Models/Person.cs index eca2b39..48030dc 100644 --- a/HangTab/Models/Person.cs +++ b/HangTab/Models/Person.cs @@ -6,9 +6,14 @@ public class Person { [PrimaryKey, AutoIncrement] public int Id { get; set; } + [NotNull, MaxLength(50)] public string Name { get; set; } = string.Empty; + [MaxLength(2083)] public string? ImageUrl { get; set; } = null; + public bool IsSub { get; set; } + + public bool IsDeleted { get; set; } } diff --git a/HangTab/ViewModels/BowlerSelectSubViewModel.cs b/HangTab/ViewModels/BowlerSelectSubViewModel.cs index 2c1356f..65ceee9 100644 --- a/HangTab/ViewModels/BowlerSelectSubViewModel.cs +++ b/HangTab/ViewModels/BowlerSelectSubViewModel.cs @@ -17,8 +17,7 @@ namespace HangTab.ViewModels; public partial class BowlerSelectSubViewModel( IPersonService personService, IBowlerService bowlerService, - INavigationService navigationService, - IMapper, IEnumerable> mapper) : ViewModelBase, IQueryAttributable + INavigationService navigationService) : ViewModelBase, IQueryAttributable { private Bowler? _bowler; @@ -88,7 +87,7 @@ private async Task GetSubs() if (subs.Any()) { Subs.Clear(); - Subs = mapper.Map(subs).ToObservableCollection(); + Subs = subs.ToSubListItemViewModelList().ToObservableCollection(); } } diff --git a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs index 3f98583..dba739a 100644 --- a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs +++ b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs @@ -17,55 +17,48 @@ namespace HangTab.ViewModels; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "This is a ViewModel for the UI and does not require unit tests.")] public partial class CurrentWeekOverviewViewModel : ViewModelBase, + IRecipient, IRecipient, IRecipient, - IRecipient, - IRecipient, - IRecipient + IRecipient { private readonly IAudioService _audioService; + private readonly IBowlerService _bowlerService; private readonly IDialogService _dialogService; + private readonly IMessenger _messenger; private readonly INavigationService _navigationService; private readonly ISettingsService _settingsService; - private readonly IWeekService _weekService; - private readonly IBowlerService _bowlerService; private readonly IScreenshotService _screenshotService; private readonly IShareService _shareService; - - private readonly IMapper _bowlerMapper; - private readonly IMapper, IEnumerable> _currentWeekListItemViewModelMapper; + private readonly IWeekService _weekService; private const string BusSound = "beepbeep.mp3"; public CurrentWeekOverviewViewModel( IAudioService audioService, + IBowlerService bowlerService, IDialogService dialogService, + IMessenger messenger, INavigationService navigationService, - ISettingsService settingsService, - IWeekService weekService, - IBowlerService bowlerService, IScreenshotService screenshotService, + ISettingsService settingsService, IShareService shareService, - IMapper bowlerMapper, - IMapper, IEnumerable> currentWeekListItemViewModelMapper) + IWeekService weekService) { _audioService = audioService; + _bowlerService = bowlerService; _dialogService = dialogService; + _messenger = messenger; _navigationService = navigationService; - _settingsService = settingsService; - _weekService = weekService; - _bowlerService = bowlerService; _screenshotService = screenshotService; + _settingsService = settingsService; _shareService = shareService; + _weekService = weekService; - _bowlerMapper = bowlerMapper; - _currentWeekListItemViewModelMapper = currentWeekListItemViewModelMapper; - - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); + _messenger.Register(this); + _messenger.Register(this); + _messenger.Register(this); + _messenger.Register(this); } [ObservableProperty] @@ -208,7 +201,7 @@ private async Task GetCurrentWeek() if (CurrentWeek.Bowlers.Count > 0) { CurrentWeekBowlers.Clear(); - CurrentWeekBowlers = _currentWeekListItemViewModelMapper.Map(CurrentWeek.Bowlers).ToObservableCollection(); + CurrentWeekBowlers = CurrentWeek.Bowlers.ToCurrentWeekListItemViewModelList().ToObservableCollection(); CurrentWeekBowlers.SetLowestBowlerHangCount(); } @@ -241,54 +234,30 @@ private async Task SetBowlerStatus(CurrentWeekListItemViewModel? vm, Enums.Statu case Enums.Status.Active: vm.SubId = null; vm.Status = Enums.Status.Active; - await _bowlerService.UpdateBowler(_bowlerMapper.Map(vm)); + await _bowlerService.UpdateBowler(vm.ToBowler()); break; case Enums.Status.Blind: vm.SubId = null; vm.Status = Enums.Status.Blind; - await _bowlerService.UpdateBowler(_bowlerMapper.Map(vm)); + await _bowlerService.UpdateBowler(vm.ToBowler()); break; case Enums.Status.UsingSub: - await _navigationService.GoToSelectSub(_bowlerMapper.Map(vm)); + await _navigationService.GoToSelectSub(vm.ToBowler()); return; } await GetCurrentWeek(); } - public async void Receive(SystemResetMessage message) - { - CurrentWeekBowlers.Clear(); - TeamHangTotal = 0; - IsEnableCompleteWeek = false; - PageTitle = "Week 1"; - await GetCurrentWeek(); - } - - public async void Receive(BowlerSubChangedMessage message) - { - var bowler = CurrentWeekBowlers.FirstOrDefault(b => b.BowlerId == message.Id); - if (bowler is null) - { - return; - } - - bowler.SubId = message.SubId; - bowler.Status = Enums.Status.UsingSub; - await _bowlerService.UpdateBowler(_bowlerMapper.Map(bowler)); - - await GetCurrentWeek(); - } - - public async void Receive(BowlerHangCountChangedMessage message) + [RelayCommand] + private async Task BowlerHangCountChangedAsync(CurrentWeekListItemViewModel? vm) { - var bowler = CurrentWeekBowlers.FirstOrDefault(b => b.BowlerId == message.BowlerId); - if (bowler is null) + if (vm is null) { return; } - if (await _bowlerService.UpdateBowler(_bowlerMapper.Map(bowler))) + if (await _bowlerService.UpdateBowler(vm.ToBowler())) { var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); var isIncrease = newHangTotal > TeamHangTotal; @@ -301,6 +270,8 @@ public async void Receive(BowlerHangCountChangedMessage message) await Task.Delay(1000); PlayPopperAnimation = false; } + + _messenger.Send(new BowlerHangCountChangedMessage(vm.BowlerId, vm.HangCount)); } else { @@ -308,6 +279,30 @@ public async void Receive(BowlerHangCountChangedMessage message) } } + public async void Receive(SystemResetMessage message) + { + CurrentWeekBowlers.Clear(); + TeamHangTotal = 0; + IsEnableCompleteWeek = false; + PageTitle = "Week 1"; + await GetCurrentWeek(); + } + + public async void Receive(BowlerSubChangedMessage message) + { + var vm = CurrentWeekBowlers.FirstOrDefault(b => b.BowlerId == message.Id); + if (vm is null) + { + return; + } + + vm.SubId = message.SubId; + vm.Status = Enums.Status.UsingSub; + await _bowlerService.UpdateBowler(vm.ToBowler()); + + await GetCurrentWeek(); + } + public async void Receive(PersonAddedOrChangedMessage message) { if (message.Id > 0) diff --git a/HangTab/ViewModels/Items/BowlerListItemViewModel.cs b/HangTab/ViewModels/Items/BowlerListItemViewModel.cs index 5f8a996..11c610b 100644 --- a/HangTab/ViewModels/Items/BowlerListItemViewModel.cs +++ b/HangTab/ViewModels/Items/BowlerListItemViewModel.cs @@ -1,9 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Messaging; using HangTab.Enums; using HangTab.Extensions; -using HangTab.Messages; using HangTab.ViewModels.Items.Interfaces; namespace HangTab.ViewModels.Items; @@ -14,53 +12,29 @@ public partial class BowlerListItemViewModel : ObservableObject, ILowestHangCoun private int _id; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(Initials))] private string _name = string.Empty; [ObservableProperty] private bool _isSub; [ObservableProperty] - private int _hangCount; + private bool _isDeleted; - partial void OnHangCountChanged(int oldValue, int newValue) - { - if (oldValue != newValue && newValue >= 0) - { - WeakReferenceMessenger.Default.Send(new BowlerHangCountChangedMessage(BowlerId, newValue)); - } - } + [ObservableProperty] + private int _hangCount; [ObservableProperty] private int _bowlerId; [ObservableProperty] - private string? _imageUrl; - - [ObservableProperty] - private string _initials; + private string? _imageUrl = null; + + public string Initials => Name.GetInitials(); [ObservableProperty] private bool _hasLowestHangCount; [ObservableProperty] - private Status _status; - - public BowlerListItemViewModel( - int id, - string name, - bool isSub, - int bowlerId = 0, - int hangCount = 0, - string? imageUrl = null, - Status status = Status.Active) - { - Id = id; - Name = name; - IsSub = isSub; - BowlerId = bowlerId; - HangCount = hangCount; - ImageUrl = imageUrl; - Status = status; - Initials = name.GetInitials(); - } + private Status _status = Status.Active; } diff --git a/HangTab/ViewModels/Items/CurrentWeekItemViewModel.cs b/HangTab/ViewModels/Items/CurrentWeekItemViewModel.cs index 842cf69..ca8067a 100644 --- a/HangTab/ViewModels/Items/CurrentWeekItemViewModel.cs +++ b/HangTab/ViewModels/Items/CurrentWeekItemViewModel.cs @@ -1,8 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Messaging; using HangTab.Enums; -using HangTab.Messages; +using HangTab.Extensions; using HangTab.ViewModels.Items.Interfaces; namespace HangTab.ViewModels.Items; @@ -30,17 +29,10 @@ public partial class CurrentWeekListItemViewModel : ObservableObject, ILowestHan [ObservableProperty] private int _hangCount; - - partial void OnHangCountChanged(int oldValue, int newValue) - { - if (oldValue != newValue && newValue >= 0) - { - WeakReferenceMessenger.Default.Send(new BowlerHangCountChangedMessage(BowlerId, newValue)); - } - } [ObservableProperty] - private string _name; + [NotifyPropertyChangedFor(nameof(Initials))] + private string _name = string.Empty; [ObservableProperty] private string? _imageUrl; @@ -48,39 +40,12 @@ partial void OnHangCountChanged(int oldValue, int newValue) [ObservableProperty] private bool _isSub; - [ObservableProperty] - private string _initials; - [ObservableProperty] private bool _hasLowestHangCount; public bool EnableStepper => Status is not Status.Blind; - public bool ShowActiveOption => Status is Status.Blind or Status.UsingSub; public bool ShowBlindOption => Status is Status.Active; public bool IsBlind => Status is Status.Blind; - - public CurrentWeekListItemViewModel( - int weekId, - int bowlerId, - int personId, - int? subId, - Status status, - int hangCount, - string name, - bool isSub, - string initials, - string? imageUrl = null) - { - WeekId = weekId; - BowlerId = bowlerId; - PersonId = personId; - SubId = subId; - Status = status; - HangCount = hangCount; - Name = name; - IsSub = isSub; - Initials = initials; - ImageUrl = imageUrl; - } + public string Initials => Name.GetInitials(); } diff --git a/HangTab/ViewModels/Items/SubListItemViewModel.cs b/HangTab/ViewModels/Items/SubListItemViewModel.cs index f9e33e5..90f4eb2 100644 --- a/HangTab/ViewModels/Items/SubListItemViewModel.cs +++ b/HangTab/ViewModels/Items/SubListItemViewModel.cs @@ -10,6 +10,7 @@ public partial class SubListItemViewModel : ObservableObject private int _id; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(Initials))] private string _name = string.Empty; [ObservableProperty] @@ -19,21 +20,10 @@ public partial class SubListItemViewModel : ObservableObject private string? _imageUrl; [ObservableProperty] - private string _initials; + private bool _isSelected; [ObservableProperty] - private bool _isSelected; + private bool _isDeleted; - public SubListItemViewModel( - int id, - string name, - bool isSub, - string? imageUrl = null) - { - Id = id; - Name = name; - IsSub = isSub; - ImageUrl = imageUrl; - Initials = name.GetInitials(); - } + public string Initials => Name.GetInitials(); } diff --git a/HangTab/ViewModels/Items/WeekListItemViewModel.cs b/HangTab/ViewModels/Items/WeekListItemViewModel.cs index 398fddc..2207b3e 100644 --- a/HangTab/ViewModels/Items/WeekListItemViewModel.cs +++ b/HangTab/ViewModels/Items/WeekListItemViewModel.cs @@ -14,17 +14,5 @@ public partial class WeekListItemViewModel : ObservableObject private int _busRides; [ObservableProperty] - private int _hangCount; - - public WeekListItemViewModel( - int id, - int number, - int busRides, - int hangCount) - { - Id = id; - Number = number; - BusRides = busRides; - HangCount = hangCount; - } + private int _totalHangCount; } diff --git a/HangTab/ViewModels/PersonAddEditViewModel.cs b/HangTab/ViewModels/PersonAddEditViewModel.cs index 401119a..a10096b 100644 --- a/HangTab/ViewModels/PersonAddEditViewModel.cs +++ b/HangTab/ViewModels/PersonAddEditViewModel.cs @@ -75,6 +75,9 @@ public PersonAddEditViewModel( [ObservableProperty] private bool _isSub; + [ObservableProperty] + private bool _isDeleted; + [ObservableProperty] private ObservableCollection _errors = []; @@ -190,6 +193,7 @@ private Person MapDataToPerson() Name = Name, ImageUrl = ImageUrl, IsSub = BowlerTypeIndex == (int)BowlerType.Sub, + IsDeleted = IsDeleted }; } @@ -201,6 +205,7 @@ private void MapPerson(Person? model) Name = model.Name; ImageUrl = model.ImageUrl; IsSub = model.IsSub; + IsDeleted = model.IsDeleted; Initials = model.Id > 0 ? model.Name.GetInitials() : string.Empty; BowlerTypeIndex = model.IsSub diff --git a/HangTab/ViewModels/PersonListOverviewViewModel.cs b/HangTab/ViewModels/PersonListOverviewViewModel.cs index 9d57a3b..be87550 100644 --- a/HangTab/ViewModels/PersonListOverviewViewModel.cs +++ b/HangTab/ViewModels/PersonListOverviewViewModel.cs @@ -5,9 +5,7 @@ using HangTab.Extensions; using HangTab.Mappers; - using HangTab.Messages; -using HangTab.Models; using HangTab.Services; using HangTab.ViewModels.Base; using HangTab.ViewModels.Items; @@ -23,31 +21,26 @@ public partial class PersonListOverviewViewModel : IRecipient, IRecipient { - private readonly IPersonService _personService; + private readonly IMessenger _messenger; private readonly INavigationService _navigationService; + private readonly IPersonService _personService; private readonly IWeekService _weekService; - private readonly IMapper _personMapper; - private readonly IMapper, IEnumerable> _bowlerListItemViewModelMapper; - public PersonListOverviewViewModel( - IPersonService personService, + IMessenger messenger, INavigationService navigationService, - IWeekService weekService, - IMapper personMapper, - IMapper, IEnumerable> bowlerListItemViewModelMapper) + IPersonService personService, + IWeekService weekService) { - _personService = personService; + _messenger = messenger; _navigationService = navigationService; + _personService = personService; _weekService = weekService; - _personMapper = personMapper; - _bowlerListItemViewModelMapper = bowlerListItemViewModelMapper; - - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); + _messenger.Register(this); + _messenger.Register(this); + _messenger.Register(this); + _messenger.Register(this); } private IEnumerable _allBowlers = []; @@ -96,7 +89,7 @@ private async Task NavigateToEditSelectedBowler() { if (SelectedBowler is not null) { - await _navigationService.GoToEditBowler(_personMapper.Map(SelectedBowler)); + await _navigationService.GoToEditBowler(SelectedBowler.ToPerson()); SelectedBowler = null; } } @@ -118,7 +111,7 @@ private async Task GetBowlers() } Bowlers.Clear(); - AllBowlers = _bowlerListItemViewModelMapper.Map(people.OrderBy(b => b.Name)); + AllBowlers = people.OrderBy(b => b.Name).ToBowlerListItemViewModelList(); Bowlers = AllBowlers.ToObservableCollection(); await UpdateBowlerHangCounts(); diff --git a/HangTab/ViewModels/SeasonSummaryViewModel.cs b/HangTab/ViewModels/SeasonSummaryViewModel.cs index fd443b3..2bc1f4b 100644 --- a/HangTab/ViewModels/SeasonSummaryViewModel.cs +++ b/HangTab/ViewModels/SeasonSummaryViewModel.cs @@ -4,7 +4,6 @@ using HangTab.Extensions; using HangTab.Mappers; -using HangTab.Models; using HangTab.Services; using HangTab.ViewModels.Base; using HangTab.ViewModels.Items; @@ -17,7 +16,6 @@ public partial class SeasonSummaryViewModel( INavigationService navigationService, IPersonService personService, IWeekService weekService, - IMapper, IEnumerable> mapper, DataManagerViewModel dataManager) : ViewModelBase { [ObservableProperty] @@ -74,7 +72,7 @@ private async Task GetBowlers() return; } - var bowlers = mapper.Map(people.OrderBy(b => b.Name)).ToList(); + var bowlers = people.OrderBy(b => b.Name).ToBowlerListItemViewModelList().ToList(); bowlers.SetBowlerHangSumByWeeks(allWeeks); BestBowlers = bowlers.Where(b => b.HangCount == bowlers.Min(b => b.HangCount)).ToObservableCollection(); @@ -89,20 +87,22 @@ private async Task GetWeeks() } var weeks = allWeeks - .Select(w => new WeekListItemViewModel( - w.Id, - w.Number, - w.BusRides, - w.Bowlers.Sum(b => b.HangCount))) - .OrderByDescending(w => w.HangCount); + .Select(w => new WeekListItemViewModel + { + Id = w.Id, + Number = w.Number, + BusRides = w.BusRides, + TotalHangCount = w.Bowlers.Sum(b => b.HangCount) + }) + .OrderByDescending(w => w.TotalHangCount); var bestHangingWeek = weeks.First(); BestHangWeekNumber = bestHangingWeek.Number; - BestHangWeekCount = bestHangingWeek.HangCount; + BestHangWeekCount = bestHangingWeek.TotalHangCount; var worstHangingWeek = weeks.Last(); WorstHangWeekNumber = worstHangingWeek.Number; - WorstHangWeekCount = worstHangingWeek.HangCount; + WorstHangWeekCount = worstHangingWeek.TotalHangCount; var bestBusRideWeek = weeks.OrderByDescending(w => w.BusRides).First(); BestBusRideWeekNumber = bestBusRideWeek.Number; diff --git a/HangTab/ViewModels/WeekDetailsViewModel.cs b/HangTab/ViewModels/WeekDetailsViewModel.cs index 6406f14..b9d67a7 100644 --- a/HangTab/ViewModels/WeekDetailsViewModel.cs +++ b/HangTab/ViewModels/WeekDetailsViewModel.cs @@ -13,8 +13,7 @@ namespace HangTab.ViewModels; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "This is a ViewModel for the UI and does not require unit tests.")] public partial class WeekDetailsViewModel( ISettingsService settingsService, - IWeekService weekService, - IMapper, IEnumerable> mapper) : ViewModelBase, IQueryAttributable + IWeekService weekService) : ViewModelBase, IQueryAttributable { [ObservableProperty] private int _id; @@ -69,7 +68,7 @@ private void MapWeekData(Week week) Id = week.Id; Number = week.Number; BusRides = week.BusRides; - Bowlers = mapper.Map(week.Bowlers).ToObservableCollection(); + Bowlers = week.Bowlers.ToCurrentWeekListItemViewModelList().ToObservableCollection(); } public void ApplyQueryAttributes(IDictionary query) diff --git a/HangTab/ViewModels/WeekListOverviewViewModel.cs b/HangTab/ViewModels/WeekListOverviewViewModel.cs index b002fdc..182051b 100644 --- a/HangTab/ViewModels/WeekListOverviewViewModel.cs +++ b/HangTab/ViewModels/WeekListOverviewViewModel.cs @@ -3,7 +3,6 @@ using CommunityToolkit.Mvvm.Input; using HangTab.Mappers; -using HangTab.Models; using HangTab.Services; using HangTab.ViewModels.Base; using HangTab.ViewModels.Items; @@ -15,8 +14,7 @@ namespace HangTab.ViewModels; public partial class WeekListOverviewViewModel( IWeekService weekService, ISettingsService settingsService, - INavigationService navigationService, - IMapper, IEnumerable> mapper) : ViewModelBase + INavigationService navigationService) : ViewModelBase { [ObservableProperty] private ObservableCollection _weeks = []; @@ -35,7 +33,7 @@ private async Task GetWeeks() } Weeks.Clear(); - Weeks = mapper.Map(weeks.Where(w => w.Id != settingsService.CurrentWeekPrimaryKey).OrderByDescending(w => w.Number)).ToObservableCollection(); + Weeks = weeks.Where(w => w.Id != settingsService.CurrentWeekPrimaryKey).OrderByDescending(w => w.Number).ToWeekListItemViewModelList().ToObservableCollection(); } [RelayCommand] diff --git a/HangTab/Views/Controls/Stepper.xaml.cs b/HangTab/Views/Controls/Stepper.xaml.cs index 5107637..4e6a69b 100644 --- a/HangTab/Views/Controls/Stepper.xaml.cs +++ b/HangTab/Views/Controls/Stepper.xaml.cs @@ -1,5 +1,7 @@ using HangTab.Utilities; +using System.Windows.Input; + namespace HangTab.Views.Controls; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "This is a Behavior for the UI and does not require unit tests.")] public partial class Stepper : ContentView @@ -34,6 +36,18 @@ public bool IsControlEnabled set => SetValue(IsControlEnabledProperty, value); } + public ICommand ValueChangedCommand + { + get => (ICommand)GetValue(ValueChangedCommandProperty); + set => SetValue(ValueChangedCommandProperty, value); + } + + public object ValueChangedCommandParameter + { + get => GetValue(ValueChangedCommandParameterProperty); + set => SetValue(ValueChangedCommandParameterProperty, value); + } + public static readonly BindableProperty ValueProperty = BindableProperty.Create(nameof(Value), typeof(int), typeof(Stepper), defaultValue: 0, BindingMode.TwoWay, propertyChanged: (bindableObject, oldValue, newValue) => @@ -43,6 +57,7 @@ public bool IsControlEnabled { stepper.SetDecreaseButtonState((int)newValue > stepper.Minimum); stepper.SetIncreaseButtonState((int)newValue < stepper.Maximum); + stepper.OnValueChanged(); } }); @@ -69,6 +84,12 @@ public bool IsControlEnabled } }); + public static readonly BindableProperty ValueChangedCommandProperty = + BindableProperty.Create(nameof(ValueChangedCommand), typeof(ICommand), typeof(Stepper), default(ICommand), BindingMode.OneWay); + + public static readonly BindableProperty ValueChangedCommandParameterProperty = + BindableProperty.Create(nameof(ValueChangedCommandParameter), typeof(object), typeof(Stepper), default, BindingMode.OneWay); + private void OnMinusButtonClicked(object sender, EventArgs e) { if (Value > Minimum) @@ -108,4 +129,13 @@ private void SetIncreaseButtonState(bool isEnabled) : TextColor.ControlDisabledTextColor; } } + + private void OnValueChanged() + { + var parameter = ValueChangedCommandParameter ?? Value; + if (ValueChangedCommand?.CanExecute(parameter) ?? false) + { + ValueChangedCommand.Execute(parameter); + } + } } \ No newline at end of file diff --git a/HangTab/Views/CurrentWeekOverviewPage.xaml b/HangTab/Views/CurrentWeekOverviewPage.xaml index aac218e..478cbfa 100644 --- a/HangTab/Views/CurrentWeekOverviewPage.xaml +++ b/HangTab/Views/CurrentWeekOverviewPage.xaml @@ -130,7 +130,9 @@ + Value="{Binding HangCount}" + ValueChangedCommand="{Binding Source={RelativeSource AncestorType={x:Type vm:CurrentWeekOverviewViewModel}}, Path=BowlerHangCountChangedCommand}" + ValueChangedCommandParameter="{Binding .}"/> From db6edc7734015324b6129a802e41d1464def130b Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:05:12 -0400 Subject: [PATCH 02/12] now injecting IMessenger --- .../BottomSheets/AvatarSelectViewModel.cs | 13 +++++++------ HangTab/ViewModels/BowlerSelectSubViewModel.cs | 3 ++- HangTab/ViewModels/DataManagerViewModel.cs | 3 ++- HangTab/ViewModels/PersonAddEditViewModel.cs | 11 +++++++---- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/HangTab/ViewModels/BottomSheets/AvatarSelectViewModel.cs b/HangTab/ViewModels/BottomSheets/AvatarSelectViewModel.cs index 3c3f275..73ba475 100644 --- a/HangTab/ViewModels/BottomSheets/AvatarSelectViewModel.cs +++ b/HangTab/ViewModels/BottomSheets/AvatarSelectViewModel.cs @@ -12,20 +12,21 @@ namespace HangTab.ViewModels.BottomSheets; public partial class AvatarSelectViewModel( IDialogService dialogService, IMediaPickerService mediaPickerService, + IMessenger messenger, IBottomSheetNavigationService bottomSheetNavigationService) : ViewModelBase { [RelayCommand] - private async Task DeleteBowlerImage() + private async Task DeleteBowlerImageAsync() { if (await dialogService.Ask("Delete", "Remove the bowler's profile image?")) { await bottomSheetNavigationService.GoBackAsync(); - WeakReferenceMessenger.Default.Send(new PersonImageAddedOrChangedMessage(null)); + messenger.Send(new PersonImageAddedOrChangedMessage(null)); } } [RelayCommand] - private async Task SelectBowlerImageFromCamera() + private async Task SelectBowlerImageFromCameraAsync() { var photoPath = await mediaPickerService.TakePhotoAsync(); if (string.IsNullOrEmpty(photoPath)) @@ -34,11 +35,11 @@ private async Task SelectBowlerImageFromCamera() } await bottomSheetNavigationService.GoBackAsync(); - WeakReferenceMessenger.Default.Send(new PersonImageAddedOrChangedMessage(photoPath)); + messenger.Send(new PersonImageAddedOrChangedMessage(photoPath)); } [RelayCommand] - private async Task SelectBowlerImageFromGallery() + private async Task SelectBowlerImageFromGalleryAsync() { var photoPath = await mediaPickerService.PickPhotoAsync(); if (string.IsNullOrEmpty(photoPath)) @@ -47,6 +48,6 @@ private async Task SelectBowlerImageFromGallery() } await bottomSheetNavigationService.GoBackAsync(); - WeakReferenceMessenger.Default.Send(new PersonImageAddedOrChangedMessage(photoPath)); + messenger.Send(new PersonImageAddedOrChangedMessage(photoPath)); } } diff --git a/HangTab/ViewModels/BowlerSelectSubViewModel.cs b/HangTab/ViewModels/BowlerSelectSubViewModel.cs index 65ceee9..97841e9 100644 --- a/HangTab/ViewModels/BowlerSelectSubViewModel.cs +++ b/HangTab/ViewModels/BowlerSelectSubViewModel.cs @@ -17,6 +17,7 @@ namespace HangTab.ViewModels; public partial class BowlerSelectSubViewModel( IPersonService personService, IBowlerService bowlerService, + IMessenger messenger, INavigationService navigationService) : ViewModelBase, IQueryAttributable { private Bowler? _bowler; @@ -77,7 +78,7 @@ private async Task SubmitSelectedSubAsync() } await bowlerService.UpdateBowler(MapDataToBowler()); - WeakReferenceMessenger.Default.Send(new BowlerSubChangedMessage(Id, SelectedSub.Id)); + messenger.Send(new BowlerSubChangedMessage(Id, SelectedSub.Id)); await navigationService.GoBack(); } diff --git a/HangTab/ViewModels/DataManagerViewModel.cs b/HangTab/ViewModels/DataManagerViewModel.cs index 7d8bf6d..336fc3f 100644 --- a/HangTab/ViewModels/DataManagerViewModel.cs +++ b/HangTab/ViewModels/DataManagerViewModel.cs @@ -8,6 +8,7 @@ namespace HangTab.ViewModels; public partial class DataManagerViewModel( IDialogService dialogService, IDatabaseService databaseService, + IMessenger messenger, ISettingsService settingsService) { /// @@ -62,6 +63,6 @@ private void SendSystemResetMessage() { settingsService.CurrentWeekPrimaryKey = 0; settingsService.SeasonComplete = false; - WeakReferenceMessenger.Default.Send(new SystemResetMessage()); + messenger.Send(new SystemResetMessage()); } } diff --git a/HangTab/ViewModels/PersonAddEditViewModel.cs b/HangTab/ViewModels/PersonAddEditViewModel.cs index a10096b..cbf9f1c 100644 --- a/HangTab/ViewModels/PersonAddEditViewModel.cs +++ b/HangTab/ViewModels/PersonAddEditViewModel.cs @@ -26,21 +26,24 @@ public partial class PersonAddEditViewModel : { private readonly IPersonService _personService; private readonly IDialogService _dialogService; + private readonly IMessenger _messenger; private readonly INavigationService _navigationService; private readonly IBottomSheetNavigationService _bottomSheetNavigationService; public PersonAddEditViewModel( IPersonService personService, IDialogService dialogService, + IMessenger messenger, INavigationService navigationService, IBottomSheetNavigationService bottomSheetNavigationService) { _personService = personService; _dialogService = dialogService; + _messenger = messenger; _navigationService = navigationService; _bottomSheetNavigationService = bottomSheetNavigationService; - WeakReferenceMessenger.Default.Register(this); + _messenger.Register(this); ErrorsChanged += AddBowlerViewModel_ErrorsChanged; } @@ -118,7 +121,7 @@ private async Task DeletePersonAsync() { if (await _personService.DeletePerson(_person.Id)) { - WeakReferenceMessenger.Default.Send(new PersonDeletedMessage(_person.Id)); + _messenger.Send(new PersonDeletedMessage(_person.Id)); } else { @@ -154,7 +157,7 @@ private async Task SubmitAsync() { if (await _personService.AddPerson(person)) { - WeakReferenceMessenger.Default.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); + _messenger.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); } else { @@ -166,7 +169,7 @@ private async Task SubmitAsync() { if (await _personService.UpdatePerson(person)) { - WeakReferenceMessenger.Default.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); + _messenger.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); } else { From 26b50293191a7bb9753a8e2b68ef9302dc06e0fb Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:22:24 -0400 Subject: [PATCH 03/12] added async to method names --- .../ViewModels/BowlerSelectSubViewModel.cs | 8 ++--- .../CurrentWeekOverviewViewModel.cs | 30 +++++++++---------- .../ViewModels/PersonListOverviewViewModel.cs | 25 ++++++++-------- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/HangTab/ViewModels/BowlerSelectSubViewModel.cs b/HangTab/ViewModels/BowlerSelectSubViewModel.cs index 97841e9..5b51665 100644 --- a/HangTab/ViewModels/BowlerSelectSubViewModel.cs +++ b/HangTab/ViewModels/BowlerSelectSubViewModel.cs @@ -41,7 +41,7 @@ public override async Task LoadAsync() await Loading( async () => { - await GetSubs(); + await GetSubsAsync(); if (_bowler is not null) { @@ -82,9 +82,9 @@ private async Task SubmitSelectedSubAsync() await navigationService.GoBack(); } - private async Task GetSubs() + private async Task GetSubsAsync() { - var subs = await GetAvailableSubs(); + var subs = await GetAvailableSubsAsync(); if (subs.Any()) { Subs.Clear(); @@ -92,7 +92,7 @@ private async Task GetSubs() } } - private async Task> GetAvailableSubs() + private async Task> GetAvailableSubsAsync() { var subs = await personService.GetSubstitutes(); if (!subs.Any()) diff --git a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs index dba739a..7c69029 100644 --- a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs +++ b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs @@ -123,23 +123,23 @@ public override async Task LoadAsync() if (CurrentWeekBowlers.Count == 0) { - await Loading(GetCurrentWeek); + await Loading(GetCurrentWeekAsync); } InitializeCurrentWeekPageSettings(); } [RelayCommand] - private async Task SetBowlerStatusToActive(CurrentWeekListItemViewModel? vm) => await SetBowlerStatus(vm, Enums.Status.Active); + private async Task SetBowlerStatusToActiveAsync(CurrentWeekListItemViewModel? vm) => await SetBowlerStatusAsync(vm, Enums.Status.Active); [RelayCommand] - private async Task SetBowlerStatusToBlind(CurrentWeekListItemViewModel? vm) => await SetBowlerStatus(vm, Enums.Status.Blind); + private async Task SetBowlerStatusToBlindAsync(CurrentWeekListItemViewModel? vm) => await SetBowlerStatusAsync(vm, Enums.Status.Blind); [RelayCommand] - private async Task SetBowlerStatusToUsingSub(CurrentWeekListItemViewModel? vm) => await SetBowlerStatus(vm, Enums.Status.UsingSub); + private async Task SetBowlerStatusToUsingSubAsync(CurrentWeekListItemViewModel? vm) => await SetBowlerStatusAsync(vm, Enums.Status.UsingSub); [RelayCommand] - private async Task ShareScreenshot() + private async Task ShareScreenshotAsync() { var screenshot = await _screenshotService.TakeScreenshotAsync(); if (string.IsNullOrWhiteSpace(screenshot)) @@ -153,7 +153,7 @@ private async Task ShareScreenshot() } [RelayCommand] - private async Task SubmitWeek() + private async Task SubmitWeekAsync() { if (!await _dialogService.Ask("Complete Week", $"Are you ready to complete week {CurrentWeek.Number}?", "Yes", "No")) { @@ -162,7 +162,7 @@ private async Task SubmitWeek() if (CurrentWeek.Number < _settingsService.TotalSeasonWeeks) { - await Loading(StartNewWeek); + await Loading(StartNewWeekAsync); } else { @@ -171,7 +171,7 @@ private async Task SubmitWeek() } } - private async Task StartNewWeek() + private async Task StartNewWeekAsync() { await _weekService.CreateWeek(CurrentWeek.Number + 1).ContinueWith(async saveTask => { @@ -179,7 +179,7 @@ await _weekService.CreateWeek(CurrentWeek.Number + 1).ContinueWith(async saveTas { var newWeek = await saveTask; _settingsService.CurrentWeekPrimaryKey = newWeek.Id; - await GetCurrentWeek(); + await GetCurrentWeekAsync(); InitializeCurrentWeekPageSettings(); } else @@ -189,7 +189,7 @@ await _weekService.CreateWeek(CurrentWeek.Number + 1).ContinueWith(async saveTas }); } - private async Task GetCurrentWeek() + private async Task GetCurrentWeekAsync() { CurrentWeek = await _weekService.GetWeekById(_settingsService.CurrentWeekPrimaryKey); if (CurrentWeek is null) @@ -222,7 +222,7 @@ private void MapWeekData(Week week) BusRides = week.BusRides; } - private async Task SetBowlerStatus(CurrentWeekListItemViewModel? vm, Enums.Status status) + private async Task SetBowlerStatusAsync(CurrentWeekListItemViewModel? vm, Enums.Status status) { if (vm is null) { @@ -246,7 +246,7 @@ private async Task SetBowlerStatus(CurrentWeekListItemViewModel? vm, Enums.Statu return; } - await GetCurrentWeek(); + await GetCurrentWeekAsync(); } [RelayCommand] @@ -285,7 +285,7 @@ public async void Receive(SystemResetMessage message) TeamHangTotal = 0; IsEnableCompleteWeek = false; PageTitle = "Week 1"; - await GetCurrentWeek(); + await GetCurrentWeekAsync(); } public async void Receive(BowlerSubChangedMessage message) @@ -300,7 +300,7 @@ public async void Receive(BowlerSubChangedMessage message) vm.Status = Enums.Status.UsingSub; await _bowlerService.UpdateBowler(vm.ToBowler()); - await GetCurrentWeek(); + await GetCurrentWeekAsync(); } public async void Receive(PersonAddedOrChangedMessage message) @@ -324,7 +324,7 @@ public async void Receive(PersonAddedOrChangedMessage message) } CurrentWeekBowlers.Clear(); - await GetCurrentWeek(); + await GetCurrentWeekAsync(); } public void Receive(PersonDeletedMessage message) diff --git a/HangTab/ViewModels/PersonListOverviewViewModel.cs b/HangTab/ViewModels/PersonListOverviewViewModel.cs index be87550..5ee3451 100644 --- a/HangTab/ViewModels/PersonListOverviewViewModel.cs +++ b/HangTab/ViewModels/PersonListOverviewViewModel.cs @@ -74,18 +74,18 @@ partial void OnSearchTextChanged(string value) } else { - Task.Run(async () => { await SearchBowlers(value); }).Wait(); + SearchBowlers(value); } } - // TODO: Add filtering by Subs,Regulars + // TODO: Add filtering by Subs,Regulars (using chips?) // TODO: Add sort by most hangs,least hangs, name asc/desc [RelayCommand] - private async Task NavigateToAddBowler() => await _navigationService.GoToAddBowler(); + private async Task NavigateToAddBowlerAsync() => await _navigationService.GoToAddBowler(); [RelayCommand] - private async Task NavigateToEditSelectedBowler() + private async Task NavigateToEditSelectedBowlerAsync() { if (SelectedBowler is not null) { @@ -98,11 +98,11 @@ public override async Task LoadAsync() { if (Bowlers.Count == 0) { - await Loading(GetBowlers); + await Loading(GetBowlersAsync); } } - private async Task GetBowlers() + private async Task GetBowlersAsync() { var people = await _personService.GetAllPeople(); if (!people.Any()) @@ -114,10 +114,10 @@ private async Task GetBowlers() AllBowlers = people.OrderBy(b => b.Name).ToBowlerListItemViewModelList(); Bowlers = AllBowlers.ToObservableCollection(); - await UpdateBowlerHangCounts(); + await UpdateBowlerHangCountsAsync(); } - private async Task UpdateBowlerHangCounts() + private async Task UpdateBowlerHangCountsAsync() { if (Bowlers.Count > 0) { @@ -132,11 +132,10 @@ private async Task UpdateBowlerHangCounts() } } - private Task SearchBowlers(string searchText) + private void SearchBowlers(string searchText) { Bowlers.Clear(); Bowlers = AllBowlers.Where(b => b.Name.Contains(searchText, StringComparison.OrdinalIgnoreCase)).ToObservableCollection(); - return Task.CompletedTask; } public void Receive(SystemResetMessage message) @@ -145,9 +144,9 @@ public void Receive(SystemResetMessage message) AllBowlers = []; } - public async void Receive(BowlerHangCountChangedMessage message) => await UpdateBowlerHangCounts(); + public async void Receive(BowlerHangCountChangedMessage message) => await UpdateBowlerHangCountsAsync(); - public async void Receive(PersonAddedOrChangedMessage message) => await GetBowlers(); + public async void Receive(PersonAddedOrChangedMessage message) => await GetBowlersAsync(); - public async void Receive(PersonDeletedMessage message) => await GetBowlers(); + public async void Receive(PersonDeletedMessage message) => await GetBowlersAsync(); } From b88faaef181a8b25f8db861d152950b2c2f05c67 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:57:55 -0500 Subject: [PATCH 04/12] changed asynchronous method names to include async in the name --- .../Repositories/BowlerRepositoryTests.cs | 36 +++++----- .../Repositories/WeekRepositoryTests.cs | 18 +++-- HangTab.Tests/Services/BowlerServiceTests.cs | 72 +++++++++---------- .../Services/DatabaseServiceTests.cs | 38 +++++----- HangTab.Tests/Services/WeekServiceTests.cs | 36 +++++----- HangTab/App.xaml.cs | 2 +- HangTab/Repositories/IBowlerRepository.cs | 12 ++-- HangTab/Repositories/IDatabaseRepository.cs | 6 +- HangTab/Repositories/IWeekRepository.cs | 8 +-- HangTab/Repositories/Impl/BowlerRepository.cs | 12 ++-- .../Repositories/Impl/DatabaseRepository.cs | 6 +- HangTab/Repositories/Impl/WeekRepository.cs | 10 +-- HangTab/Services/IBowlerService.cs | 12 ++-- HangTab/Services/IDatabaseService.cs | 6 +- HangTab/Services/IWeekService.cs | 8 +-- HangTab/Services/Impl/BowlerService.cs | 12 ++-- HangTab/Services/Impl/DatabaseService.cs | 6 +- HangTab/Services/Impl/WeekService.cs | 8 +-- .../ViewModels/BowlerSelectSubViewModel.cs | 6 +- .../CurrentWeekOverviewViewModel.cs | 18 ++--- HangTab/ViewModels/DataManagerViewModel.cs | 4 +- HangTab/ViewModels/SeasonSummaryViewModel.cs | 6 +- HangTab/ViewModels/WeekDetailsViewModel.cs | 2 +- .../ViewModels/WeekListOverviewViewModel.cs | 2 +- 24 files changed, 172 insertions(+), 174 deletions(-) diff --git a/HangTab.Tests/Repositories/BowlerRepositoryTests.cs b/HangTab.Tests/Repositories/BowlerRepositoryTests.cs index 00ccb04..859e1df 100644 --- a/HangTab.Tests/Repositories/BowlerRepositoryTests.cs +++ b/HangTab.Tests/Repositories/BowlerRepositoryTests.cs @@ -18,7 +18,7 @@ public async Task AddBowler_CallsContextAndReturnsResult() A.CallTo(() => context.AddItemAsync(A._)).Returns(true); // Act - var actual = await bowlerRepository.AddBowler(bowler); + var actual = await bowlerRepository.AddBowlerAsync(bowler); // Assert Assert.True(actual); @@ -37,7 +37,7 @@ public async Task GetAllBowlersByWeekId_CallsContextWithCorrectPredicate() .Returns(expected); // Act - var actual = await bowlerRepository.GetAllBowlersByWeekId(weekId); + var actual = await bowlerRepository.GetAllBowlersByWeekIdAsync(weekId); // Assert Assert.Equal(expected, actual); @@ -55,7 +55,7 @@ public async Task GetAllBowlers_CallsContext() A.CallTo(() => context.GetAllWithChildrenAsync(null)).Returns(expected); // Act - var actual = await bowlerRepository.GetAllBowlers(); + var actual = await bowlerRepository.GetAllBowlersAsync(); // Assert Assert.Equal(expected, actual); @@ -73,7 +73,7 @@ public async Task GetBowlerById_CallsContextWithId() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(bowler); // Act - var result = await bowlerRepository.GetBowlerById(id); + var result = await bowlerRepository.GetBowlerByIdAsync(id); // Assert Assert.Equal(bowler, result); @@ -92,7 +92,7 @@ public async Task GetBowlersByWeekId_CallsContextWithCorrectPredicate() .Returns(expected); // Act - var actual = await bowlerRepository.GetAllBowlersByWeekId(weekId); + var actual = await bowlerRepository.GetAllBowlersByWeekIdAsync(weekId); // Assert Assert.Equal(expected, actual); @@ -110,7 +110,7 @@ public async Task UpdateBowler_CallsContextAndReturnsResult() A.CallTo(() => context.UpdateItemAsync(A._)).Returns(true); // Act - var result = await bowlerRepository.UpdateBowler(bowler); + var result = await bowlerRepository.UpdateBowlerAsync(bowler); // Assert Assert.True(result); @@ -125,7 +125,7 @@ public async Task AddBowler_ThrowsOnNull() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.AddBowler(null!)); + await Assert.ThrowsAsync(() => bowlerRepository.AddBowlerAsync(null!)); } [Fact] @@ -136,8 +136,8 @@ public async Task GetAllBowlersByWeekId_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekId(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekId(-1)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(-1)); } [Fact] @@ -148,8 +148,8 @@ public async Task GetBowlerById_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetBowlerById(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetBowlerById(-5)); + await Assert.ThrowsAsync(() => bowlerRepository.GetBowlerByIdAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.GetBowlerByIdAsync(-5)); } [Fact] @@ -160,8 +160,8 @@ public async Task GetBowlersByWeekId_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekId(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekId(-2)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(-2)); } [Fact] @@ -172,7 +172,7 @@ public async Task UpdateBowler_ThrowsOnNull() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.UpdateBowler(null!)); + await Assert.ThrowsAsync(() => bowlerRepository.UpdateBowlerAsync(null!)); } [Fact] @@ -185,7 +185,7 @@ public async Task RemoveBowler_CallsContextAndReturnsTrue() A.CallTo(() => context.DeleteItemByIdAsync(id)).Returns(true); // Act - var result = await bowlerRepository.RemoveBowler(id); + var result = await bowlerRepository.RemoveBowlerAsync(id); // Assert Assert.True(result); @@ -202,7 +202,7 @@ public async Task RemoveBowler_CallsContextAndReturnsFalse() A.CallTo(() => context.DeleteItemByIdAsync(id)).Returns(false); // Act - var result = await bowlerRepository.RemoveBowler(id); + var result = await bowlerRepository.RemoveBowlerAsync(id); // Assert Assert.False(result); @@ -217,7 +217,7 @@ public async Task RemoveBowler_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.RemoveBowler(0)); - await Assert.ThrowsAsync(() => bowlerRepository.RemoveBowler(-1)); + await Assert.ThrowsAsync(() => bowlerRepository.RemoveBowlerAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.RemoveBowlerAsync(-1)); } } \ No newline at end of file diff --git a/HangTab.Tests/Repositories/WeekRepositoryTests.cs b/HangTab.Tests/Repositories/WeekRepositoryTests.cs index bb10ef4..71848f2 100644 --- a/HangTab.Tests/Repositories/WeekRepositoryTests.cs +++ b/HangTab.Tests/Repositories/WeekRepositoryTests.cs @@ -10,8 +10,6 @@ public class WeekRepositoryTests { private readonly IDatabaseContext _context = A.Fake(); - private WeekRepository CreateRepo() => new(_context); - [Fact] public async Task GetWeekById_WithValidId_ReturnsWeekWithBowlers() { @@ -33,7 +31,7 @@ public async Task GetWeekById_WithValidId_ReturnsWeekWithBowlers() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(person); // Act - var actual = await weekRepository.GetWeekById(weekId); + var actual = await weekRepository.GetWeekByIdAsync(weekId); // Assert Assert.Equal(weekId, actual.Id); @@ -55,7 +53,7 @@ public async Task GetWeekById_WithIdLessThan1_CreatesWeek() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(createdWeek); // Act - var actual = await weekRepository.GetWeekById(0); + var actual = await weekRepository.GetWeekByIdAsync(0); // Assert Assert.Equal(1, actual.Number); @@ -68,10 +66,10 @@ public async Task GetWeekById_WeekNotFound_ReturnsNewWeek() var weekId = 99; var context = A.Fake(); var weekRepository = new WeekRepository(context); - A.CallTo(() => context.GetItemByIdAsync(weekId)).Returns((Week?)null); + A.CallTo(() => context.GetItemByIdAsync(A._)).Returns((Week?)null!); // Act - var actual = await weekRepository.GetWeekById(weekId); + var actual = await weekRepository.GetWeekByIdAsync(weekId); // Assert Assert.NotNull(actual); @@ -88,7 +86,7 @@ public async Task GetAllWeeks_CallsContext() A.CallTo(() => context.GetAllWithChildrenAsync(null)).Returns(weeks); // Act - var actual = await weekRepository.GetAllWeeks(); + var actual = await weekRepository.GetAllWeeksAsync(); // Assert Assert.Equal(weeks, actual); @@ -111,7 +109,7 @@ public async Task CreateWeek_AddsWeekAndBowlers() A.CallTo(() => context.AddItemAsync(A._)).Returns(true); // Act - var actual = await weekRepository.CreateWeek(3); + var actual = await weekRepository.CreateWeekAsync(3); // Assert Assert.Equal(3, actual.Number); @@ -129,7 +127,7 @@ public async Task CreateWeek_WithNoPeople_AddsWeekOnly() A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns([]); // Act - var actual = await weekRepository.CreateWeek(2); + var actual = await weekRepository.CreateWeekAsync(2); // Assert Assert.Equal(2, actual.Number); @@ -147,7 +145,7 @@ public async Task UpdateWeek_CallsContext() A.CallTo(() => context.UpdateWithChildrenAsync(A._)).Returns(Task.CompletedTask); // Act - await weekRepository.UpdateWeek(week); + await weekRepository.UpdateWeekAsync(week); // Assert A.CallTo(() => context.UpdateWithChildrenAsync(A._)).MustHaveHappenedOnceExactly(); diff --git a/HangTab.Tests/Services/BowlerServiceTests.cs b/HangTab.Tests/Services/BowlerServiceTests.cs index 5124b61..1656d45 100644 --- a/HangTab.Tests/Services/BowlerServiceTests.cs +++ b/HangTab.Tests/Services/BowlerServiceTests.cs @@ -13,14 +13,14 @@ public async Task AddBowler_ValidBowler_ReturnsTrue() var bowler = new Bowler { Id = 1 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddBowler(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).Returns(Task.FromResult(true)); // Act - var result = await service.AddBowler(bowler); + var result = await service.AddBowlerAsync(bowler); // Assert Assert.True(result); - A.CallTo(() => bowlerRepo.AddBowler(bowler)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.AddBowlerAsync(bowler)).MustHaveHappenedOnceExactly(); } [Fact] @@ -30,14 +30,14 @@ public async Task AddBowler_RepositoryReturnsFalse_ReturnsFalse() var bowler = new Bowler { Id = 2 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddBowler(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).Returns(Task.FromResult(false)); // Act - var result = await service.AddBowler(bowler); + var result = await service.AddBowlerAsync(bowler); // Assert Assert.False(result); - A.CallTo(() => bowlerRepo.AddBowler(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -53,14 +53,14 @@ public async Task GetAllBowlersByWeekId_ValidId_ReturnsBowlers() var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekId(A._)).Returns(Task.FromResult>(expected)); + A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); // Act - var result = await service.GetAllBowlersByWeekId(weeekId); + var result = await service.GetAllBowlersByWeekIdAsync(weeekId); // Assert Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekId(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -69,14 +69,14 @@ public async Task GetAllBowlers_RepositoryReturnsEmpty_ReturnsEmpty() // Arrange var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllBowlers()).Returns(Task.FromResult>([])); + A.CallTo(() => bowlerRepo.GetAllBowlersAsync()).Returns(Task.FromResult>([])); // Act - var result = await service.GetAllBowlers(); + var result = await service.GetAllBowlersAsync(); // Assert Assert.Empty(result); - A.CallTo(() => bowlerRepo.GetAllBowlers()).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetAllBowlersAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -87,14 +87,14 @@ public async Task GetBowlerById_ValidId_ReturnsBowler() var expected = new Bowler { Id = id }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetBowlerById(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => bowlerRepo.GetBowlerByIdAsync(A._)).Returns(Task.FromResult(expected)); // Act - var result = await service.GetBowlerById(id); + var result = await service.GetBowlerByIdAsync(id); // Assert Assert.Equal(expected.Id, result.Id); - A.CallTo(() => bowlerRepo.GetBowlerById(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetBowlerByIdAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -105,15 +105,15 @@ public async Task GetBowlersByWeekId_ValidId_ReturnsBowlers() var expected = new List { new() { Id = 3, WeekId = id } }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekId(A._)).Returns(Task.FromResult>(expected)); + A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); // Act - var result = await service.GetAllBowlersByWeekId(id); + var result = await service.GetAllBowlersByWeekIdAsync(id); // Assert Assert.Single(result); Assert.Equal(expected[0].Id, result.First().Id); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekId(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -123,14 +123,14 @@ public async Task RemoveBowler_ValidId_ReturnsTrue() var id = 1; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveBowler(id)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).Returns(Task.FromResult(true)); // Act - var result = await service.RemoveBowler(id); + var result = await service.RemoveBowlerAsync(id); // Assert Assert.True(result); - A.CallTo(() => bowlerRepo.RemoveBowler(id)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).MustHaveHappenedOnceExactly(); } [Fact] @@ -140,14 +140,14 @@ public async Task RemoveBowler_InvalidId_ReturnsFalse() var id = 999; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveBowler(id)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).Returns(Task.FromResult(false)); // Act - var result = await service.RemoveBowler(id); + var result = await service.RemoveBowlerAsync(id); // Assert Assert.False(result); - A.CallTo(() => bowlerRepo.RemoveBowler(id)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).MustHaveHappenedOnceExactly(); } [Fact] @@ -157,10 +157,10 @@ public async Task RemoveBowler_RepositoryThrowsException_PropagatesException() var id = 2; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveBowler(id)).Throws(new InvalidOperationException("Repository error")); + A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).Throws(new InvalidOperationException("Repository error")); // Act & Assert - await Assert.ThrowsAsync(() => service.RemoveBowler(id)); + await Assert.ThrowsAsync(() => service.RemoveBowlerAsync(id)); } [Fact] @@ -170,14 +170,14 @@ public async Task UpdateBowler_RepositoryReturnsTrue_ReturnsTrue() var expected = new Bowler { Id = 4 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateBowler(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).Returns(Task.FromResult(true)); // Act - var result = await service.UpdateBowler(expected); + var result = await service.UpdateBowlerAsync(expected); // Assert Assert.True(result); - A.CallTo(() => bowlerRepo.UpdateBowler(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -187,14 +187,14 @@ public async Task UpdateBowler_RepositoryReturnsFalse_ReturnsFalse() var expected = new Bowler { Id = 5 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateBowler(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).Returns(Task.FromResult(false)); // Act - var result = await service.UpdateBowler(expected); + var result = await service.UpdateBowlerAsync(expected); // Assert Assert.False(result); - A.CallTo(() => bowlerRepo.UpdateBowler(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -203,10 +203,10 @@ public async Task AddBowler_NullBowler_ThrowsArgumentNullException() // Arrange var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddBowler(A._)).Throws(new ArgumentNullException(nameof(Bowler))); + A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); // Act & Assert - await Assert.ThrowsAsync(() => service.AddBowler(null!)); + await Assert.ThrowsAsync(() => service.AddBowlerAsync(null!)); } [Fact] @@ -215,9 +215,9 @@ public async Task UpdateBowler_NullBowler_ThrowsArgumentNullException() // Arrange var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateBowler(A._)).Throws(new ArgumentNullException(nameof(Bowler))); + A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); // Act & Assert - await Assert.ThrowsAsync(() => service.UpdateBowler(null!)); + await Assert.ThrowsAsync(() => service.UpdateBowlerAsync(null!)); } } \ No newline at end of file diff --git a/HangTab.Tests/Services/DatabaseServiceTests.cs b/HangTab.Tests/Services/DatabaseServiceTests.cs index eda5350..3a5ed41 100644 --- a/HangTab.Tests/Services/DatabaseServiceTests.cs +++ b/HangTab.Tests/Services/DatabaseServiceTests.cs @@ -11,14 +11,14 @@ public async Task DeleteAllData_RepositoryReturnsTrue_ReturnsTrue() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.DeleteAllData()).Returns(Task.FromResult(true)); + A.CallTo(() => databaseRepo.DeleteAllDataAsync()).Returns(Task.FromResult(true)); // Act - var result = await service.DeleteAllData(); + var result = await service.DeleteAllDataAsync(); // Assert Assert.True(result); - A.CallTo(() => databaseRepo.DeleteAllData()).MustHaveHappenedOnceExactly(); + A.CallTo(() => databaseRepo.DeleteAllDataAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -27,14 +27,14 @@ public async Task DeleteAllData_RepositoryReturnsFalse_ReturnsFalse() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.DeleteAllData()).Returns(Task.FromResult(false)); + A.CallTo(() => databaseRepo.DeleteAllDataAsync()).Returns(Task.FromResult(false)); // Act - var result = await service.DeleteAllData(); + var result = await service.DeleteAllDataAsync(); // Assert Assert.False(result); - A.CallTo(() => databaseRepo.DeleteAllData()).MustHaveHappenedOnceExactly(); + A.CallTo(() => databaseRepo.DeleteAllDataAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -43,10 +43,10 @@ public async Task DeleteAllData_RepositoryThrows_PropagatesException() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.DeleteAllData()).Throws(new InvalidOperationException("fail")); + A.CallTo(() => databaseRepo.DeleteAllDataAsync()).Throws(new InvalidOperationException("fail")); // Act & Assert - await Assert.ThrowsAsync(service.DeleteAllData); + await Assert.ThrowsAsync(service.DeleteAllDataAsync); } [Fact] @@ -55,14 +55,14 @@ public async Task DeleteSeasonData_RepositoryReturnsTrue_ReturnsTrue() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.DeleteSeasonData()).Returns(Task.FromResult(true)); + A.CallTo(() => databaseRepo.DeleteSeasonDataAsync()).Returns(Task.FromResult(true)); // Act - var result = await service.DeleteSeasonData(); + var result = await service.DeleteSeasonDataAsync(); // Assert Assert.True(result); - A.CallTo(() => databaseRepo.DeleteSeasonData()).MustHaveHappenedOnceExactly(); + A.CallTo(() => databaseRepo.DeleteSeasonDataAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -71,14 +71,14 @@ public async Task DeleteSeasonData_RepositoryReturnsFalse_ReturnsFalse() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.DeleteSeasonData()).Returns(Task.FromResult(false)); + A.CallTo(() => databaseRepo.DeleteSeasonDataAsync()).Returns(Task.FromResult(false)); // Act - var result = await service.DeleteSeasonData(); + var result = await service.DeleteSeasonDataAsync(); // Assert Assert.False(result); - A.CallTo(() => databaseRepo.DeleteSeasonData()).MustHaveHappenedOnceExactly(); + A.CallTo(() => databaseRepo.DeleteSeasonDataAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -87,10 +87,10 @@ public async Task DeleteSeasonData_RepositoryThrows_PropagatesException() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.DeleteSeasonData()).Throws(new Exception("fail")); + A.CallTo(() => databaseRepo.DeleteSeasonDataAsync()).Throws(new Exception("fail")); // Act & Assert - await Assert.ThrowsAsync(service.DeleteSeasonData); + await Assert.ThrowsAsync(service.DeleteSeasonDataAsync); } [Fact] @@ -99,12 +99,12 @@ public async Task InitializeDatabase_CallsRepositoryInitializeDatabase() // Arrange var databaseRepo = A.Fake(); var service = new DatabaseService(databaseRepo); - A.CallTo(() => databaseRepo.InitializeDatabase()).Returns(Task.CompletedTask); + A.CallTo(() => databaseRepo.InitializeDatabaseAsync()).Returns(Task.CompletedTask); // Act - await service.InitializeDatabase(); + await service.InitializeDatabaseAsync(); // Assert - A.CallTo(() => databaseRepo.InitializeDatabase()).MustHaveHappenedOnceExactly(); + A.CallTo(() => databaseRepo.InitializeDatabaseAsync()).MustHaveHappenedOnceExactly(); } } \ No newline at end of file diff --git a/HangTab.Tests/Services/WeekServiceTests.cs b/HangTab.Tests/Services/WeekServiceTests.cs index ef92455..7cc6998 100644 --- a/HangTab.Tests/Services/WeekServiceTests.cs +++ b/HangTab.Tests/Services/WeekServiceTests.cs @@ -13,15 +13,15 @@ public async Task GetWeekById_ValidId_ReturnsWeek() var expected = new Week { Id = 1, Number = 7 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetWeekById(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => weekRepo.GetWeekByIdAsync(A._)).Returns(Task.FromResult(expected)); // Act - var result = await service.GetWeekById(1); + var result = await service.GetWeekByIdAsync(1); // Assert Assert.Equal(expected.Id, result.Id); Assert.Equal(expected.Number, result.Number); - A.CallTo(() => weekRepo.GetWeekById(1)).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.GetWeekByIdAsync(1)).MustHaveHappenedOnceExactly(); } [Fact] @@ -31,14 +31,14 @@ public async Task GetAllWeeks_RepositoryReturnsWeeks_ReturnsWeeks() var expected = new List { new() { Id = 1 }, new() { Id = 2 } }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetAllWeeks()).Returns(Task.FromResult>(expected)); + A.CallTo(() => weekRepo.GetAllWeeksAsync()).Returns(Task.FromResult>(expected)); // Act - var result = await service.GetAllWeeks(); + var result = await service.GetAllWeeksAsync(); // Assert Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => weekRepo.GetAllWeeks()).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.GetAllWeeksAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -48,15 +48,15 @@ public async Task CreateWeek_ValidNumber_ReturnsWeek() var expected = new Week { Id = 3, Number = 4 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.CreateWeek(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => weekRepo.CreateWeekAsync(A._)).Returns(Task.FromResult(expected)); // Act - var result = await service.CreateWeek(4); + var result = await service.CreateWeekAsync(4); // Assert Assert.Equal(expected.Id, result.Id); Assert.Equal(expected.Number, result.Number); - A.CallTo(() => weekRepo.CreateWeek(4)).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.CreateWeekAsync(4)).MustHaveHappenedOnceExactly(); } [Fact] @@ -66,13 +66,13 @@ public async Task UpdateWeek_ValidWeek_CallsRepository() var week = new Week { Id = 5, Number = 6 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.UpdateWeek(A._)).Returns(Task.CompletedTask); + A.CallTo(() => weekRepo.UpdateWeekAsync(A._)).Returns(Task.CompletedTask); // Act - await service.UpdateWeek(week); + await service.UpdateWeekAsync(week); // Assert - A.CallTo(() => weekRepo.UpdateWeek(week)).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.UpdateWeekAsync(week)).MustHaveHappenedOnceExactly(); } [Fact] @@ -82,10 +82,10 @@ public async Task GetWeekById_RepositoryThrows_PropagatesException() var id = 99; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetWeekById(A._)).Throws(new InvalidOperationException("Not found")); + A.CallTo(() => weekRepo.GetWeekByIdAsync(A._)).Throws(new InvalidOperationException("Not found")); // Act & Assert - await Assert.ThrowsAsync(() => service.GetWeekById(id)); + await Assert.ThrowsAsync(() => service.GetWeekByIdAsync(id)); } [Fact] @@ -95,10 +95,10 @@ public async Task CreateWeek_RepositoryThrows_PropagatesException() var id = 42; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.CreateWeek(A._)).Throws(new Exception("Create failed")); + A.CallTo(() => weekRepo.CreateWeekAsync(A._)).Throws(new Exception("Create failed")); // Act & Assert - await Assert.ThrowsAsync(() => service.CreateWeek(id)); + await Assert.ThrowsAsync(() => service.CreateWeekAsync(id)); } [Fact] @@ -108,9 +108,9 @@ public async Task UpdateWeek_RepositoryThrows_PropagatesException() var week = new Week { Id = 7, Number = 8 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.UpdateWeek(A._)).Throws(new Exception("Update failed")); + A.CallTo(() => weekRepo.UpdateWeekAsync(A._)).Throws(new Exception("Update failed")); // Act & Assert - await Assert.ThrowsAsync(() => service.UpdateWeek(week)); + await Assert.ThrowsAsync(() => service.UpdateWeekAsync(week)); } } diff --git a/HangTab/App.xaml.cs b/HangTab/App.xaml.cs index df5a17b..75bce7a 100644 --- a/HangTab/App.xaml.cs +++ b/HangTab/App.xaml.cs @@ -28,7 +28,7 @@ private void InitializeDatabase() return; } - databaseService.InitializeDatabase(); + databaseService.InitializeDatabaseAsync(); } private void SetCurrentUserSelectedTheme() diff --git a/HangTab/Repositories/IBowlerRepository.cs b/HangTab/Repositories/IBowlerRepository.cs index 6308df4..44a581e 100644 --- a/HangTab/Repositories/IBowlerRepository.cs +++ b/HangTab/Repositories/IBowlerRepository.cs @@ -3,10 +3,10 @@ namespace HangTab.Repositories; public interface IBowlerRepository { - Task AddBowler(Bowler bowler); - Task> GetAllBowlersByWeekId(int id); - Task> GetAllBowlers(); - Task GetBowlerById(int id); - Task RemoveBowler(int id); - Task UpdateBowler(Bowler bowler); + Task AddBowlerAsync(Bowler bowler); + Task> GetAllBowlersByWeekIdAsync(int id); + Task> GetAllBowlersAsync(); + Task GetBowlerByIdAsync(int id); + Task RemoveBowlerAsync(int id); + Task UpdateBowlerAsync(Bowler bowler); } diff --git a/HangTab/Repositories/IDatabaseRepository.cs b/HangTab/Repositories/IDatabaseRepository.cs index 08294e3..85100f1 100644 --- a/HangTab/Repositories/IDatabaseRepository.cs +++ b/HangTab/Repositories/IDatabaseRepository.cs @@ -1,7 +1,7 @@ namespace HangTab.Repositories; public interface IDatabaseRepository { - Task DeleteAllData(); - Task DeleteSeasonData(); - Task InitializeDatabase(); + Task DeleteAllDataAsync(); + Task DeleteSeasonDataAsync(); + Task InitializeDatabaseAsync(); } diff --git a/HangTab/Repositories/IWeekRepository.cs b/HangTab/Repositories/IWeekRepository.cs index f0be47d..03ba649 100644 --- a/HangTab/Repositories/IWeekRepository.cs +++ b/HangTab/Repositories/IWeekRepository.cs @@ -3,8 +3,8 @@ namespace HangTab.Repositories; public interface IWeekRepository { - Task GetWeekById(int id); - Task> GetAllWeeks(); - Task CreateWeek(int weekNumber = 1); - Task UpdateWeek(Week week); + Task GetWeekByIdAsync(int id); + Task> GetAllWeeksAsync(); + Task CreateWeekAsync(int weekNumber = 1); + Task UpdateWeekAsync(Week week); } diff --git a/HangTab/Repositories/Impl/BowlerRepository.cs b/HangTab/Repositories/Impl/BowlerRepository.cs index 9a980a9..6804c05 100644 --- a/HangTab/Repositories/Impl/BowlerRepository.cs +++ b/HangTab/Repositories/Impl/BowlerRepository.cs @@ -4,33 +4,33 @@ namespace HangTab.Repositories.Impl; public class BowlerRepository(IDatabaseContext context) : IBowlerRepository { - public Task AddBowler(Bowler bowler) + public Task AddBowlerAsync(Bowler bowler) { ArgumentNullException.ThrowIfNull(bowler); return context.AddItemAsync(bowler); } - public Task> GetAllBowlersByWeekId(int id) + public Task> GetAllBowlersByWeekIdAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.GetAllWithChildrenAsync(wl => wl.WeekId == id); } - public Task> GetAllBowlers() => context.GetAllWithChildrenAsync(); + public Task> GetAllBowlersAsync() => context.GetAllWithChildrenAsync(); - public Task GetBowlerById(int id) + public Task GetBowlerByIdAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.GetItemByIdAsync(id); } - public Task RemoveBowler(int id) + public Task RemoveBowlerAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.DeleteItemByIdAsync(id); } - public Task UpdateBowler(Bowler bowler) + public Task UpdateBowlerAsync(Bowler bowler) { ArgumentNullException.ThrowIfNull(bowler); return context.UpdateItemAsync(bowler); diff --git a/HangTab/Repositories/Impl/DatabaseRepository.cs b/HangTab/Repositories/Impl/DatabaseRepository.cs index 0404129..881880f 100644 --- a/HangTab/Repositories/Impl/DatabaseRepository.cs +++ b/HangTab/Repositories/Impl/DatabaseRepository.cs @@ -5,20 +5,20 @@ namespace HangTab.Repositories.Impl; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "This is a Repository for the data layer and does not require unit tests.")] public class DatabaseRepository(IDatabaseContext context) : IDatabaseRepository { - public async Task DeleteAllData() + public async Task DeleteAllDataAsync() { return await context.ResetTableAsync() && await context.ResetTableAsync() && await context.ResetTableAsync(); } - public async Task DeleteSeasonData() + public async Task DeleteSeasonDataAsync() { return await context.ResetTableAsync() && await context.ResetTableAsync(); } - public async Task InitializeDatabase() + public async Task InitializeDatabaseAsync() { await context.CreateTableIfNotExists(); await context.CreateTableIfNotExists(); diff --git a/HangTab/Repositories/Impl/WeekRepository.cs b/HangTab/Repositories/Impl/WeekRepository.cs index 9a0aecd..e29d268 100644 --- a/HangTab/Repositories/Impl/WeekRepository.cs +++ b/HangTab/Repositories/Impl/WeekRepository.cs @@ -4,11 +4,11 @@ namespace HangTab.Repositories.Impl; public class WeekRepository(IDatabaseContext context) : IWeekRepository { - public async Task GetWeekById(int id) + public async Task GetWeekByIdAsync(int id) { if (id < 1) { - return await CreateWeek(); // Create the first week if no valid ID is provided + return await CreateWeekAsync(); // Create the first week if no valid ID is provided } var week = await context.GetItemByIdAsync(id); @@ -37,9 +37,9 @@ public async Task GetWeekById(int id) return week; } - public Task> GetAllWeeks() => context.GetAllWithChildrenAsync(); + public Task> GetAllWeeksAsync() => context.GetAllWithChildrenAsync(); - public async Task CreateWeek(int weekNumber = 1) + public async Task CreateWeekAsync(int weekNumber = 1) { var week = new Week { @@ -64,5 +64,5 @@ public async Task CreateWeek(int weekNumber = 1) return week; } - public Task UpdateWeek(Week week) => context.UpdateWithChildrenAsync(week); + public Task UpdateWeekAsync(Week week) => context.UpdateWithChildrenAsync(week); } diff --git a/HangTab/Services/IBowlerService.cs b/HangTab/Services/IBowlerService.cs index b8793d6..7e5bed7 100644 --- a/HangTab/Services/IBowlerService.cs +++ b/HangTab/Services/IBowlerService.cs @@ -3,10 +3,10 @@ namespace HangTab.Services; public interface IBowlerService { - Task AddBowler(Bowler bowler); - Task> GetAllBowlersByWeekId(int id); - Task> GetAllBowlers(); - Task GetBowlerById(int id); - Task RemoveBowler(int id); - Task UpdateBowler(Bowler bowler); + Task AddBowlerAsync(Bowler bowler); + Task> GetAllBowlersByWeekIdAsync(int id); + Task> GetAllBowlersAsync(); + Task GetBowlerByIdAsync(int id); + Task RemoveBowlerAsync(int id); + Task UpdateBowlerAsync(Bowler bowler); } diff --git a/HangTab/Services/IDatabaseService.cs b/HangTab/Services/IDatabaseService.cs index 08fafeb..78ca9ff 100644 --- a/HangTab/Services/IDatabaseService.cs +++ b/HangTab/Services/IDatabaseService.cs @@ -1,7 +1,7 @@ namespace HangTab.Services; public interface IDatabaseService { - Task DeleteAllData(); - Task DeleteSeasonData(); - Task InitializeDatabase(); + Task DeleteAllDataAsync(); + Task DeleteSeasonDataAsync(); + Task InitializeDatabaseAsync(); } diff --git a/HangTab/Services/IWeekService.cs b/HangTab/Services/IWeekService.cs index 78b0994..326e14b 100644 --- a/HangTab/Services/IWeekService.cs +++ b/HangTab/Services/IWeekService.cs @@ -3,8 +3,8 @@ namespace HangTab.Services; public interface IWeekService { - Task GetWeekById(int id); - Task> GetAllWeeks(); - Task CreateWeek(int weekNumber); - Task UpdateWeek(Week week); + Task GetWeekByIdAsync(int id); + Task> GetAllWeeksAsync(); + Task CreateWeekAsync(int weekNumber); + Task UpdateWeekAsync(Week week); } diff --git a/HangTab/Services/Impl/BowlerService.cs b/HangTab/Services/Impl/BowlerService.cs index 3098025..76074f4 100644 --- a/HangTab/Services/Impl/BowlerService.cs +++ b/HangTab/Services/Impl/BowlerService.cs @@ -4,10 +4,10 @@ namespace HangTab.Services.Impl; public class BowlerService(IBowlerRepository bowlerRepository) : IBowlerService { - public Task AddBowler(Bowler bowler) => bowlerRepository.AddBowler(bowler); - public Task> GetAllBowlersByWeekId(int id) => bowlerRepository.GetAllBowlersByWeekId(id); - public Task> GetAllBowlers() => bowlerRepository.GetAllBowlers(); - public Task GetBowlerById(int id) => bowlerRepository.GetBowlerById(id); - public Task RemoveBowler(int id) => bowlerRepository.RemoveBowler(id); - public Task UpdateBowler(Bowler bowler) => bowlerRepository.UpdateBowler(bowler); + public Task AddBowlerAsync(Bowler bowler) => bowlerRepository.AddBowlerAsync(bowler); + public Task> GetAllBowlersByWeekIdAsync(int id) => bowlerRepository.GetAllBowlersByWeekIdAsync(id); + public Task> GetAllBowlersAsync() => bowlerRepository.GetAllBowlersAsync(); + public Task GetBowlerByIdAsync(int id) => bowlerRepository.GetBowlerByIdAsync(id); + public Task RemoveBowlerAsync(int id) => bowlerRepository.RemoveBowlerAsync(id); + public Task UpdateBowlerAsync(Bowler bowler) => bowlerRepository.UpdateBowlerAsync(bowler); } diff --git a/HangTab/Services/Impl/DatabaseService.cs b/HangTab/Services/Impl/DatabaseService.cs index fd9a7b0..0422008 100644 --- a/HangTab/Services/Impl/DatabaseService.cs +++ b/HangTab/Services/Impl/DatabaseService.cs @@ -3,7 +3,7 @@ namespace HangTab.Services.Impl; public class DatabaseService(IDatabaseRepository databaseRepository) : IDatabaseService { - public Task DeleteAllData() => databaseRepository.DeleteAllData(); - public Task DeleteSeasonData() => databaseRepository.DeleteSeasonData(); - public Task InitializeDatabase() => databaseRepository.InitializeDatabase(); + public Task DeleteAllDataAsync() => databaseRepository.DeleteAllDataAsync(); + public Task DeleteSeasonDataAsync() => databaseRepository.DeleteSeasonDataAsync(); + public Task InitializeDatabaseAsync() => databaseRepository.InitializeDatabaseAsync(); } diff --git a/HangTab/Services/Impl/WeekService.cs b/HangTab/Services/Impl/WeekService.cs index aa34f9e..664697b 100644 --- a/HangTab/Services/Impl/WeekService.cs +++ b/HangTab/Services/Impl/WeekService.cs @@ -4,8 +4,8 @@ namespace HangTab.Services.Impl; public class WeekService(IWeekRepository weekRepository) : IWeekService { - public Task GetWeekById(int id) => weekRepository.GetWeekById(id); - public Task> GetAllWeeks() => weekRepository.GetAllWeeks(); - public Task CreateWeek(int weekNumber) => weekRepository.CreateWeek(weekNumber); - public Task UpdateWeek(Week week) => weekRepository.UpdateWeek(week); + public Task GetWeekByIdAsync(int id) => weekRepository.GetWeekByIdAsync(id); + public Task> GetAllWeeksAsync() => weekRepository.GetAllWeeksAsync(); + public Task CreateWeekAsync(int weekNumber) => weekRepository.CreateWeekAsync(weekNumber); + public Task UpdateWeekAsync(Week week) => weekRepository.UpdateWeekAsync(week); } diff --git a/HangTab/ViewModels/BowlerSelectSubViewModel.cs b/HangTab/ViewModels/BowlerSelectSubViewModel.cs index 5b51665..93f348d 100644 --- a/HangTab/ViewModels/BowlerSelectSubViewModel.cs +++ b/HangTab/ViewModels/BowlerSelectSubViewModel.cs @@ -77,7 +77,7 @@ private async Task SubmitSelectedSubAsync() return; } - await bowlerService.UpdateBowler(MapDataToBowler()); + await bowlerService.UpdateBowlerAsync(MapDataToBowler()); messenger.Send(new BowlerSubChangedMessage(Id, SelectedSub.Id)); await navigationService.GoBack(); } @@ -94,13 +94,13 @@ private async Task GetSubsAsync() private async Task> GetAvailableSubsAsync() { - var subs = await personService.GetSubstitutes(); + var subs = await personService.GetSubstitutesAsync(); if (!subs.Any()) { return []; } - var bowlers = await bowlerService.GetAllBowlersByWeekId(_bowler?.WeekId ?? 0); + var bowlers = await bowlerService.GetAllBowlersByWeekIdAsync(_bowler?.WeekId ?? 0); return bowlers.Any() ? subs.Where(s => !bowlers.Any(b => b.SubId == s.Id)) : subs; diff --git a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs index 7c69029..3a1c375 100644 --- a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs +++ b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs @@ -83,7 +83,7 @@ async partial void OnBusRidesChanged(int value) } CurrentWeek.BusRides = value; - await _weekService.UpdateWeek(CurrentWeek); + await _weekService.UpdateWeekAsync(CurrentWeek); } } @@ -173,7 +173,7 @@ private async Task SubmitWeekAsync() private async Task StartNewWeekAsync() { - await _weekService.CreateWeek(CurrentWeek.Number + 1).ContinueWith(async saveTask => + await _weekService.CreateWeekAsync(CurrentWeek.Number + 1).ContinueWith(async saveTask => { if (saveTask.IsCompletedSuccessfully) { @@ -191,7 +191,7 @@ await _weekService.CreateWeek(CurrentWeek.Number + 1).ContinueWith(async saveTas private async Task GetCurrentWeekAsync() { - CurrentWeek = await _weekService.GetWeekById(_settingsService.CurrentWeekPrimaryKey); + CurrentWeek = await _weekService.GetWeekByIdAsync(_settingsService.CurrentWeekPrimaryKey); if (CurrentWeek is null) { return; @@ -234,12 +234,12 @@ private async Task SetBowlerStatusAsync(CurrentWeekListItemViewModel? vm, Enums. case Enums.Status.Active: vm.SubId = null; vm.Status = Enums.Status.Active; - await _bowlerService.UpdateBowler(vm.ToBowler()); + await _bowlerService.UpdateBowlerAsync(vm.ToBowler()); break; case Enums.Status.Blind: vm.SubId = null; vm.Status = Enums.Status.Blind; - await _bowlerService.UpdateBowler(vm.ToBowler()); + await _bowlerService.UpdateBowlerAsync(vm.ToBowler()); break; case Enums.Status.UsingSub: await _navigationService.GoToSelectSub(vm.ToBowler()); @@ -257,7 +257,7 @@ private async Task BowlerHangCountChangedAsync(CurrentWeekListItemViewModel? vm) return; } - if (await _bowlerService.UpdateBowler(vm.ToBowler())) + if (await _bowlerService.UpdateBowlerAsync(vm.ToBowler())) { var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); var isIncrease = newHangTotal > TeamHangTotal; @@ -298,7 +298,7 @@ public async void Receive(BowlerSubChangedMessage message) vm.SubId = message.SubId; vm.Status = Enums.Status.UsingSub; - await _bowlerService.UpdateBowler(vm.ToBowler()); + await _bowlerService.UpdateBowlerAsync(vm.ToBowler()); await GetCurrentWeekAsync(); } @@ -315,11 +315,11 @@ public async void Receive(PersonAddedOrChangedMessage message) if (!message.IsSub && !CurrentWeekBowlers.Any(b => b.PersonId == message.Id)) { - await _bowlerService.AddBowler(bowler); + await _bowlerService.AddBowlerAsync(bowler); } else if (message.IsSub && CurrentWeekBowlers.Any(b => b.PersonId == message.Id)) { - await _bowlerService.RemoveBowler(message.Id); + await _bowlerService.RemoveBowlerAsync(message.Id); } } diff --git a/HangTab/ViewModels/DataManagerViewModel.cs b/HangTab/ViewModels/DataManagerViewModel.cs index 336fc3f..6298567 100644 --- a/HangTab/ViewModels/DataManagerViewModel.cs +++ b/HangTab/ViewModels/DataManagerViewModel.cs @@ -26,7 +26,7 @@ public async Task DeleteAllDataAsync() return; } - if (await databaseService.DeleteAllData()) + if (await databaseService.DeleteAllDataAsync()) { SendSystemResetMessage(); await dialogService.ToastAsync("All data has been deleted"); @@ -48,7 +48,7 @@ public async Task StartNewSeasonAsync() return; } - if (await databaseService.DeleteSeasonData()) + if (await databaseService.DeleteSeasonDataAsync()) { SendSystemResetMessage(); await dialogService.ToastAsync("A new season has started"); diff --git a/HangTab/ViewModels/SeasonSummaryViewModel.cs b/HangTab/ViewModels/SeasonSummaryViewModel.cs index 2bc1f4b..85fef8d 100644 --- a/HangTab/ViewModels/SeasonSummaryViewModel.cs +++ b/HangTab/ViewModels/SeasonSummaryViewModel.cs @@ -60,13 +60,13 @@ private async Task SubmitSeasonAsync() private async Task GetBowlers() { - var people = await personService.GetRegulars(); + var people = await personService.GetRegularsAsync(); if (!people.Any()) { return; } - var allWeeks = await weekService.GetAllWeeks(); + var allWeeks = await weekService.GetAllWeeksAsync(); if (!allWeeks.Any()) { return; @@ -80,7 +80,7 @@ private async Task GetBowlers() private async Task GetWeeks() { - var allWeeks = await weekService.GetAllWeeks(); + var allWeeks = await weekService.GetAllWeeksAsync(); if (!allWeeks.Any()) { return; diff --git a/HangTab/ViewModels/WeekDetailsViewModel.cs b/HangTab/ViewModels/WeekDetailsViewModel.cs index b9d67a7..e87a490 100644 --- a/HangTab/ViewModels/WeekDetailsViewModel.cs +++ b/HangTab/ViewModels/WeekDetailsViewModel.cs @@ -49,7 +49,7 @@ await Loading( private async Task GetWeek(int id) { - var week = await weekService.GetWeekById(id); + var week = await weekService.GetWeekByIdAsync(id); if (week is not null) { MapWeekData(week); diff --git a/HangTab/ViewModels/WeekListOverviewViewModel.cs b/HangTab/ViewModels/WeekListOverviewViewModel.cs index 182051b..0aa6d19 100644 --- a/HangTab/ViewModels/WeekListOverviewViewModel.cs +++ b/HangTab/ViewModels/WeekListOverviewViewModel.cs @@ -26,7 +26,7 @@ public partial class WeekListOverviewViewModel( private async Task GetWeeks() { - var weeks = await weekService.GetAllWeeks(); + var weeks = await weekService.GetAllWeeksAsync(); if (!weeks.Any()) { return; From 483b8262fd74fef6e8c1933bcc654eacc70f5101 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Fri, 7 Nov 2025 19:33:46 -0500 Subject: [PATCH 05/12] updated repo and service layers to adhere to boilerplate standards --- .../Repositories/BowlerRepositoryTests.cs | 36 +++++----- .../Repositories/WeekRepositoryTests.cs | 14 ++-- HangTab.Tests/Services/BowlerServiceTests.cs | 72 +++++++++---------- HangTab.Tests/Services/WeekServiceTests.cs | 36 +++++----- HangTab/Repositories/IBowlerRepository.cs | 12 ++-- HangTab/Repositories/IWeekRepository.cs | 8 +-- HangTab/Repositories/Impl/AudioRepository.cs | 10 +++ HangTab/Repositories/Impl/BowlerRepository.cs | 12 ++-- HangTab/Repositories/Impl/WeekRepository.cs | 62 ++++++++-------- HangTab/Services/IBowlerService.cs | 12 ++-- HangTab/Services/IWeekService.cs | 8 +-- HangTab/Services/Impl/BowlerService.cs | 14 ++-- HangTab/Services/Impl/WeekService.cs | 10 +-- .../ViewModels/BowlerSelectSubViewModel.cs | 4 +- .../CurrentWeekOverviewViewModel.cs | 18 ++--- HangTab/ViewModels/SeasonSummaryViewModel.cs | 4 +- HangTab/ViewModels/WeekDetailsViewModel.cs | 2 +- .../ViewModels/WeekListOverviewViewModel.cs | 2 +- 18 files changed, 174 insertions(+), 162 deletions(-) diff --git a/HangTab.Tests/Repositories/BowlerRepositoryTests.cs b/HangTab.Tests/Repositories/BowlerRepositoryTests.cs index 859e1df..efb6669 100644 --- a/HangTab.Tests/Repositories/BowlerRepositoryTests.cs +++ b/HangTab.Tests/Repositories/BowlerRepositoryTests.cs @@ -18,7 +18,7 @@ public async Task AddBowler_CallsContextAndReturnsResult() A.CallTo(() => context.AddItemAsync(A._)).Returns(true); // Act - var actual = await bowlerRepository.AddBowlerAsync(bowler); + var actual = await bowlerRepository.AddAsync(bowler); // Assert Assert.True(actual); @@ -37,7 +37,7 @@ public async Task GetAllBowlersByWeekId_CallsContextWithCorrectPredicate() .Returns(expected); // Act - var actual = await bowlerRepository.GetAllBowlersByWeekIdAsync(weekId); + var actual = await bowlerRepository.GetAllByWeekIdAsync(weekId); // Assert Assert.Equal(expected, actual); @@ -55,7 +55,7 @@ public async Task GetAllBowlers_CallsContext() A.CallTo(() => context.GetAllWithChildrenAsync(null)).Returns(expected); // Act - var actual = await bowlerRepository.GetAllBowlersAsync(); + var actual = await bowlerRepository.GetAllAsync(); // Assert Assert.Equal(expected, actual); @@ -73,7 +73,7 @@ public async Task GetBowlerById_CallsContextWithId() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(bowler); // Act - var result = await bowlerRepository.GetBowlerByIdAsync(id); + var result = await bowlerRepository.GetByIdAsync(id); // Assert Assert.Equal(bowler, result); @@ -92,7 +92,7 @@ public async Task GetBowlersByWeekId_CallsContextWithCorrectPredicate() .Returns(expected); // Act - var actual = await bowlerRepository.GetAllBowlersByWeekIdAsync(weekId); + var actual = await bowlerRepository.GetAllByWeekIdAsync(weekId); // Assert Assert.Equal(expected, actual); @@ -110,7 +110,7 @@ public async Task UpdateBowler_CallsContextAndReturnsResult() A.CallTo(() => context.UpdateItemAsync(A._)).Returns(true); // Act - var result = await bowlerRepository.UpdateBowlerAsync(bowler); + var result = await bowlerRepository.UpdateAsync(bowler); // Assert Assert.True(result); @@ -125,7 +125,7 @@ public async Task AddBowler_ThrowsOnNull() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.AddBowlerAsync(null!)); + await Assert.ThrowsAsync(() => bowlerRepository.AddAsync(null!)); } [Fact] @@ -136,8 +136,8 @@ public async Task GetAllBowlersByWeekId_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(-1)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(-1)); } [Fact] @@ -148,8 +148,8 @@ public async Task GetBowlerById_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetBowlerByIdAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetBowlerByIdAsync(-5)); + await Assert.ThrowsAsync(() => bowlerRepository.GetByIdAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.GetByIdAsync(-5)); } [Fact] @@ -160,8 +160,8 @@ public async Task GetBowlersByWeekId_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetAllBowlersByWeekIdAsync(-2)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(-2)); } [Fact] @@ -172,7 +172,7 @@ public async Task UpdateBowler_ThrowsOnNull() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.UpdateBowlerAsync(null!)); + await Assert.ThrowsAsync(() => bowlerRepository.UpdateAsync(null!)); } [Fact] @@ -185,7 +185,7 @@ public async Task RemoveBowler_CallsContextAndReturnsTrue() A.CallTo(() => context.DeleteItemByIdAsync(id)).Returns(true); // Act - var result = await bowlerRepository.RemoveBowlerAsync(id); + var result = await bowlerRepository.RemoveAsync(id); // Assert Assert.True(result); @@ -202,7 +202,7 @@ public async Task RemoveBowler_CallsContextAndReturnsFalse() A.CallTo(() => context.DeleteItemByIdAsync(id)).Returns(false); // Act - var result = await bowlerRepository.RemoveBowlerAsync(id); + var result = await bowlerRepository.RemoveAsync(id); // Assert Assert.False(result); @@ -217,7 +217,7 @@ public async Task RemoveBowler_ThrowsOnInvalidId() var bowlerRepository = new BowlerRepository(context); // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.RemoveBowlerAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.RemoveBowlerAsync(-1)); + await Assert.ThrowsAsync(() => bowlerRepository.RemoveAsync(0)); + await Assert.ThrowsAsync(() => bowlerRepository.RemoveAsync(-1)); } } \ No newline at end of file diff --git a/HangTab.Tests/Repositories/WeekRepositoryTests.cs b/HangTab.Tests/Repositories/WeekRepositoryTests.cs index 71848f2..a87cf2c 100644 --- a/HangTab.Tests/Repositories/WeekRepositoryTests.cs +++ b/HangTab.Tests/Repositories/WeekRepositoryTests.cs @@ -31,7 +31,7 @@ public async Task GetWeekById_WithValidId_ReturnsWeekWithBowlers() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(person); // Act - var actual = await weekRepository.GetWeekByIdAsync(weekId); + var actual = await weekRepository.GetByIdAsync(weekId); // Assert Assert.Equal(weekId, actual.Id); @@ -53,7 +53,7 @@ public async Task GetWeekById_WithIdLessThan1_CreatesWeek() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(createdWeek); // Act - var actual = await weekRepository.GetWeekByIdAsync(0); + var actual = await weekRepository.GetByIdAsync(0); // Assert Assert.Equal(1, actual.Number); @@ -69,7 +69,7 @@ public async Task GetWeekById_WeekNotFound_ReturnsNewWeek() A.CallTo(() => context.GetItemByIdAsync(A._)).Returns((Week?)null!); // Act - var actual = await weekRepository.GetWeekByIdAsync(weekId); + var actual = await weekRepository.GetByIdAsync(weekId); // Assert Assert.NotNull(actual); @@ -86,7 +86,7 @@ public async Task GetAllWeeks_CallsContext() A.CallTo(() => context.GetAllWithChildrenAsync(null)).Returns(weeks); // Act - var actual = await weekRepository.GetAllWeeksAsync(); + var actual = await weekRepository.GetAllAsync(); // Assert Assert.Equal(weeks, actual); @@ -109,7 +109,7 @@ public async Task CreateWeek_AddsWeekAndBowlers() A.CallTo(() => context.AddItemAsync(A._)).Returns(true); // Act - var actual = await weekRepository.CreateWeekAsync(3); + var actual = await weekRepository.CreateAsync(3); // Assert Assert.Equal(3, actual.Number); @@ -127,7 +127,7 @@ public async Task CreateWeek_WithNoPeople_AddsWeekOnly() A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns([]); // Act - var actual = await weekRepository.CreateWeekAsync(2); + var actual = await weekRepository.CreateAsync(2); // Assert Assert.Equal(2, actual.Number); @@ -145,7 +145,7 @@ public async Task UpdateWeek_CallsContext() A.CallTo(() => context.UpdateWithChildrenAsync(A._)).Returns(Task.CompletedTask); // Act - await weekRepository.UpdateWeekAsync(week); + await weekRepository.UpdateAsync(week); // Assert A.CallTo(() => context.UpdateWithChildrenAsync(A._)).MustHaveHappenedOnceExactly(); diff --git a/HangTab.Tests/Services/BowlerServiceTests.cs b/HangTab.Tests/Services/BowlerServiceTests.cs index 1656d45..2b49ad5 100644 --- a/HangTab.Tests/Services/BowlerServiceTests.cs +++ b/HangTab.Tests/Services/BowlerServiceTests.cs @@ -13,14 +13,14 @@ public async Task AddBowler_ValidBowler_ReturnsTrue() var bowler = new Bowler { Id = 1 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.AddAsync(A._)).Returns(Task.FromResult(true)); // Act - var result = await service.AddBowlerAsync(bowler); + var result = await service.AddAsync(bowler); // Assert Assert.True(result); - A.CallTo(() => bowlerRepo.AddBowlerAsync(bowler)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.AddAsync(bowler)).MustHaveHappenedOnceExactly(); } [Fact] @@ -30,14 +30,14 @@ public async Task AddBowler_RepositoryReturnsFalse_ReturnsFalse() var bowler = new Bowler { Id = 2 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.AddAsync(A._)).Returns(Task.FromResult(false)); // Act - var result = await service.AddBowlerAsync(bowler); + var result = await service.AddAsync(bowler); // Assert Assert.False(result); - A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.AddAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -53,14 +53,14 @@ public async Task GetAllBowlersByWeekId_ValidId_ReturnsBowlers() var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); + A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); // Act - var result = await service.GetAllBowlersByWeekIdAsync(weeekId); + var result = await service.GetAllByWeekIdAsync(weeekId); // Assert Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -69,14 +69,14 @@ public async Task GetAllBowlers_RepositoryReturnsEmpty_ReturnsEmpty() // Arrange var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllBowlersAsync()).Returns(Task.FromResult>([])); + A.CallTo(() => bowlerRepo.GetAllAsync()).Returns(Task.FromResult>([])); // Act - var result = await service.GetAllBowlersAsync(); + var result = await service.GetAllAsync(); // Assert Assert.Empty(result); - A.CallTo(() => bowlerRepo.GetAllBowlersAsync()).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetAllAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -87,14 +87,14 @@ public async Task GetBowlerById_ValidId_ReturnsBowler() var expected = new Bowler { Id = id }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetBowlerByIdAsync(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => bowlerRepo.GetByIdAsync(A._)).Returns(Task.FromResult(expected)); // Act - var result = await service.GetBowlerByIdAsync(id); + var result = await service.GetByIdAsync(id); // Assert Assert.Equal(expected.Id, result.Id); - A.CallTo(() => bowlerRepo.GetBowlerByIdAsync(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetByIdAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -105,15 +105,15 @@ public async Task GetBowlersByWeekId_ValidId_ReturnsBowlers() var expected = new List { new() { Id = 3, WeekId = id } }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); + A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); // Act - var result = await service.GetAllBowlersByWeekIdAsync(id); + var result = await service.GetAllByWeekIdAsync(id); // Assert Assert.Single(result); Assert.Equal(expected[0].Id, result.First().Id); - A.CallTo(() => bowlerRepo.GetAllBowlersByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -123,14 +123,14 @@ public async Task RemoveBowler_ValidId_ReturnsTrue() var id = 1; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.RemoveAsync(id)).Returns(Task.FromResult(true)); // Act - var result = await service.RemoveBowlerAsync(id); + var result = await service.RemoveAsync(id); // Assert Assert.True(result); - A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.RemoveAsync(id)).MustHaveHappenedOnceExactly(); } [Fact] @@ -140,14 +140,14 @@ public async Task RemoveBowler_InvalidId_ReturnsFalse() var id = 999; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.RemoveAsync(id)).Returns(Task.FromResult(false)); // Act - var result = await service.RemoveBowlerAsync(id); + var result = await service.RemoveAsync(id); // Assert Assert.False(result); - A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.RemoveAsync(id)).MustHaveHappenedOnceExactly(); } [Fact] @@ -157,10 +157,10 @@ public async Task RemoveBowler_RepositoryThrowsException_PropagatesException() var id = 2; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveBowlerAsync(id)).Throws(new InvalidOperationException("Repository error")); + A.CallTo(() => bowlerRepo.RemoveAsync(id)).Throws(new InvalidOperationException("Repository error")); // Act & Assert - await Assert.ThrowsAsync(() => service.RemoveBowlerAsync(id)); + await Assert.ThrowsAsync(() => service.RemoveAsync(id)); } [Fact] @@ -170,14 +170,14 @@ public async Task UpdateBowler_RepositoryReturnsTrue_ReturnsTrue() var expected = new Bowler { Id = 4 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(Task.FromResult(true)); // Act - var result = await service.UpdateBowlerAsync(expected); + var result = await service.UpdateAsync(expected); // Assert Assert.True(result); - A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -187,14 +187,14 @@ public async Task UpdateBowler_RepositoryReturnsFalse_ReturnsFalse() var expected = new Bowler { Id = 5 }; var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(Task.FromResult(false)); // Act - var result = await service.UpdateBowlerAsync(expected); + var result = await service.UpdateAsync(expected); // Assert Assert.False(result); - A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).MustHaveHappenedOnceExactly(); } [Fact] @@ -203,10 +203,10 @@ public async Task AddBowler_NullBowler_ThrowsArgumentNullException() // Arrange var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddBowlerAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); + A.CallTo(() => bowlerRepo.AddAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); // Act & Assert - await Assert.ThrowsAsync(() => service.AddBowlerAsync(null!)); + await Assert.ThrowsAsync(() => service.AddAsync(null!)); } [Fact] @@ -215,9 +215,9 @@ public async Task UpdateBowler_NullBowler_ThrowsArgumentNullException() // Arrange var bowlerRepo = A.Fake(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateBowlerAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); // Act & Assert - await Assert.ThrowsAsync(() => service.UpdateBowlerAsync(null!)); + await Assert.ThrowsAsync(() => service.UpdateAsync(null!)); } } \ No newline at end of file diff --git a/HangTab.Tests/Services/WeekServiceTests.cs b/HangTab.Tests/Services/WeekServiceTests.cs index 7cc6998..ff11d45 100644 --- a/HangTab.Tests/Services/WeekServiceTests.cs +++ b/HangTab.Tests/Services/WeekServiceTests.cs @@ -13,15 +13,15 @@ public async Task GetWeekById_ValidId_ReturnsWeek() var expected = new Week { Id = 1, Number = 7 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetWeekByIdAsync(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => weekRepo.GetByIdAsync(A._)).Returns(Task.FromResult(expected)); // Act - var result = await service.GetWeekByIdAsync(1); + var result = await service.GetByIdAsync(1); // Assert Assert.Equal(expected.Id, result.Id); Assert.Equal(expected.Number, result.Number); - A.CallTo(() => weekRepo.GetWeekByIdAsync(1)).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.GetByIdAsync(1)).MustHaveHappenedOnceExactly(); } [Fact] @@ -31,14 +31,14 @@ public async Task GetAllWeeks_RepositoryReturnsWeeks_ReturnsWeeks() var expected = new List { new() { Id = 1 }, new() { Id = 2 } }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetAllWeeksAsync()).Returns(Task.FromResult>(expected)); + A.CallTo(() => weekRepo.GetAllAsync()).Returns(Task.FromResult>(expected)); // Act - var result = await service.GetAllWeeksAsync(); + var result = await service.GetAllAsync(); // Assert Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => weekRepo.GetAllWeeksAsync()).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.GetAllAsync()).MustHaveHappenedOnceExactly(); } [Fact] @@ -48,15 +48,15 @@ public async Task CreateWeek_ValidNumber_ReturnsWeek() var expected = new Week { Id = 3, Number = 4 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.CreateWeekAsync(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => weekRepo.CreateAsync(A._)).Returns(Task.FromResult(expected)); // Act - var result = await service.CreateWeekAsync(4); + var result = await service.CreateAsync(4); // Assert Assert.Equal(expected.Id, result.Id); Assert.Equal(expected.Number, result.Number); - A.CallTo(() => weekRepo.CreateWeekAsync(4)).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.CreateAsync(4)).MustHaveHappenedOnceExactly(); } [Fact] @@ -66,13 +66,13 @@ public async Task UpdateWeek_ValidWeek_CallsRepository() var week = new Week { Id = 5, Number = 6 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.UpdateWeekAsync(A._)).Returns(Task.CompletedTask); + A.CallTo(() => weekRepo.UpdateAsync(A._)).Returns(Task.CompletedTask); // Act - await service.UpdateWeekAsync(week); + await service.UpdateAsync(week); // Assert - A.CallTo(() => weekRepo.UpdateWeekAsync(week)).MustHaveHappenedOnceExactly(); + A.CallTo(() => weekRepo.UpdateAsync(week)).MustHaveHappenedOnceExactly(); } [Fact] @@ -82,10 +82,10 @@ public async Task GetWeekById_RepositoryThrows_PropagatesException() var id = 99; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetWeekByIdAsync(A._)).Throws(new InvalidOperationException("Not found")); + A.CallTo(() => weekRepo.GetByIdAsync(A._)).Throws(new InvalidOperationException("Not found")); // Act & Assert - await Assert.ThrowsAsync(() => service.GetWeekByIdAsync(id)); + await Assert.ThrowsAsync(() => service.GetByIdAsync(id)); } [Fact] @@ -95,10 +95,10 @@ public async Task CreateWeek_RepositoryThrows_PropagatesException() var id = 42; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.CreateWeekAsync(A._)).Throws(new Exception("Create failed")); + A.CallTo(() => weekRepo.CreateAsync(A._)).Throws(new Exception("Create failed")); // Act & Assert - await Assert.ThrowsAsync(() => service.CreateWeekAsync(id)); + await Assert.ThrowsAsync(() => service.CreateAsync(id)); } [Fact] @@ -108,9 +108,9 @@ public async Task UpdateWeek_RepositoryThrows_PropagatesException() var week = new Week { Id = 7, Number = 8 }; var weekRepo = A.Fake(); var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.UpdateWeekAsync(A._)).Throws(new Exception("Update failed")); + A.CallTo(() => weekRepo.UpdateAsync(A._)).Throws(new Exception("Update failed")); // Act & Assert - await Assert.ThrowsAsync(() => service.UpdateWeekAsync(week)); + await Assert.ThrowsAsync(() => service.UpdateAsync(week)); } } diff --git a/HangTab/Repositories/IBowlerRepository.cs b/HangTab/Repositories/IBowlerRepository.cs index 44a581e..e4def7b 100644 --- a/HangTab/Repositories/IBowlerRepository.cs +++ b/HangTab/Repositories/IBowlerRepository.cs @@ -3,10 +3,10 @@ namespace HangTab.Repositories; public interface IBowlerRepository { - Task AddBowlerAsync(Bowler bowler); - Task> GetAllBowlersByWeekIdAsync(int id); - Task> GetAllBowlersAsync(); - Task GetBowlerByIdAsync(int id); - Task RemoveBowlerAsync(int id); - Task UpdateBowlerAsync(Bowler bowler); + Task AddAsync(Bowler bowler); + Task> GetAllByWeekIdAsync(int id); + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task RemoveAsync(int id); + Task UpdateAsync(Bowler bowler); } diff --git a/HangTab/Repositories/IWeekRepository.cs b/HangTab/Repositories/IWeekRepository.cs index 03ba649..b3d98c9 100644 --- a/HangTab/Repositories/IWeekRepository.cs +++ b/HangTab/Repositories/IWeekRepository.cs @@ -3,8 +3,8 @@ namespace HangTab.Repositories; public interface IWeekRepository { - Task GetWeekByIdAsync(int id); - Task> GetAllWeeksAsync(); - Task CreateWeekAsync(int weekNumber = 1); - Task UpdateWeekAsync(Week week); + Task CreateAsync(int weekNumber = 1); + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task UpdateAsync(Week week); } diff --git a/HangTab/Repositories/Impl/AudioRepository.cs b/HangTab/Repositories/Impl/AudioRepository.cs index 722d728..e6ceeca 100644 --- a/HangTab/Repositories/Impl/AudioRepository.cs +++ b/HangTab/Repositories/Impl/AudioRepository.cs @@ -8,6 +8,16 @@ public class AudioRepository(IAudioManager audioManager) : IAudioRepository { public async Task PlayAudioStreamAsync(Stream audioStream) { + if (audioStream is null) + { + return Result.Fail(new Error("Audio stream cannot be null")); + } + + if (!audioStream.CanRead) + { + return Result.Fail(new Error("Audio stream is not readable")); + } + try { var player = audioManager.CreateAsyncPlayer(audioStream); diff --git a/HangTab/Repositories/Impl/BowlerRepository.cs b/HangTab/Repositories/Impl/BowlerRepository.cs index 6804c05..b84e097 100644 --- a/HangTab/Repositories/Impl/BowlerRepository.cs +++ b/HangTab/Repositories/Impl/BowlerRepository.cs @@ -4,33 +4,33 @@ namespace HangTab.Repositories.Impl; public class BowlerRepository(IDatabaseContext context) : IBowlerRepository { - public Task AddBowlerAsync(Bowler bowler) + public Task AddAsync(Bowler bowler) { ArgumentNullException.ThrowIfNull(bowler); return context.AddItemAsync(bowler); } - public Task> GetAllBowlersByWeekIdAsync(int id) + public Task> GetAllByWeekIdAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.GetAllWithChildrenAsync(wl => wl.WeekId == id); } - public Task> GetAllBowlersAsync() => context.GetAllWithChildrenAsync(); + public Task> GetAllAsync() => context.GetAllWithChildrenAsync(); - public Task GetBowlerByIdAsync(int id) + public Task GetByIdAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.GetItemByIdAsync(id); } - public Task RemoveBowlerAsync(int id) + public Task RemoveAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.DeleteItemByIdAsync(id); } - public Task UpdateBowlerAsync(Bowler bowler) + public Task UpdateAsync(Bowler bowler) { ArgumentNullException.ThrowIfNull(bowler); return context.UpdateItemAsync(bowler); diff --git a/HangTab/Repositories/Impl/WeekRepository.cs b/HangTab/Repositories/Impl/WeekRepository.cs index e29d268..ab44468 100644 --- a/HangTab/Repositories/Impl/WeekRepository.cs +++ b/HangTab/Repositories/Impl/WeekRepository.cs @@ -4,11 +4,40 @@ namespace HangTab.Repositories.Impl; public class WeekRepository(IDatabaseContext context) : IWeekRepository { - public async Task GetWeekByIdAsync(int id) + public async Task CreateAsync(int weekNumber = 1) + { + var week = new Week + { + Number = weekNumber + }; + await context.AddItemAsync(week); + + var regulars = await context.GetFilteredAsync(p => !p.IsSub); + if (!regulars.Any()) + { + return week; + } + + foreach (var regular in regulars) + { + var bowler = new Bowler + { + PersonId = regular.Id, + WeekId = week.Id + }; + await context.AddItemAsync(bowler); + } + + return week; + } + + public Task> GetAllAsync() => context.GetAllWithChildrenAsync(); + + public async Task GetByIdAsync(int id) { if (id < 1) { - return await CreateWeekAsync(); // Create the first week if no valid ID is provided + return await CreateAsync(); // Create the first week if no valid ID is provided } var week = await context.GetItemByIdAsync(id); @@ -37,32 +66,5 @@ public async Task GetWeekByIdAsync(int id) return week; } - public Task> GetAllWeeksAsync() => context.GetAllWithChildrenAsync(); - - public async Task CreateWeekAsync(int weekNumber = 1) - { - var week = new Week - { - Number = weekNumber - }; - await context.AddItemAsync(week); - - var people = await context.GetFilteredAsync(p => !p.IsSub); - if (people.Any()) - { - foreach (var person in people) - { - var bowler = new Bowler - { - PersonId = person.Id, - WeekId = week.Id - }; - await context.AddItemAsync(bowler); - } - } - - return week; - } - - public Task UpdateWeekAsync(Week week) => context.UpdateWithChildrenAsync(week); + public Task UpdateAsync(Week week) => context.UpdateWithChildrenAsync(week); } diff --git a/HangTab/Services/IBowlerService.cs b/HangTab/Services/IBowlerService.cs index 7e5bed7..09edc3f 100644 --- a/HangTab/Services/IBowlerService.cs +++ b/HangTab/Services/IBowlerService.cs @@ -3,10 +3,10 @@ namespace HangTab.Services; public interface IBowlerService { - Task AddBowlerAsync(Bowler bowler); - Task> GetAllBowlersByWeekIdAsync(int id); - Task> GetAllBowlersAsync(); - Task GetBowlerByIdAsync(int id); - Task RemoveBowlerAsync(int id); - Task UpdateBowlerAsync(Bowler bowler); + Task AddAsync(Bowler bowler); + Task> GetAllAsync(); + Task> GetAllByWeekIdAsync(int id); + Task GetByIdAsync(int id); + Task RemoveAsync(int id); + Task UpdateAsync(Bowler bowler); } diff --git a/HangTab/Services/IWeekService.cs b/HangTab/Services/IWeekService.cs index 326e14b..881b78c 100644 --- a/HangTab/Services/IWeekService.cs +++ b/HangTab/Services/IWeekService.cs @@ -3,8 +3,8 @@ namespace HangTab.Services; public interface IWeekService { - Task GetWeekByIdAsync(int id); - Task> GetAllWeeksAsync(); - Task CreateWeekAsync(int weekNumber); - Task UpdateWeekAsync(Week week); + Task CreateAsync(int weekNumber); + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task UpdateAsync(Week week); } diff --git a/HangTab/Services/Impl/BowlerService.cs b/HangTab/Services/Impl/BowlerService.cs index 76074f4..988fc19 100644 --- a/HangTab/Services/Impl/BowlerService.cs +++ b/HangTab/Services/Impl/BowlerService.cs @@ -2,12 +2,12 @@ using HangTab.Repositories; namespace HangTab.Services.Impl; -public class BowlerService(IBowlerRepository bowlerRepository) : IBowlerService +public class BowlerService(IBowlerRepository repo) : IBowlerService { - public Task AddBowlerAsync(Bowler bowler) => bowlerRepository.AddBowlerAsync(bowler); - public Task> GetAllBowlersByWeekIdAsync(int id) => bowlerRepository.GetAllBowlersByWeekIdAsync(id); - public Task> GetAllBowlersAsync() => bowlerRepository.GetAllBowlersAsync(); - public Task GetBowlerByIdAsync(int id) => bowlerRepository.GetBowlerByIdAsync(id); - public Task RemoveBowlerAsync(int id) => bowlerRepository.RemoveBowlerAsync(id); - public Task UpdateBowlerAsync(Bowler bowler) => bowlerRepository.UpdateBowlerAsync(bowler); + public Task AddAsync(Bowler bowler) => repo.AddAsync(bowler); + public Task> GetAllByWeekIdAsync(int id) => repo.GetAllByWeekIdAsync(id); + public Task> GetAllAsync() => repo.GetAllAsync(); + public Task GetByIdAsync(int id) => repo.GetByIdAsync(id); + public Task RemoveAsync(int id) => repo.RemoveAsync(id); + public Task UpdateAsync(Bowler bowler) => repo.UpdateAsync(bowler); } diff --git a/HangTab/Services/Impl/WeekService.cs b/HangTab/Services/Impl/WeekService.cs index 664697b..22b6494 100644 --- a/HangTab/Services/Impl/WeekService.cs +++ b/HangTab/Services/Impl/WeekService.cs @@ -2,10 +2,10 @@ using HangTab.Repositories; namespace HangTab.Services.Impl; -public class WeekService(IWeekRepository weekRepository) : IWeekService +public class WeekService(IWeekRepository repo) : IWeekService { - public Task GetWeekByIdAsync(int id) => weekRepository.GetWeekByIdAsync(id); - public Task> GetAllWeeksAsync() => weekRepository.GetAllWeeksAsync(); - public Task CreateWeekAsync(int weekNumber) => weekRepository.CreateWeekAsync(weekNumber); - public Task UpdateWeekAsync(Week week) => weekRepository.UpdateWeekAsync(week); + public Task CreateAsync(int weekNumber) => repo.CreateAsync(weekNumber); + public Task> GetAllAsync() => repo.GetAllAsync(); + public Task GetByIdAsync(int id) => repo.GetByIdAsync(id); + public Task UpdateAsync(Week week) => repo.UpdateAsync(week); } diff --git a/HangTab/ViewModels/BowlerSelectSubViewModel.cs b/HangTab/ViewModels/BowlerSelectSubViewModel.cs index 93f348d..c420329 100644 --- a/HangTab/ViewModels/BowlerSelectSubViewModel.cs +++ b/HangTab/ViewModels/BowlerSelectSubViewModel.cs @@ -77,7 +77,7 @@ private async Task SubmitSelectedSubAsync() return; } - await bowlerService.UpdateBowlerAsync(MapDataToBowler()); + await bowlerService.UpdateAsync(MapDataToBowler()); messenger.Send(new BowlerSubChangedMessage(Id, SelectedSub.Id)); await navigationService.GoBack(); } @@ -100,7 +100,7 @@ private async Task> GetAvailableSubsAsync() return []; } - var bowlers = await bowlerService.GetAllBowlersByWeekIdAsync(_bowler?.WeekId ?? 0); + var bowlers = await bowlerService.GetAllByWeekIdAsync(_bowler?.WeekId ?? 0); return bowlers.Any() ? subs.Where(s => !bowlers.Any(b => b.SubId == s.Id)) : subs; diff --git a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs index 3a1c375..77aba81 100644 --- a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs +++ b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs @@ -83,7 +83,7 @@ async partial void OnBusRidesChanged(int value) } CurrentWeek.BusRides = value; - await _weekService.UpdateWeekAsync(CurrentWeek); + await _weekService.UpdateAsync(CurrentWeek); } } @@ -173,7 +173,7 @@ private async Task SubmitWeekAsync() private async Task StartNewWeekAsync() { - await _weekService.CreateWeekAsync(CurrentWeek.Number + 1).ContinueWith(async saveTask => + await _weekService.CreateAsync(CurrentWeek.Number + 1).ContinueWith(async saveTask => { if (saveTask.IsCompletedSuccessfully) { @@ -191,7 +191,7 @@ await _weekService.CreateWeekAsync(CurrentWeek.Number + 1).ContinueWith(async sa private async Task GetCurrentWeekAsync() { - CurrentWeek = await _weekService.GetWeekByIdAsync(_settingsService.CurrentWeekPrimaryKey); + CurrentWeek = await _weekService.GetByIdAsync(_settingsService.CurrentWeekPrimaryKey); if (CurrentWeek is null) { return; @@ -234,12 +234,12 @@ private async Task SetBowlerStatusAsync(CurrentWeekListItemViewModel? vm, Enums. case Enums.Status.Active: vm.SubId = null; vm.Status = Enums.Status.Active; - await _bowlerService.UpdateBowlerAsync(vm.ToBowler()); + await _bowlerService.UpdateAsync(vm.ToBowler()); break; case Enums.Status.Blind: vm.SubId = null; vm.Status = Enums.Status.Blind; - await _bowlerService.UpdateBowlerAsync(vm.ToBowler()); + await _bowlerService.UpdateAsync(vm.ToBowler()); break; case Enums.Status.UsingSub: await _navigationService.GoToSelectSub(vm.ToBowler()); @@ -257,7 +257,7 @@ private async Task BowlerHangCountChangedAsync(CurrentWeekListItemViewModel? vm) return; } - if (await _bowlerService.UpdateBowlerAsync(vm.ToBowler())) + if (await _bowlerService.UpdateAsync(vm.ToBowler())) { var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); var isIncrease = newHangTotal > TeamHangTotal; @@ -298,7 +298,7 @@ public async void Receive(BowlerSubChangedMessage message) vm.SubId = message.SubId; vm.Status = Enums.Status.UsingSub; - await _bowlerService.UpdateBowlerAsync(vm.ToBowler()); + await _bowlerService.UpdateAsync(vm.ToBowler()); await GetCurrentWeekAsync(); } @@ -315,11 +315,11 @@ public async void Receive(PersonAddedOrChangedMessage message) if (!message.IsSub && !CurrentWeekBowlers.Any(b => b.PersonId == message.Id)) { - await _bowlerService.AddBowlerAsync(bowler); + await _bowlerService.AddAsync(bowler); } else if (message.IsSub && CurrentWeekBowlers.Any(b => b.PersonId == message.Id)) { - await _bowlerService.RemoveBowlerAsync(message.Id); + await _bowlerService.RemoveAsync(message.Id); } } diff --git a/HangTab/ViewModels/SeasonSummaryViewModel.cs b/HangTab/ViewModels/SeasonSummaryViewModel.cs index 85fef8d..7dcf93b 100644 --- a/HangTab/ViewModels/SeasonSummaryViewModel.cs +++ b/HangTab/ViewModels/SeasonSummaryViewModel.cs @@ -66,7 +66,7 @@ private async Task GetBowlers() return; } - var allWeeks = await weekService.GetAllWeeksAsync(); + var allWeeks = await weekService.GetAllAsync(); if (!allWeeks.Any()) { return; @@ -80,7 +80,7 @@ private async Task GetBowlers() private async Task GetWeeks() { - var allWeeks = await weekService.GetAllWeeksAsync(); + var allWeeks = await weekService.GetAllAsync(); if (!allWeeks.Any()) { return; diff --git a/HangTab/ViewModels/WeekDetailsViewModel.cs b/HangTab/ViewModels/WeekDetailsViewModel.cs index e87a490..93ae7f3 100644 --- a/HangTab/ViewModels/WeekDetailsViewModel.cs +++ b/HangTab/ViewModels/WeekDetailsViewModel.cs @@ -49,7 +49,7 @@ await Loading( private async Task GetWeek(int id) { - var week = await weekService.GetWeekByIdAsync(id); + var week = await weekService.GetByIdAsync(id); if (week is not null) { MapWeekData(week); diff --git a/HangTab/ViewModels/WeekListOverviewViewModel.cs b/HangTab/ViewModels/WeekListOverviewViewModel.cs index 0aa6d19..50a6e3c 100644 --- a/HangTab/ViewModels/WeekListOverviewViewModel.cs +++ b/HangTab/ViewModels/WeekListOverviewViewModel.cs @@ -26,7 +26,7 @@ public partial class WeekListOverviewViewModel( private async Task GetWeeks() { - var weeks = await weekService.GetAllWeeksAsync(); + var weeks = await weekService.GetAllAsync(); if (!weeks.Any()) { return; From dec663a3b35a5d10fff3386615f4a256b24a3986 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:03:42 -0500 Subject: [PATCH 06/12] moved FluentResults to a global usings file --- HangTab/GlobalUsings.cs | 1 + HangTab/Repositories/IAudioRepository.cs | 4 +--- HangTab/Repositories/IStorageRepository.cs | 4 +--- HangTab/Repositories/Impl/AudioRepository.cs | 4 +--- HangTab/Repositories/Impl/StorageRepository.cs | 4 +--- HangTab/Services/IAudioService.cs | 4 +--- HangTab/Services/Impl/AudioService.cs | 4 +--- 7 files changed, 7 insertions(+), 18 deletions(-) create mode 100644 HangTab/GlobalUsings.cs diff --git a/HangTab/GlobalUsings.cs b/HangTab/GlobalUsings.cs new file mode 100644 index 0000000..9bec751 --- /dev/null +++ b/HangTab/GlobalUsings.cs @@ -0,0 +1 @@ +global using FluentResults; \ No newline at end of file diff --git a/HangTab/Repositories/IAudioRepository.cs b/HangTab/Repositories/IAudioRepository.cs index 18bd7fd..2883a24 100644 --- a/HangTab/Repositories/IAudioRepository.cs +++ b/HangTab/Repositories/IAudioRepository.cs @@ -1,6 +1,4 @@ -using FluentResults; - -namespace HangTab.Repositories; +namespace HangTab.Repositories; public interface IAudioRepository { Task PlayAudioStreamAsync(Stream audioStream); diff --git a/HangTab/Repositories/IStorageRepository.cs b/HangTab/Repositories/IStorageRepository.cs index e6fb0f4..43104aa 100644 --- a/HangTab/Repositories/IStorageRepository.cs +++ b/HangTab/Repositories/IStorageRepository.cs @@ -1,6 +1,4 @@ -using FluentResults; - -namespace HangTab.Repositories; +namespace HangTab.Repositories; public interface IStorageRepository { Task> OpenAppPackageFileAsync(string fileName); diff --git a/HangTab/Repositories/Impl/AudioRepository.cs b/HangTab/Repositories/Impl/AudioRepository.cs index e6ceeca..0e2e204 100644 --- a/HangTab/Repositories/Impl/AudioRepository.cs +++ b/HangTab/Repositories/Impl/AudioRepository.cs @@ -1,6 +1,4 @@ -using FluentResults; - -using Plugin.Maui.Audio; +using Plugin.Maui.Audio; namespace HangTab.Repositories.Impl; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "This is a Repository for the data layer and does not require unit tests.")] diff --git a/HangTab/Repositories/Impl/StorageRepository.cs b/HangTab/Repositories/Impl/StorageRepository.cs index 98d956a..1222062 100644 --- a/HangTab/Repositories/Impl/StorageRepository.cs +++ b/HangTab/Repositories/Impl/StorageRepository.cs @@ -1,6 +1,4 @@ -using FluentResults; - -namespace HangTab.Repositories.Impl; +namespace HangTab.Repositories.Impl; [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "This is a Repository for the data layer and does not require unit tests.")] public class StorageRepository(IFileSystem fileSystem) : IStorageRepository { diff --git a/HangTab/Services/IAudioService.cs b/HangTab/Services/IAudioService.cs index 86112dc..71c5301 100644 --- a/HangTab/Services/IAudioService.cs +++ b/HangTab/Services/IAudioService.cs @@ -1,6 +1,4 @@ -using FluentResults; - -namespace HangTab.Services; +namespace HangTab.Services; public interface IAudioService { Task PlaySoundAsync(string audioFileName); diff --git a/HangTab/Services/Impl/AudioService.cs b/HangTab/Services/Impl/AudioService.cs index 2c0aca6..40c9bf8 100644 --- a/HangTab/Services/Impl/AudioService.cs +++ b/HangTab/Services/Impl/AudioService.cs @@ -1,6 +1,4 @@ -using FluentResults; - -using HangTab.Repositories; +using HangTab.Repositories; namespace HangTab.Services.Impl; public class AudioService( From 2e0c796bb663c795e9200720e29c4abba15865d0 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:51:39 -0500 Subject: [PATCH 07/12] removed concrete repos and replaced with a base repo which takes care of basic CRUD operations --- HangTab/Repositories/IBaseRepository.cs | 10 +++ HangTab/Repositories/IBowlerRepository.cs | 12 ---- HangTab/Repositories/IPersonRepository.cs | 13 ---- HangTab/Repositories/IWeekRepository.cs | 10 --- HangTab/Repositories/Impl/BaseRepository.cs | 37 ++++++++++ HangTab/Repositories/Impl/BowlerRepository.cs | 38 ---------- HangTab/Repositories/Impl/PersonRepository.cs | 14 ---- HangTab/Repositories/Impl/WeekRepository.cs | 70 ------------------- 8 files changed, 47 insertions(+), 157 deletions(-) create mode 100644 HangTab/Repositories/IBaseRepository.cs delete mode 100644 HangTab/Repositories/IBowlerRepository.cs delete mode 100644 HangTab/Repositories/IPersonRepository.cs delete mode 100644 HangTab/Repositories/IWeekRepository.cs create mode 100644 HangTab/Repositories/Impl/BaseRepository.cs delete mode 100644 HangTab/Repositories/Impl/BowlerRepository.cs delete mode 100644 HangTab/Repositories/Impl/PersonRepository.cs delete mode 100644 HangTab/Repositories/Impl/WeekRepository.cs diff --git a/HangTab/Repositories/IBaseRepository.cs b/HangTab/Repositories/IBaseRepository.cs new file mode 100644 index 0000000..7aef08c --- /dev/null +++ b/HangTab/Repositories/IBaseRepository.cs @@ -0,0 +1,10 @@ +namespace HangTab.Repositories; +public interface IBaseRepository where T : class, new() +{ + Task AddAsync(T item); + Task DeleteByIdAsync(int id); + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task> GetFilteredAsync(System.Linq.Expressions.Expression> predicate); + Task UpdateAsync(T item); +} diff --git a/HangTab/Repositories/IBowlerRepository.cs b/HangTab/Repositories/IBowlerRepository.cs deleted file mode 100644 index e4def7b..0000000 --- a/HangTab/Repositories/IBowlerRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using HangTab.Models; - -namespace HangTab.Repositories; -public interface IBowlerRepository -{ - Task AddAsync(Bowler bowler); - Task> GetAllByWeekIdAsync(int id); - Task> GetAllAsync(); - Task GetByIdAsync(int id); - Task RemoveAsync(int id); - Task UpdateAsync(Bowler bowler); -} diff --git a/HangTab/Repositories/IPersonRepository.cs b/HangTab/Repositories/IPersonRepository.cs deleted file mode 100644 index 9d84109..0000000 --- a/HangTab/Repositories/IPersonRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using HangTab.Models; - -namespace HangTab.Repositories; -public interface IPersonRepository -{ - Task GetPersonById(int id); - Task> GetAllPeople(); - Task> GetRegulars(); - Task> GetSubstitutes(); - Task AddPerson(Person person); - Task DeletePerson(int id); - Task UpdatePerson(Person person); -} diff --git a/HangTab/Repositories/IWeekRepository.cs b/HangTab/Repositories/IWeekRepository.cs deleted file mode 100644 index b3d98c9..0000000 --- a/HangTab/Repositories/IWeekRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using HangTab.Models; - -namespace HangTab.Repositories; -public interface IWeekRepository -{ - Task CreateAsync(int weekNumber = 1); - Task> GetAllAsync(); - Task GetByIdAsync(int id); - Task UpdateAsync(Week week); -} diff --git a/HangTab/Repositories/Impl/BaseRepository.cs b/HangTab/Repositories/Impl/BaseRepository.cs new file mode 100644 index 0000000..046cd39 --- /dev/null +++ b/HangTab/Repositories/Impl/BaseRepository.cs @@ -0,0 +1,37 @@ +using HangTab.Data; + +namespace HangTab.Repositories.Impl; +public partial class BaseRepository(IDatabaseContext context) : IBaseRepository where T : class, new() +{ + public Task AddAsync(T item) + { + ArgumentNullException.ThrowIfNull(item); + return context.AddItemAsync(item); + } + + public Task DeleteByIdAsync(int id) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); + return context.DeleteItemByIdAsync(id); + } + + public Task> GetAllAsync() => context.GetAllWithChildrenAsync(); + + public Task GetByIdAsync(int id) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); + return context.GetItemByIdAsync(id); + } + + public Task> GetFilteredAsync(System.Linq.Expressions.Expression> predicate) + { + ArgumentNullException.ThrowIfNull(predicate); + return context.GetFilteredAsync(predicate); + } + + public Task UpdateAsync(T item) + { + ArgumentNullException.ThrowIfNull(item); + return context.UpdateItemAsync(item); + } +} diff --git a/HangTab/Repositories/Impl/BowlerRepository.cs b/HangTab/Repositories/Impl/BowlerRepository.cs deleted file mode 100644 index b84e097..0000000 --- a/HangTab/Repositories/Impl/BowlerRepository.cs +++ /dev/null @@ -1,38 +0,0 @@ -using HangTab.Data; -using HangTab.Models; - -namespace HangTab.Repositories.Impl; -public class BowlerRepository(IDatabaseContext context) : IBowlerRepository -{ - public Task AddAsync(Bowler bowler) - { - ArgumentNullException.ThrowIfNull(bowler); - return context.AddItemAsync(bowler); - } - - public Task> GetAllByWeekIdAsync(int id) - { - ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); - return context.GetAllWithChildrenAsync(wl => wl.WeekId == id); - } - - public Task> GetAllAsync() => context.GetAllWithChildrenAsync(); - - public Task GetByIdAsync(int id) - { - ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); - return context.GetItemByIdAsync(id); - } - - public Task RemoveAsync(int id) - { - ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); - return context.DeleteItemByIdAsync(id); - } - - public Task UpdateAsync(Bowler bowler) - { - ArgumentNullException.ThrowIfNull(bowler); - return context.UpdateItemAsync(bowler); - } -} diff --git a/HangTab/Repositories/Impl/PersonRepository.cs b/HangTab/Repositories/Impl/PersonRepository.cs deleted file mode 100644 index d165a96..0000000 --- a/HangTab/Repositories/Impl/PersonRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using HangTab.Data; -using HangTab.Models; - -namespace HangTab.Repositories.Impl; -public class PersonRepository(IDatabaseContext context) : IPersonRepository -{ - public Task GetPersonById(int id) => context.GetItemByIdAsync(id); - public Task> GetAllPeople() => context.GetAllAsync(); - public Task> GetRegulars() => context.GetFilteredAsync(b => !b.IsSub); - public Task> GetSubstitutes() => context.GetFilteredAsync(b => b.IsSub); - public Task AddPerson(Person person) => context.AddItemAsync(person); - public Task DeletePerson(int id) => context.DeleteItemByIdAsync(id); - public Task UpdatePerson(Person person) => context.UpdateItemAsync(person); -} diff --git a/HangTab/Repositories/Impl/WeekRepository.cs b/HangTab/Repositories/Impl/WeekRepository.cs deleted file mode 100644 index ab44468..0000000 --- a/HangTab/Repositories/Impl/WeekRepository.cs +++ /dev/null @@ -1,70 +0,0 @@ -using HangTab.Data; -using HangTab.Models; - -namespace HangTab.Repositories.Impl; -public class WeekRepository(IDatabaseContext context) : IWeekRepository -{ - public async Task CreateAsync(int weekNumber = 1) - { - var week = new Week - { - Number = weekNumber - }; - await context.AddItemAsync(week); - - var regulars = await context.GetFilteredAsync(p => !p.IsSub); - if (!regulars.Any()) - { - return week; - } - - foreach (var regular in regulars) - { - var bowler = new Bowler - { - PersonId = regular.Id, - WeekId = week.Id - }; - await context.AddItemAsync(bowler); - } - - return week; - } - - public Task> GetAllAsync() => context.GetAllWithChildrenAsync(); - - public async Task GetByIdAsync(int id) - { - if (id < 1) - { - return await CreateAsync(); // Create the first week if no valid ID is provided - } - - var week = await context.GetItemByIdAsync(id); - if (week is null) - { - return new Week(); - } - - var lineup = new List(); - var bowlers = await context.GetFilteredAsync(b => b.WeekId == id); - foreach (var bowler in bowlers) - { - lineup.Add(new Bowler - { - Id = bowler.Id, - Status = bowler.Status, - HangCount = bowler.HangCount, - WeekId = bowler.WeekId, - PersonId = bowler.PersonId, - SubId = bowler.SubId, - Person = await context.GetItemByIdAsync(bowler.SubId is null ? bowler.PersonId : bowler.SubId) - }); - } - - week.Bowlers = lineup; - return week; - } - - public Task UpdateAsync(Week week) => context.UpdateWithChildrenAsync(week); -} From 4cadada1461fcfc80890bb076cc853696d806403 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:52:29 -0500 Subject: [PATCH 08/12] updated services to now use base repo and return FluentResults --- HangTab/Services/IBowlerService.cs | 12 ++-- HangTab/Services/IPersonService.cs | 14 ++-- HangTab/Services/IWeekService.cs | 8 +-- HangTab/Services/Impl/BowlerService.cs | 77 ++++++++++++++++++++-- HangTab/Services/Impl/PersonService.cs | 89 +++++++++++++++++++++++--- HangTab/Services/Impl/WeekService.cs | 88 +++++++++++++++++++++++-- 6 files changed, 251 insertions(+), 37 deletions(-) diff --git a/HangTab/Services/IBowlerService.cs b/HangTab/Services/IBowlerService.cs index 09edc3f..82bf4af 100644 --- a/HangTab/Services/IBowlerService.cs +++ b/HangTab/Services/IBowlerService.cs @@ -3,10 +3,10 @@ namespace HangTab.Services; public interface IBowlerService { - Task AddAsync(Bowler bowler); - Task> GetAllAsync(); - Task> GetAllByWeekIdAsync(int id); - Task GetByIdAsync(int id); - Task RemoveAsync(int id); - Task UpdateAsync(Bowler bowler); + Task AddAsync(Bowler bowler); + Task>> GetAllAsync(); + Task>> GetAllByWeekIdAsync(int id); + Task> GetByIdAsync(int id); + Task RemoveAsync(int id); + Task UpdateAsync(Bowler bowler); } diff --git a/HangTab/Services/IPersonService.cs b/HangTab/Services/IPersonService.cs index a16d4db..a7ceb6c 100644 --- a/HangTab/Services/IPersonService.cs +++ b/HangTab/Services/IPersonService.cs @@ -3,11 +3,11 @@ namespace HangTab.Services; public interface IPersonService { - Task GetPersonById(int id); - Task> GetAllPeople(); - Task> GetRegulars(); - Task> GetSubstitutes(); - Task AddPerson(Person person); - Task DeletePerson(int id); - Task UpdatePerson(Person person); + Task AddAsync(Person person); + Task DeleteAsync(int id); + Task>> GetAllAsync(); + Task> GetByIdAsync(int id); + Task>> GetRegularsAsync(); + Task>> GetSubstitutesAsync(); + Task UpdateAsync(Person person); } diff --git a/HangTab/Services/IWeekService.cs b/HangTab/Services/IWeekService.cs index 881b78c..f43cd47 100644 --- a/HangTab/Services/IWeekService.cs +++ b/HangTab/Services/IWeekService.cs @@ -3,8 +3,8 @@ namespace HangTab.Services; public interface IWeekService { - Task CreateAsync(int weekNumber); - Task> GetAllAsync(); - Task GetByIdAsync(int id); - Task UpdateAsync(Week week); + Task> AddAsync(int weekNumber = 1); + Task>> GetAllAsync(); + Task> GetByIdAsync(int id); + Task UpdateAsync(Week week); } diff --git a/HangTab/Services/Impl/BowlerService.cs b/HangTab/Services/Impl/BowlerService.cs index 988fc19..e689a48 100644 --- a/HangTab/Services/Impl/BowlerService.cs +++ b/HangTab/Services/Impl/BowlerService.cs @@ -2,12 +2,75 @@ using HangTab.Repositories; namespace HangTab.Services.Impl; -public class BowlerService(IBowlerRepository repo) : IBowlerService +public class BowlerService(IBaseRepository repo) : IBowlerService { - public Task AddAsync(Bowler bowler) => repo.AddAsync(bowler); - public Task> GetAllByWeekIdAsync(int id) => repo.GetAllByWeekIdAsync(id); - public Task> GetAllAsync() => repo.GetAllAsync(); - public Task GetByIdAsync(int id) => repo.GetByIdAsync(id); - public Task RemoveAsync(int id) => repo.RemoveAsync(id); - public Task UpdateAsync(Bowler bowler) => repo.UpdateAsync(bowler); + public async Task AddAsync(Bowler bowler) + { + if (bowler is null) + { + return Result.Fail("Bowler cannot be null"); + } + + return await repo.AddAsync(bowler) + ? Result.Ok() + : Result.Fail("Failed to add the bowler"); + } + + public async Task>> GetAllAsync() + { + var bowlers = await repo.GetAllAsync(); + return bowlers is null || !bowlers.Any() + ? Result.Fail("No bowlers found") + : Result.Ok(bowlers); + } + + public async Task>> GetAllByWeekIdAsync(int id) + { + if (id < 1) + { + return Result.Fail("Invalid week ID"); + } + + var bowlers = await repo.GetFilteredAsync(b => b.WeekId == id); + return bowlers is null || !bowlers.Any() + ? Result.Fail("No bowlers found for the specified week") + : Result.Ok(bowlers); + } + + public async Task> GetByIdAsync(int id) + { + if (id < 1) + { + return Result.Fail("Invalid bowler ID"); + } + + var bowler = await repo.GetByIdAsync(id); + return bowler is null + ? Result.Fail("Bowler not found") + : Result.Ok(bowler); + } + + public async Task RemoveAsync(int id) + { + if (id < 1) + { + return Result.Fail("Invalid bowler ID"); + } + + return await repo.DeleteByIdAsync(id) + ? Result.Ok() + : Result.Fail("Failed to remove the bowler"); + } + + public async Task UpdateAsync(Bowler bowler) + { + if (bowler is null) + { + return Result.Fail("Bowler cannot be null"); + } + + return await repo.UpdateAsync(bowler) + ? Result.Ok() + : Result.Fail("Failed to update the bowler"); + } } diff --git a/HangTab/Services/Impl/PersonService.cs b/HangTab/Services/Impl/PersonService.cs index 23dd909..4b39caa 100644 --- a/HangTab/Services/Impl/PersonService.cs +++ b/HangTab/Services/Impl/PersonService.cs @@ -2,13 +2,86 @@ using HangTab.Repositories; namespace HangTab.Services.Impl; -public class PersonService(IPersonRepository personRepository) : IPersonService +public class PersonService(IBaseRepository repo) : IPersonService { - public Task GetPersonById(int id) => personRepository.GetPersonById(id); - public Task> GetAllPeople() => personRepository.GetAllPeople(); - public Task> GetRegulars() => personRepository.GetRegulars(); - public Task> GetSubstitutes() => personRepository.GetSubstitutes(); - public Task AddPerson(Person person) => personRepository.AddPerson(person); - public Task DeletePerson(int id) => personRepository.DeletePerson(id); - public Task UpdatePerson(Person person) => personRepository.UpdatePerson(person); + public async Task AddAsync(Person person) + { + if (person is null) + { + return Result.Fail("Person cannot be null."); + } + + return await repo.AddAsync(person) + ? Result.Ok() + : Result.Fail("Failed to add the person."); + } + + public async Task DeleteAsync(int id) + { + if (id < 1) + { + return Result.Fail("Invalid person ID."); + } + + var person = await repo.GetByIdAsync(id); + if (person is null) + { + return Result.Fail($"Person with ID {id} was not found."); + } + + person.IsDeleted = true; + + return await repo.UpdateAsync(person) + ? Result.Ok() + : Result.Fail("Failed to delete the person."); + } + + public async Task>> GetAllAsync() + { + var people = await repo.GetAllAsync(); + return people is null || !people.Any() + ? Result.Fail("No people found.") + : Result.Ok(people); + } + + public async Task> GetByIdAsync(int id) + { + if (id < 1) + { + return Result.Fail("Invalid person ID."); + } + + var person = await repo.GetByIdAsync(id); + return person is null + ? Result.Fail($"Person with ID {id} was not found.") + : Result.Ok(person); + } + + public async Task>> GetRegularsAsync() + { + var people = await repo.GetFilteredAsync(b => !b.IsSub); + return people is null || !people.Any() + ? Result.Fail("No regulars found.") + : Result.Ok(people); + } + + public async Task>> GetSubstitutesAsync() + { + var people = await repo.GetFilteredAsync(b => b.IsSub); + return people is null || !people.Any() + ? Result.Fail("No substitutes found.") + : Result.Ok(people); + } + + public async Task UpdateAsync(Person person) + { + if (person is null) + { + return Result.Fail("Person cannot be null."); + } + + return await repo.UpdateAsync(person) + ? Result.Ok() + : Result.Fail("Failed to update the person."); + } } diff --git a/HangTab/Services/Impl/WeekService.cs b/HangTab/Services/Impl/WeekService.cs index 22b6494..7d59e82 100644 --- a/HangTab/Services/Impl/WeekService.cs +++ b/HangTab/Services/Impl/WeekService.cs @@ -2,10 +2,88 @@ using HangTab.Repositories; namespace HangTab.Services.Impl; -public class WeekService(IWeekRepository repo) : IWeekService +public class WeekService( + IBaseRepository weekRepo, + IBaseRepository personRepo, + IBaseRepository bowlerRepo) : IWeekService { - public Task CreateAsync(int weekNumber) => repo.CreateAsync(weekNumber); - public Task> GetAllAsync() => repo.GetAllAsync(); - public Task GetByIdAsync(int id) => repo.GetByIdAsync(id); - public Task UpdateAsync(Week week) => repo.UpdateAsync(week); + public async Task> AddAsync(int weekNumber = 1) + { + var week = new Week + { + Number = weekNumber + }; + await weekRepo.AddAsync(week); + + var regulars = await personRepo.GetFilteredAsync(p => !p.IsSub); + if (!regulars.Any()) + { + return Result.Ok(week); + } + + foreach (var regular in regulars) + { + var bowler = new Bowler + { + PersonId = regular.Id, + WeekId = week.Id + }; + await bowlerRepo.AddAsync(bowler); + } + + return Result.Ok(week); + } + + public async Task>> GetAllAsync() + { + var weeks = await weekRepo.GetAllAsync(); + return weeks is null || !weeks.Any() + ? Result.Fail>("No weeks found.") + : Result.Ok(weeks); + } + + public async Task> GetByIdAsync(int id) + { + if (id < 1) + { + return await AddAsync(); // Create the first week if no valid ID is provided + } + + var week = await weekRepo.GetByIdAsync(id); + if (week is null) + { + return Result.Ok(new Week()); + } + + var lineup = new List(); + var bowlers = await bowlerRepo.GetFilteredAsync(b => b.WeekId == id); + foreach (var bowler in bowlers) + { + lineup.Add(new Bowler + { + Id = bowler.Id, + Status = bowler.Status, + HangCount = bowler.HangCount, + WeekId = bowler.WeekId, + PersonId = bowler.PersonId, + SubId = bowler.SubId, + Person = await personRepo.GetByIdAsync(bowler.SubId is not null ? (int)bowler.SubId : bowler.PersonId) + }); + } + + week.Bowlers = lineup; + return Result.Ok(week); + } + + public async Task UpdateAsync(Week week) + { + if (week is null) + { + return Result.Fail("Week cannot be null."); + } + + return await weekRepo.UpdateAsync(week) + ? Result.Ok() + : Result.Fail("Failed to update the week."); + } } From 35407c756d66749cd689c900c6ee6a860bc36d8f Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:54:11 -0500 Subject: [PATCH 09/12] updated viewmodels to use modified services --- HangTab/MauiProgram.cs | 4 +- .../ViewModels/BowlerSelectSubViewModel.cs | 11 +-- .../CurrentWeekOverviewViewModel.cs | 59 ++++++++------- HangTab/ViewModels/PersonAddEditViewModel.cs | 72 +++++++++++-------- .../ViewModels/PersonListOverviewViewModel.cs | 10 +-- HangTab/ViewModels/SeasonSummaryViewModel.cs | 16 +++-- HangTab/ViewModels/WeekDetailsViewModel.cs | 7 +- .../ViewModels/WeekListOverviewViewModel.cs | 5 +- HangTab/Views/WeekListOverviewPage.xaml | 2 +- 9 files changed, 101 insertions(+), 85 deletions(-) diff --git a/HangTab/MauiProgram.cs b/HangTab/MauiProgram.cs index 8902600..293162c 100644 --- a/HangTab/MauiProgram.cs +++ b/HangTab/MauiProgram.cs @@ -75,10 +75,8 @@ private static MauiAppBuilder RegisterRepositories(this MauiAppBuilder builder) builder.Services.AddSingleton(new ShareRepository(Share.Default)); builder.Services.AddSingleton(new StorageRepository(FileSystem.Current)); - builder.Services.AddTransient(); + builder.Services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>)); builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); return builder; } diff --git a/HangTab/ViewModels/BowlerSelectSubViewModel.cs b/HangTab/ViewModels/BowlerSelectSubViewModel.cs index c420329..9c770b3 100644 --- a/HangTab/ViewModels/BowlerSelectSubViewModel.cs +++ b/HangTab/ViewModels/BowlerSelectSubViewModel.cs @@ -94,15 +94,16 @@ private async Task GetSubsAsync() private async Task> GetAvailableSubsAsync() { - var subs = await personService.GetSubstitutesAsync(); - if (!subs.Any()) + var subsResult = await personService.GetSubstitutesAsync(); + if (subsResult.IsFailed) { return []; } - var bowlers = await bowlerService.GetAllByWeekIdAsync(_bowler?.WeekId ?? 0); - return bowlers.Any() - ? subs.Where(s => !bowlers.Any(b => b.SubId == s.Id)) + var subs = subsResult.Value; + var bowlersResult = await bowlerService.GetAllByWeekIdAsync(_bowler?.WeekId ?? 0); + return bowlersResult.IsSuccess + ? subs.Where(s => !bowlersResult.Value.Any(b => b.SubId == s.Id)) : subs; } diff --git a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs index 77aba81..80e7cb1 100644 --- a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs +++ b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs @@ -173,30 +173,29 @@ private async Task SubmitWeekAsync() private async Task StartNewWeekAsync() { - await _weekService.CreateAsync(CurrentWeek.Number + 1).ContinueWith(async saveTask => + var result = await _weekService.AddAsync(CurrentWeek.Number + 1); + if (result.IsFailed) { - if (saveTask.IsCompletedSuccessfully) - { - var newWeek = await saveTask; - _settingsService.CurrentWeekPrimaryKey = newWeek.Id; - await GetCurrentWeekAsync(); - InitializeCurrentWeekPageSettings(); - } - else - { - await _dialogService.AlertAsync("Error", "Unable to create new week.", "Ok"); - } - }); + await _dialogService.AlertAsync("Error", "Unable to create a new week.", "Ok"); + return; + } + + var newWeek = result.Value; + _settingsService.CurrentWeekPrimaryKey = newWeek.Id; + await GetCurrentWeekAsync(); + InitializeCurrentWeekPageSettings(); } private async Task GetCurrentWeekAsync() { - CurrentWeek = await _weekService.GetByIdAsync(_settingsService.CurrentWeekPrimaryKey); - if (CurrentWeek is null) + var result = await _weekService.GetByIdAsync(_settingsService.CurrentWeekPrimaryKey); + if (result.IsFailed) { + await _dialogService.AlertAsync("Error", "Unable to load current week data.", "Ok"); return; } + CurrentWeek = result.Value; _settingsService.CurrentWeekPrimaryKey = CurrentWeek.Id; if (CurrentWeek.Bowlers.Count > 0) { @@ -257,26 +256,26 @@ private async Task BowlerHangCountChangedAsync(CurrentWeekListItemViewModel? vm) return; } - if (await _bowlerService.UpdateAsync(vm.ToBowler())) + var result = await _bowlerService.UpdateAsync(vm.ToBowler()); + if (result.IsFailed) { - var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); - var isIncrease = newHangTotal > TeamHangTotal; - TeamHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); - CurrentWeekBowlers.SetLowestBowlerHangCount(); + await _dialogService.AlertAsync("Error", "Unable to update bowler hang count", "Ok"); + return; + } - if (isIncrease) - { - PlayPopperAnimation = true; - await Task.Delay(1000); - PlayPopperAnimation = false; - } + var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); + var isIncrease = newHangTotal > TeamHangTotal; + TeamHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); + CurrentWeekBowlers.SetLowestBowlerHangCount(); - _messenger.Send(new BowlerHangCountChangedMessage(vm.BowlerId, vm.HangCount)); - } - else + if (isIncrease) { - await _dialogService.AlertAsync("Error", "Unable to update bowler hang count", "Ok"); + PlayPopperAnimation = true; + await Task.Delay(1000); + PlayPopperAnimation = false; } + + _messenger.Send(new BowlerHangCountChangedMessage(vm.BowlerId, vm.HangCount)); } public async void Receive(SystemResetMessage message) diff --git a/HangTab/ViewModels/PersonAddEditViewModel.cs b/HangTab/ViewModels/PersonAddEditViewModel.cs index cbf9f1c..e7bb804 100644 --- a/HangTab/ViewModels/PersonAddEditViewModel.cs +++ b/HangTab/ViewModels/PersonAddEditViewModel.cs @@ -96,7 +96,11 @@ await Loading( { if (_person is null && Id > 0) { - _person = await _personService.GetPersonById(Id); + var result = await _personService.GetByIdAsync(Id); + if (result.IsSuccess) + { + _person = result.Value; + } } if (_person is not null) @@ -117,19 +121,22 @@ private async Task DeletePersonAsync() return; } - if (await _dialogService.Ask("Delete", "Are you sure you want to delete this bowler?")) + if (!await _dialogService.Ask("Delete", "Are you sure you want to delete this bowler?")) { - if (await _personService.DeletePerson(_person.Id)) - { - _messenger.Send(new PersonDeletedMessage(_person.Id)); - } - else - { - await _dialogService.AlertAsync("Critical Error", "Error occurred while deleting the bowler!", "Ok"); - } + return; + } - await _navigationService.GoBack(); + var result = await _personService.DeleteAsync(Id); + if (result.IsSuccess) + { + _messenger.Send(new PersonDeletedMessage(_person.Id)); } + else + { + await _dialogService.AlertAsync("Critical Error", "Error occurred while deleting the bowler!", "Ok"); + } + + await _navigationService.GoBack(); } [RelayCommand] @@ -153,29 +160,34 @@ private async Task SubmitAsync() } var person = MapDataToPerson(); - if (Id == 0) + var result = await SaveBowler(person); + if (result.IsSuccess) { - if (await _personService.AddPerson(person)) - { - _messenger.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); - } - else - { - await _dialogService.AlertAsync("Error", "The bowler could not be added.", "Ok"); - } - await _navigationService.GoBack(); + _messenger.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); } else { - if (await _personService.UpdatePerson(person)) - { - _messenger.Send(new PersonAddedOrChangedMessage(person.Id, person.IsSub)); - } - else - { - await _dialogService.AlertAsync("Error", "The bowler could not be updated.", "Ok"); - } - await _navigationService.GoBack(); + await _dialogService.AlertAsync("Error", result.Errors[0].Message, "Ok"); + } + + await _navigationService.GoBack(); + } + + private async Task SaveBowler(Person person) + { + if (person.Id == 0) + { + var addResult = await _personService.AddAsync(person); + return addResult.IsSuccess + ? Result.Ok() + : Result.Fail("Failed to add the bowler."); + } + else + { + var updateResult = await _personService.UpdateAsync(person); + return updateResult.IsSuccess + ? Result.Ok() + : Result.Fail("Failed to update the bowler."); } } diff --git a/HangTab/ViewModels/PersonListOverviewViewModel.cs b/HangTab/ViewModels/PersonListOverviewViewModel.cs index 5ee3451..51322f0 100644 --- a/HangTab/ViewModels/PersonListOverviewViewModel.cs +++ b/HangTab/ViewModels/PersonListOverviewViewModel.cs @@ -104,12 +104,13 @@ public override async Task LoadAsync() private async Task GetBowlersAsync() { - var people = await _personService.GetAllPeople(); - if (!people.Any()) + var result = await _personService.GetAllAsync(); + if (result.IsFailed) { return; } + var people = result.Value; Bowlers.Clear(); AllBowlers = people.OrderBy(b => b.Name).ToBowlerListItemViewModelList(); Bowlers = AllBowlers.ToObservableCollection(); @@ -121,12 +122,13 @@ private async Task UpdateBowlerHangCountsAsync() { if (Bowlers.Count > 0) { - var allWeeks = await _weekService.GetAllWeeks(); - if (allWeeks is null) + var result = await _weekService.GetAllAsync(); + if (result.IsFailed) { return; } + var allWeeks = result.Value; Bowlers.SetBowlerHangSumByWeeks(allWeeks); Bowlers.SetLowestBowlerHangCount(); } diff --git a/HangTab/ViewModels/SeasonSummaryViewModel.cs b/HangTab/ViewModels/SeasonSummaryViewModel.cs index 7dcf93b..b0ae566 100644 --- a/HangTab/ViewModels/SeasonSummaryViewModel.cs +++ b/HangTab/ViewModels/SeasonSummaryViewModel.cs @@ -60,18 +60,20 @@ private async Task SubmitSeasonAsync() private async Task GetBowlers() { - var people = await personService.GetRegularsAsync(); - if (!people.Any()) + var regularsResult = await personService.GetRegularsAsync(); + if (regularsResult.IsFailed) { return; } - var allWeeks = await weekService.GetAllAsync(); - if (!allWeeks.Any()) + var people = regularsResult.Value; + var weeksResult = await weekService.GetAllAsync(); + if (weeksResult.IsFailed) { return; } + var allWeeks = weeksResult.Value; var bowlers = people.OrderBy(b => b.Name).ToBowlerListItemViewModelList().ToList(); bowlers.SetBowlerHangSumByWeeks(allWeeks); @@ -80,13 +82,13 @@ private async Task GetBowlers() private async Task GetWeeks() { - var allWeeks = await weekService.GetAllAsync(); - if (!allWeeks.Any()) + var result = await weekService.GetAllAsync(); + if (result.IsFailed) { return; } - var weeks = allWeeks + var weeks = result.Value .Select(w => new WeekListItemViewModel { Id = w.Id, diff --git a/HangTab/ViewModels/WeekDetailsViewModel.cs b/HangTab/ViewModels/WeekDetailsViewModel.cs index 93ae7f3..c71ed63 100644 --- a/HangTab/ViewModels/WeekDetailsViewModel.cs +++ b/HangTab/ViewModels/WeekDetailsViewModel.cs @@ -49,11 +49,12 @@ await Loading( private async Task GetWeek(int id) { - var week = await weekService.GetByIdAsync(id); - if (week is not null) + var result = await weekService.GetByIdAsync(id); + if (result.IsFailed) { - MapWeekData(week); + return; } + MapWeekData(result.Value); } private void InitializeCurrentWeekPageSettings() diff --git a/HangTab/ViewModels/WeekListOverviewViewModel.cs b/HangTab/ViewModels/WeekListOverviewViewModel.cs index 50a6e3c..f3cba5d 100644 --- a/HangTab/ViewModels/WeekListOverviewViewModel.cs +++ b/HangTab/ViewModels/WeekListOverviewViewModel.cs @@ -26,12 +26,13 @@ public partial class WeekListOverviewViewModel( private async Task GetWeeks() { - var weeks = await weekService.GetAllAsync(); - if (!weeks.Any()) + var result = await weekService.GetAllAsync(); + if (result.IsFailed) { return; } + var weeks = result.Value; Weeks.Clear(); Weeks = weeks.Where(w => w.Id != settingsService.CurrentWeekPrimaryKey).OrderByDescending(w => w.Number).ToWeekListItemViewModelList().ToObservableCollection(); } diff --git a/HangTab/Views/WeekListOverviewPage.xaml b/HangTab/Views/WeekListOverviewPage.xaml index 914683d..9dd86f9 100644 --- a/HangTab/Views/WeekListOverviewPage.xaml +++ b/HangTab/Views/WeekListOverviewPage.xaml @@ -119,7 +119,7 @@ VerticalOptions="Center" FontSize="Medium" TextColor="{DynamicResource TextPrimaryColor}" - Text="{Binding HangCount}" /> + Text="{Binding TotalHangCount}" /> From dcb3df706b2e6da6f60a8fc115fe5243ec25b4ad Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:54:23 -0500 Subject: [PATCH 10/12] updated tests --- .../Repositories/BowlerRepositoryTests.cs | 223 ------------------ .../Repositories/PersonRepositoryTests.cs | 132 ----------- .../Repositories/WeekRepositoryTests.cs | 153 ------------ HangTab.Tests/Services/BowlerServiceTests.cs | 115 ++++----- HangTab.Tests/Services/PersonServiceTests.cs | 136 +++++------ HangTab.Tests/Services/WeekServiceTests.cs | 93 +++----- 6 files changed, 151 insertions(+), 701 deletions(-) delete mode 100644 HangTab.Tests/Repositories/BowlerRepositoryTests.cs delete mode 100644 HangTab.Tests/Repositories/PersonRepositoryTests.cs delete mode 100644 HangTab.Tests/Repositories/WeekRepositoryTests.cs diff --git a/HangTab.Tests/Repositories/BowlerRepositoryTests.cs b/HangTab.Tests/Repositories/BowlerRepositoryTests.cs deleted file mode 100644 index efb6669..0000000 --- a/HangTab.Tests/Repositories/BowlerRepositoryTests.cs +++ /dev/null @@ -1,223 +0,0 @@ -using HangTab.Data; -using HangTab.Models; -using HangTab.Repositories.Impl; - -using System.Linq.Expressions; - -namespace HangTab.Tests.Repositories; - -public class BowlerRepositoryTests -{ - [Fact] - public async Task AddBowler_CallsContextAndReturnsResult() - { - // Arrange - var bowler = new Bowler { Id = 1, WeekId = 2, PersonId = 3 }; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.AddItemAsync(A._)).Returns(true); - - // Act - var actual = await bowlerRepository.AddAsync(bowler); - - // Assert - Assert.True(actual); - A.CallTo(() => context.AddItemAsync(A._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetAllBowlersByWeekId_CallsContextWithCorrectPredicate() - { - // Arrange - var weekId = 5; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - var expected = new List { new() { Id = 1, WeekId = weekId, PersonId = 2 } }; - A.CallTo(() => context.GetAllWithChildrenAsync(A>>._)) - .Returns(expected); - - // Act - var actual = await bowlerRepository.GetAllByWeekIdAsync(weekId); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetAllWithChildrenAsync(A>>._)) - .MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetAllBowlers_CallsContext() - { - // Arrange - var expected = new List { new() { Id = 1 } }; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.GetAllWithChildrenAsync(null)).Returns(expected); - - // Act - var actual = await bowlerRepository.GetAllAsync(); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetAllWithChildrenAsync(null)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetBowlerById_CallsContextWithId() - { - // Arrange - var id = 7; - var bowler = new Bowler { Id = id }; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(bowler); - - // Act - var result = await bowlerRepository.GetByIdAsync(id); - - // Assert - Assert.Equal(bowler, result); - A.CallTo(() => context.GetItemByIdAsync(A._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetBowlersByWeekId_CallsContextWithCorrectPredicate() - { - // Arrange - var weekId = 3; - var expected = new List { new() { Id = 2, WeekId = weekId } }; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.GetAllWithChildrenAsync(A>>._)) - .Returns(expected); - - // Act - var actual = await bowlerRepository.GetAllByWeekIdAsync(weekId); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetAllWithChildrenAsync(A>>._)) - .MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task UpdateBowler_CallsContextAndReturnsResult() - { - // Arrange - var bowler = new Bowler { Id = 4 }; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.UpdateItemAsync(A._)).Returns(true); - - // Act - var result = await bowlerRepository.UpdateAsync(bowler); - - // Assert - Assert.True(result); - A.CallTo(() => context.UpdateItemAsync(A._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task AddBowler_ThrowsOnNull() - { - // Arrange - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - - // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.AddAsync(null!)); - } - - [Fact] - public async Task GetAllBowlersByWeekId_ThrowsOnInvalidId() - { - // Arrange - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - - // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(-1)); - } - - [Fact] - public async Task GetBowlerById_ThrowsOnInvalidId() - { - // Arrange - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - - // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetByIdAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetByIdAsync(-5)); - } - - [Fact] - public async Task GetBowlersByWeekId_ThrowsOnInvalidId() - { - // Arrange - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - - // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.GetAllByWeekIdAsync(-2)); - } - - [Fact] - public async Task UpdateBowler_ThrowsOnNull() - { - // Arrange - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - - // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.UpdateAsync(null!)); - } - - [Fact] - public async Task RemoveBowler_CallsContextAndReturnsTrue() - { - // Arrange - var id = 10; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.DeleteItemByIdAsync(id)).Returns(true); - - // Act - var result = await bowlerRepository.RemoveAsync(id); - - // Assert - Assert.True(result); - A.CallTo(() => context.DeleteItemByIdAsync(id)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task RemoveBowler_CallsContextAndReturnsFalse() - { - // Arrange - var id = 99; - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - A.CallTo(() => context.DeleteItemByIdAsync(id)).Returns(false); - - // Act - var result = await bowlerRepository.RemoveAsync(id); - - // Assert - Assert.False(result); - A.CallTo(() => context.DeleteItemByIdAsync(id)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task RemoveBowler_ThrowsOnInvalidId() - { - // Arrange - var context = A.Fake(); - var bowlerRepository = new BowlerRepository(context); - - // Act & Assert - await Assert.ThrowsAsync(() => bowlerRepository.RemoveAsync(0)); - await Assert.ThrowsAsync(() => bowlerRepository.RemoveAsync(-1)); - } -} \ No newline at end of file diff --git a/HangTab.Tests/Repositories/PersonRepositoryTests.cs b/HangTab.Tests/Repositories/PersonRepositoryTests.cs deleted file mode 100644 index e118a95..0000000 --- a/HangTab.Tests/Repositories/PersonRepositoryTests.cs +++ /dev/null @@ -1,132 +0,0 @@ -using HangTab.Data; -using HangTab.Models; -using HangTab.Repositories.Impl; - -using System.Linq.Expressions; - -namespace HangTab.Tests.Repositories; - -public class PersonRepositoryTests -{ - private readonly IDatabaseContext _context = A.Fake(); - - private PersonRepository CreateRepo() => new(_context); - - [Fact] - public async Task GetPersonById_CallsContextWithId() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - var expected = new Person { Id = 1, Name = "Test" }; - A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(expected); - - // Act - var actual = await personRepository.GetPersonById(1); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetItemByIdAsync(A._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetAllPeople_CallsContext() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - var expected = new List { new() { Id = 1, Name = "A" } }; - A.CallTo(() => context.GetAllAsync()).Returns(expected); - - // Act - var actual = await personRepository.GetAllPeople(); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetAllAsync()).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetRegulars_CallsContextWithCorrectPredicate() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - var expected = new List { new() { Id = 2, Name = "Reg", IsSub = false } }; - A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns(expected); - - // Act - var actual = await personRepository.GetRegulars(); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetFilteredAsync(A>>._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetSubstitutes_CallsContextWithCorrectPredicate() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - var expected = new List { new() { Id = 3, Name = "Sub", IsSub = true } }; - A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns(expected); - - // Act - var actual = await personRepository.GetSubstitutes(); - - // Assert - Assert.Equal(expected, actual); - A.CallTo(() => context.GetFilteredAsync(A>>._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task AddPerson_CallsContextAndReturnsResult() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - var expected = new Person { Id = 4, Name = "Add" }; - A.CallTo(() => context.AddItemAsync(A._)).Returns(true); - - // Act - var actual = await personRepository.AddPerson(expected); - - // Assert - Assert.True(actual); - A.CallTo(() => context.AddItemAsync(A._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task DeletePerson_CallsContextAndReturnsResult() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - A.CallTo(() => context.DeleteItemByIdAsync(A._)).Returns(true); - - // Act - var actual = await personRepository.DeletePerson(5); - - // Assert - Assert.True(actual); - A.CallTo(() => context.DeleteItemByIdAsync(A._)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task UpdatePerson_CallsContextAndReturnsResult() - { - // Arrange - var context = A.Fake(); - var personRepository = new PersonRepository(context); - var expected = new Person { Id = 6, Name = "Update" }; - A.CallTo(() => context.UpdateItemAsync(A._)).Returns(true); - - // Act - var actual = await personRepository.UpdatePerson(expected); - - // Assert - Assert.True(actual); - A.CallTo(() => context.UpdateItemAsync(A._)).MustHaveHappenedOnceExactly(); - } -} \ No newline at end of file diff --git a/HangTab.Tests/Repositories/WeekRepositoryTests.cs b/HangTab.Tests/Repositories/WeekRepositoryTests.cs deleted file mode 100644 index a87cf2c..0000000 --- a/HangTab.Tests/Repositories/WeekRepositoryTests.cs +++ /dev/null @@ -1,153 +0,0 @@ -using HangTab.Data; -using HangTab.Models; -using HangTab.Repositories.Impl; - -using System.Linq.Expressions; - -namespace HangTab.Tests.Repositories; - -public class WeekRepositoryTests -{ - private readonly IDatabaseContext _context = A.Fake(); - - [Fact] - public async Task GetWeekById_WithValidId_ReturnsWeekWithBowlers() - { - // Arrange - var personId = 10; - var personName = "Bowler"; - var weekId = 2; - var week = new Week { Id = weekId, Number = 5 }; - var bowlers = new List - { - new() { Id = 1, WeekId = weekId, PersonId = personId, Status = Enums.Status.Active, HangCount = 3, SubId = null } - }; - var person = new Person { Id = personId, Name = personName }; - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - - A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(week); - A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns(bowlers); - A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(person); - - // Act - var actual = await weekRepository.GetByIdAsync(weekId); - - // Assert - Assert.Equal(weekId, actual.Id); - Assert.Single(actual.Bowlers); - Assert.Equal(personId, actual.Bowlers[0].PersonId); - Assert.Equal(personName, actual.Bowlers[0].Person.Name); - } - - [Fact] - public async Task GetWeekById_WithIdLessThan1_CreatesWeek() - { - // Arrange - var createdWeek = new Week { Id = 1, Number = 1 }; - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - A.CallTo(() => context.AddItemAsync(A._)).Returns(true); - A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns([]); - // Simulate CreateWeek returns createdWeek - A.CallTo(() => context.GetItemByIdAsync(A._)).Returns(createdWeek); - - // Act - var actual = await weekRepository.GetByIdAsync(0); - - // Assert - Assert.Equal(1, actual.Number); - } - - [Fact] - public async Task GetWeekById_WeekNotFound_ReturnsNewWeek() - { - // Arrange - var weekId = 99; - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - A.CallTo(() => context.GetItemByIdAsync(A._)).Returns((Week?)null!); - - // Act - var actual = await weekRepository.GetByIdAsync(weekId); - - // Assert - Assert.NotNull(actual); - Assert.Equal(0, actual.Id); // new Week() default - } - - [Fact] - public async Task GetAllWeeks_CallsContext() - { - // Arrange - var weeks = new List { new() { Id = 1 }, new() { Id = 2 } }; - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - A.CallTo(() => context.GetAllWithChildrenAsync(null)).Returns(weeks); - - // Act - var actual = await weekRepository.GetAllAsync(); - - // Assert - Assert.Equal(weeks, actual); - A.CallTo(() => context.GetAllWithChildrenAsync(null)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task CreateWeek_AddsWeekAndBowlers() - { - // Arrange - var people = new List - { - new() { Id = 10, Name = "A", IsSub = false }, - new() { Id = 11, Name = "B", IsSub = false } - }; - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - A.CallTo(() => context.AddItemAsync(A._)).Invokes((Week args) => args.Id = 5).Returns(true); - A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns(people); - A.CallTo(() => context.AddItemAsync(A._)).Returns(true); - - // Act - var actual = await weekRepository.CreateAsync(3); - - // Assert - Assert.Equal(3, actual.Number); - A.CallTo(() => context.AddItemAsync(A._)).MustHaveHappenedOnceExactly(); - A.CallTo(() => context.AddItemAsync(A._)).MustHaveHappened(2, Times.Exactly); - } - - [Fact] - public async Task CreateWeek_WithNoPeople_AddsWeekOnly() - { - // Arrange - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - A.CallTo(() => context.AddItemAsync(A._)).Returns(true); - A.CallTo(() => context.GetFilteredAsync(A>>._)).Returns([]); - - // Act - var actual = await weekRepository.CreateAsync(2); - - // Assert - Assert.Equal(2, actual.Number); - A.CallTo(() => context.AddItemAsync(A._)).MustHaveHappenedOnceExactly(); - A.CallTo(() => context.AddItemAsync(A._)).MustNotHaveHappened(); - } - - [Fact] - public async Task UpdateWeek_CallsContext() - { - // Arrange - var week = new Week { Id = 7 }; - var context = A.Fake(); - var weekRepository = new WeekRepository(context); - A.CallTo(() => context.UpdateWithChildrenAsync(A._)).Returns(Task.CompletedTask); - - // Act - await weekRepository.UpdateAsync(week); - - // Assert - A.CallTo(() => context.UpdateWithChildrenAsync(A._)).MustHaveHappenedOnceExactly(); - } -} diff --git a/HangTab.Tests/Services/BowlerServiceTests.cs b/HangTab.Tests/Services/BowlerServiceTests.cs index 2b49ad5..da50900 100644 --- a/HangTab.Tests/Services/BowlerServiceTests.cs +++ b/HangTab.Tests/Services/BowlerServiceTests.cs @@ -11,16 +11,15 @@ public async Task AddBowler_ValidBowler_ReturnsTrue() { // Arrange var bowler = new Bowler { Id = 1 }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddAsync(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.AddAsync(A._)).Returns(true); // Act var result = await service.AddAsync(bowler); // Assert - Assert.True(result); - A.CallTo(() => bowlerRepo.AddAsync(bowler)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsSuccess); } [Fact] @@ -28,16 +27,15 @@ public async Task AddBowler_RepositoryReturnsFalse_ReturnsFalse() { // Arrange var bowler = new Bowler { Id = 2 }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddAsync(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.AddAsync(A._)).Returns(false); // Act var result = await service.AddAsync(bowler); // Assert - Assert.False(result); - A.CallTo(() => bowlerRepo.AddAsync(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsFailed); } [Fact] @@ -51,32 +49,32 @@ public async Task GetAllBowlersByWeekId_ValidId_ReturnsBowlers() new() { Id = 2, WeekId = weeekId } }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); + A.CallTo(() => bowlerRepo.GetFilteredAsync(A>>._)).Returns(expected); // Act var result = await service.GetAllByWeekIdAsync(weeekId); // Assert - Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Count, actual.Count()); } [Fact] public async Task GetAllBowlers_RepositoryReturnsEmpty_ReturnsEmpty() { // Arrange - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllAsync()).Returns(Task.FromResult>([])); + A.CallTo(() => bowlerRepo.GetAllAsync()).Returns([]); // Act var result = await service.GetAllAsync(); // Assert - Assert.Empty(result); - A.CallTo(() => bowlerRepo.GetAllAsync()).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Empty(actual); } [Fact] @@ -85,16 +83,16 @@ public async Task GetBowlerById_ValidId_ReturnsBowler() // Arrange var id = 10; var expected = new Bowler { Id = id }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetByIdAsync(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => bowlerRepo.GetByIdAsync(A._)).Returns(expected); // Act var result = await service.GetByIdAsync(id); // Assert - Assert.Equal(expected.Id, result.Id); - A.CallTo(() => bowlerRepo.GetByIdAsync(A._)).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Id, actual.Id); } [Fact] @@ -103,17 +101,17 @@ public async Task GetBowlersByWeekId_ValidId_ReturnsBowlers() // Arrange var id = 7; var expected = new List { new() { Id = 3, WeekId = id } }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).Returns(Task.FromResult>(expected)); + A.CallTo(() => bowlerRepo.GetFilteredAsync(A>>._)).Returns(expected); // Act var result = await service.GetAllByWeekIdAsync(id); // Assert - Assert.Single(result); - Assert.Equal(expected[0].Id, result.First().Id); - A.CallTo(() => bowlerRepo.GetAllByWeekIdAsync(A._)).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Single(actual); + Assert.Equal(expected[0].Id, actual.First().Id); } [Fact] @@ -121,16 +119,15 @@ public async Task RemoveBowler_ValidId_ReturnsTrue() { // Arrange var id = 1; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveAsync(id)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.DeleteByIdAsync(A._)).Returns(true); // Act var result = await service.RemoveAsync(id); // Assert - Assert.True(result); - A.CallTo(() => bowlerRepo.RemoveAsync(id)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsSuccess); } [Fact] @@ -138,29 +135,31 @@ public async Task RemoveBowler_InvalidId_ReturnsFalse() { // Arrange var id = 999; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveAsync(id)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.DeleteByIdAsync(A._)).Returns(false); // Act var result = await service.RemoveAsync(id); // Assert - Assert.False(result); - A.CallTo(() => bowlerRepo.RemoveAsync(id)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsFailed); } [Fact] - public async Task RemoveBowler_RepositoryThrowsException_PropagatesException() + public async Task RemoveBowler_NegativeId_ReturnsFalse() { // Arrange - var id = 2; - var bowlerRepo = A.Fake(); + var id = -2; + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.RemoveAsync(id)).Throws(new InvalidOperationException("Repository error")); + A.CallTo(() => bowlerRepo.DeleteByIdAsync(A._)).Returns(false); - // Act & Assert - await Assert.ThrowsAsync(() => service.RemoveAsync(id)); + // Act + var result = await service.RemoveAsync(id); + + // Assert + Assert.True(result.IsFailed); } [Fact] @@ -168,16 +167,15 @@ public async Task UpdateBowler_RepositoryReturnsTrue_ReturnsTrue() { // Arrange var expected = new Bowler { Id = 4 }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(true); // Act var result = await service.UpdateAsync(expected); // Assert - Assert.True(result); - A.CallTo(() => bowlerRepo.UpdateAsync(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsSuccess); } [Fact] @@ -185,39 +183,30 @@ public async Task UpdateBowler_RepositoryReturnsFalse_ReturnsFalse() { // Arrange var expected = new Bowler { Id = 5 }; - var bowlerRepo = A.Fake(); + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(false); // Act var result = await service.UpdateAsync(expected); // Assert - Assert.False(result); - A.CallTo(() => bowlerRepo.UpdateAsync(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsFailed); } [Fact] - public async Task AddBowler_NullBowler_ThrowsArgumentNullException() + public async Task UpdateBowler_NegativeId_ReturnsFalse() { // Arrange - var bowlerRepo = A.Fake(); + var id = -2; + var bowlerRepo = A.Fake>(); var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.AddAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); + A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Returns(false); - // Act & Assert - await Assert.ThrowsAsync(() => service.AddAsync(null!)); - } - - [Fact] - public async Task UpdateBowler_NullBowler_ThrowsArgumentNullException() - { - // Arrange - var bowlerRepo = A.Fake(); - var service = new BowlerService(bowlerRepo); - A.CallTo(() => bowlerRepo.UpdateAsync(A._)).Throws(new ArgumentNullException(nameof(Bowler))); + // Act + var result = await service.RemoveAsync(id); - // Act & Assert - await Assert.ThrowsAsync(() => service.UpdateAsync(null!)); + // Assert + Assert.True(result.IsFailed); } } \ No newline at end of file diff --git a/HangTab.Tests/Services/PersonServiceTests.cs b/HangTab.Tests/Services/PersonServiceTests.cs index 32137bd..154f1aa 100644 --- a/HangTab.Tests/Services/PersonServiceTests.cs +++ b/HangTab.Tests/Services/PersonServiceTests.cs @@ -11,18 +11,18 @@ public async Task GetPersonById_ValidId_ReturnsPerson() { // Arrange var id = 42; - var expected = new Person { Id = id, Name = "Alice" }; - var personRepo = A.Fake(); + var expected = new Person { Id = id, Name = "Jayden" }; + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.GetPersonById(A._)).Returns(Task.FromResult(expected)); + A.CallTo(() => personRepo.GetByIdAsync(A._)).Returns(expected); // Act - var result = await service.GetPersonById(id); + var result = await service.GetByIdAsync(id); // Assert - Assert.Equal(expected.Id, result.Id); - Assert.Equal("Alice", result.Name); - A.CallTo(() => personRepo.GetPersonById(A._)).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Id, actual.Id); + Assert.Equal("Jayden", actual.Name); } [Fact] @@ -30,16 +30,16 @@ public async Task GetAllPeople_RepositoryReturnsPeople_ReturnsPeople() { // Arrange var expected = new List { new() { Id = 16 }, new() { Id = 17 } }; - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.GetAllPeople()).Returns(Task.FromResult>(expected)); + A.CallTo(() => personRepo.GetAllAsync()).Returns(expected); // Act - var result = await service.GetAllPeople(); + var result = await service.GetAllAsync(); // Assert - Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => personRepo.GetAllPeople()).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Count, actual.Count()); } [Fact] @@ -47,17 +47,17 @@ public async Task GetRegulars_RepositoryReturnsRegulars_ReturnsRegulars() { // Arrange var expected = new List { new() { Id = 42, IsSub = false } }; - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.GetRegulars()).Returns(Task.FromResult>(expected)); + A.CallTo(() => personRepo.GetFilteredAsync(A>>._)).Returns(expected); // Act - var result = await service.GetRegulars(); + var result = await service.GetRegularsAsync(); // Assert - Assert.Single(result); - Assert.False(result.First().IsSub); - A.CallTo(() => personRepo.GetRegulars()).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Single(actual); + Assert.False(actual.First().IsSub); } [Fact] @@ -65,140 +65,140 @@ public async Task GetSubstitutes_RepositoryReturnsSubs_ReturnsSubs() { // Arrange var expected = new List { new() { Id = 42, IsSub = true } }; - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.GetSubstitutes()).Returns(Task.FromResult>(expected)); + A.CallTo(() => personRepo.GetFilteredAsync(A>>._)).Returns(expected); // Act - var result = await service.GetSubstitutes(); + var result = await service.GetSubstitutesAsync(); // Assert - Assert.Single(result); - Assert.True(result.First().IsSub); - A.CallTo(() => personRepo.GetSubstitutes()).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Single(actual); + Assert.True(actual.First().IsSub); } [Fact] public async Task AddPerson_ValidPerson_ReturnsTrue() { // Arrange - var expected = new Person { Id = 67, Name = "Bob" }; - var personRepo = A.Fake(); + var expected = new Person { Id = 67, Name = "Squeakers" }; + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.AddPerson(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => personRepo.AddAsync(A._)).Returns(true); // Act - var result = await service.AddPerson(expected); + var result = await service.AddAsync(expected); // Assert - Assert.True(result); - A.CallTo(() => personRepo.AddPerson(expected)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsSuccess); } [Fact] public async Task AddPerson_RepositoryReturnsFalse_ReturnsFalse() { // Arrange - var expected = new Person { Id = 4, Name = "Carol" }; - var personRepo = A.Fake(); + var expected = new Person { Id = 4, Name = "Mr. Big Hands" }; + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.AddPerson(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => personRepo.AddAsync(A._)).Returns(Task.FromResult(false)); // Act - var result = await service.AddPerson(expected); + var result = await service.AddAsync(expected); // Assert - Assert.False(result); - A.CallTo(() => personRepo.AddPerson(expected)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsFailed); } [Fact] public async Task DeletePerson_ValidId_ReturnsTrue() { // Arrange - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.DeletePerson(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => personRepo.DeleteByIdAsync(A._)).Returns(true); // Act - var result = await service.DeletePerson(5); + var result = await service.DeleteAsync(42); // Assert - Assert.True(result); - A.CallTo(() => personRepo.DeletePerson(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsSuccess); } [Fact] public async Task DeletePerson_RepositoryReturnsFalse_ReturnsFalse() { // Arrange - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.DeletePerson(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => personRepo.DeleteByIdAsync(A._)).Returns(false); // Act - var result = await service.DeletePerson(6); + var result = await service.DeleteAsync(42); // Assert - Assert.False(result); - A.CallTo(() => personRepo.DeletePerson(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsFailed); } [Fact] public async Task UpdatePerson_ValidPerson_ReturnsTrue() { // Arrange - var expected = new Person { Id = 7, Name = "Stimpy" }; - var personRepo = A.Fake(); + var expected = new Person { Id = 7, Name = "Mr. Grabby Hands" }; + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.UpdatePerson(A._)).Returns(Task.FromResult(true)); + A.CallTo(() => personRepo.UpdateAsync(A._)).Returns(true); // Act - var result = await service.UpdatePerson(expected); + var result = await service.UpdateAsync(expected); // Assert - Assert.True(result); - A.CallTo(() => personRepo.UpdatePerson(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsSuccess); } [Fact] public async Task UpdatePerson_RepositoryReturnsFalse_ReturnsFalse() { // Arrange - var expected = new Person { Id = 8, Name = "Frank" }; - var personRepo = A.Fake(); + var expected = new Person { Id = 8, Name = "Nolan" }; + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.UpdatePerson(A._)).Returns(Task.FromResult(false)); + A.CallTo(() => personRepo.UpdateAsync(A._)).Returns(false); // Act - var result = await service.UpdatePerson(expected); + var result = await service.UpdateAsync(expected); // Assert - Assert.False(result); - A.CallTo(() => personRepo.UpdatePerson(A._)).MustHaveHappenedOnceExactly(); + Assert.True(result.IsFailed); } [Fact] - public async Task AddPerson_NullPerson_ThrowsArgumentNullException() + public async Task AddPerson_NullPerson_ReturnsFailedResult() { // Arrange - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.AddPerson(A._)).Throws(new ArgumentNullException(nameof(Person))); - // Act & Assert - await Assert.ThrowsAsync(() => service.AddPerson(null!)); + // Act + var result = await service.AddAsync(null!); + + // Assert + Assert.True(result.IsFailed); + Assert.Equal("Person cannot be null.", result.Errors[0].Message); } [Fact] - public async Task UpdatePerson_NullPerson_ThrowsArgumentNullException() + public async Task UpdatePerson_NullPerson_ReturnsFailedResult() { // Arrange - var personRepo = A.Fake(); + var personRepo = A.Fake>(); var service = new PersonService(personRepo); - A.CallTo(() => personRepo.UpdatePerson(A._)).Throws(new ArgumentNullException(nameof(Person))); - // Act & Assert - await Assert.ThrowsAsync(() => service.UpdatePerson(null!)); + // Act + var result = await service.UpdateAsync(null!); + + // Assert + Assert.True(result.IsFailed); + Assert.Equal("Person cannot be null.", result.Errors[0].Message); } } \ No newline at end of file diff --git a/HangTab.Tests/Services/WeekServiceTests.cs b/HangTab.Tests/Services/WeekServiceTests.cs index ff11d45..4af4f77 100644 --- a/HangTab.Tests/Services/WeekServiceTests.cs +++ b/HangTab.Tests/Services/WeekServiceTests.cs @@ -11,17 +11,19 @@ public async Task GetWeekById_ValidId_ReturnsWeek() { // Arrange var expected = new Week { Id = 1, Number = 7 }; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetByIdAsync(A._)).Returns(Task.FromResult(expected)); + var weekRepo = A.Fake>(); + var personRepo = A.Fake>(); + var bowlerRepo = A.Fake>(); + var service = new WeekService(weekRepo, personRepo, bowlerRepo); + A.CallTo(() => weekRepo.GetByIdAsync(A._)).Returns(expected); // Act var result = await service.GetByIdAsync(1); // Assert - Assert.Equal(expected.Id, result.Id); - Assert.Equal(expected.Number, result.Number); - A.CallTo(() => weekRepo.GetByIdAsync(1)).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.Number, actual.Number); } [Fact] @@ -29,16 +31,18 @@ public async Task GetAllWeeks_RepositoryReturnsWeeks_ReturnsWeeks() { // Arrange var expected = new List { new() { Id = 1 }, new() { Id = 2 } }; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetAllAsync()).Returns(Task.FromResult>(expected)); + var weekRepo = A.Fake>(); + var personRepo = A.Fake>(); + var bowlerRepo = A.Fake>(); + var service = new WeekService(weekRepo, personRepo, bowlerRepo); + A.CallTo(() => weekRepo.GetAllAsync()).Returns(expected); // Act var result = await service.GetAllAsync(); // Assert - Assert.Equal(expected.Count, result.Count()); - A.CallTo(() => weekRepo.GetAllAsync()).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Count, actual.Count()); } [Fact] @@ -46,17 +50,19 @@ public async Task CreateWeek_ValidNumber_ReturnsWeek() { // Arrange var expected = new Week { Id = 3, Number = 4 }; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.CreateAsync(A._)).Returns(Task.FromResult(expected)); + var weekRepo = A.Fake>(); + var personRepo = A.Fake>(); + var bowlerRepo = A.Fake>(); + var service = new WeekService(weekRepo, personRepo, bowlerRepo); + A.CallTo(() => weekRepo.AddAsync(A._)).Returns(true); // Act - var result = await service.CreateAsync(4); + var result = await service.AddAsync(4); // Assert - Assert.Equal(expected.Id, result.Id); - Assert.Equal(expected.Number, result.Number); - A.CallTo(() => weekRepo.CreateAsync(4)).MustHaveHappenedOnceExactly(); + var actual = result.Value; + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.Number, actual.Number); } [Fact] @@ -64,53 +70,16 @@ public async Task UpdateWeek_ValidWeek_CallsRepository() { // Arrange var week = new Week { Id = 5, Number = 6 }; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.UpdateAsync(A._)).Returns(Task.CompletedTask); + var weekRepo = A.Fake>(); + var personRepo = A.Fake>(); + var bowlerRepo = A.Fake>(); + var service = new WeekService(weekRepo, personRepo, bowlerRepo); + A.CallTo(() => weekRepo.UpdateAsync(A._)).Returns(true); // Act - await service.UpdateAsync(week); + var result = await service.UpdateAsync(week); // Assert - A.CallTo(() => weekRepo.UpdateAsync(week)).MustHaveHappenedOnceExactly(); - } - - [Fact] - public async Task GetWeekById_RepositoryThrows_PropagatesException() - { - // Arrange - var id = 99; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.GetByIdAsync(A._)).Throws(new InvalidOperationException("Not found")); - - // Act & Assert - await Assert.ThrowsAsync(() => service.GetByIdAsync(id)); - } - - [Fact] - public async Task CreateWeek_RepositoryThrows_PropagatesException() - { - // Arrange - var id = 42; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.CreateAsync(A._)).Throws(new Exception("Create failed")); - - // Act & Assert - await Assert.ThrowsAsync(() => service.CreateAsync(id)); - } - - [Fact] - public async Task UpdateWeek_RepositoryThrows_PropagatesException() - { - // Arrange - var week = new Week { Id = 7, Number = 8 }; - var weekRepo = A.Fake(); - var service = new WeekService(weekRepo); - A.CallTo(() => weekRepo.UpdateAsync(A._)).Throws(new Exception("Update failed")); - - // Act & Assert - await Assert.ThrowsAsync(() => service.UpdateAsync(week)); + Assert.True(result.IsSuccess); } } From 823f70415962cbe2746a02a1db368a847c047961 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Thu, 13 Nov 2025 07:26:00 -0500 Subject: [PATCH 11/12] cleanup --- HangTab/Models/Week.cs | 2 +- HangTab/Repositories/Impl/BaseRepository.cs | 6 +- HangTab/Services/Impl/WeekService.cs | 2 +- .../CurrentWeekOverviewViewModel.cs | 62 +++++++++---------- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/HangTab/Models/Week.cs b/HangTab/Models/Week.cs index b46cb7e..065e82c 100644 --- a/HangTab/Models/Week.cs +++ b/HangTab/Models/Week.cs @@ -12,5 +12,5 @@ public class Week public int BusRides { get; set; } [OneToMany(CascadeOperations = CascadeOperation.All)] - public List Bowlers { get; set; } = []; + public IList Bowlers { get; set; } = []; } diff --git a/HangTab/Repositories/Impl/BaseRepository.cs b/HangTab/Repositories/Impl/BaseRepository.cs index 046cd39..8b43692 100644 --- a/HangTab/Repositories/Impl/BaseRepository.cs +++ b/HangTab/Repositories/Impl/BaseRepository.cs @@ -1,5 +1,7 @@ using HangTab.Data; +using System.Linq.Expressions; + namespace HangTab.Repositories.Impl; public partial class BaseRepository(IDatabaseContext context) : IBaseRepository where T : class, new() { @@ -17,13 +19,15 @@ public Task DeleteByIdAsync(int id) public Task> GetAllAsync() => context.GetAllWithChildrenAsync(); + public Task> GetAllFilteredAsync(Expression> predicate) => context.GetAllWithChildrenAsync(predicate); + public Task GetByIdAsync(int id) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(id); return context.GetItemByIdAsync(id); } - public Task> GetFilteredAsync(System.Linq.Expressions.Expression> predicate) + public Task> GetFilteredAsync(Expression> predicate) { ArgumentNullException.ThrowIfNull(predicate); return context.GetFilteredAsync(predicate); diff --git a/HangTab/Services/Impl/WeekService.cs b/HangTab/Services/Impl/WeekService.cs index 7d59e82..57b0915 100644 --- a/HangTab/Services/Impl/WeekService.cs +++ b/HangTab/Services/Impl/WeekService.cs @@ -67,7 +67,7 @@ public async Task> GetByIdAsync(int id) WeekId = bowler.WeekId, PersonId = bowler.PersonId, SubId = bowler.SubId, - Person = await personRepo.GetByIdAsync(bowler.SubId is not null ? (int)bowler.SubId : bowler.PersonId) + Person = await personRepo.GetByIdAsync(bowler.SubId is not null ? bowler.SubId.Value : bowler.PersonId) }); } diff --git a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs index 80e7cb1..1fd1916 100644 --- a/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs +++ b/HangTab/ViewModels/CurrentWeekOverviewViewModel.cs @@ -171,6 +171,36 @@ private async Task SubmitWeekAsync() } } + [RelayCommand] + private async Task BowlerHangCountChangedAsync(CurrentWeekListItemViewModel? vm) + { + if (vm is null) + { + return; + } + + var result = await _bowlerService.UpdateAsync(vm.ToBowler()); + if (result.IsFailed) + { + await _dialogService.AlertAsync("Error", "Unable to update bowler hang count", "Ok"); + return; + } + + var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); + var isIncrease = newHangTotal > TeamHangTotal; + TeamHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); + CurrentWeekBowlers.SetLowestBowlerHangCount(); + + if (isIncrease) + { + PlayPopperAnimation = true; + await Task.Delay(1000); + PlayPopperAnimation = false; + } + + _messenger.Send(new BowlerHangCountChangedMessage(vm.BowlerId, vm.HangCount)); + } + private async Task StartNewWeekAsync() { var result = await _weekService.AddAsync(CurrentWeek.Number + 1); @@ -200,7 +230,7 @@ private async Task GetCurrentWeekAsync() if (CurrentWeek.Bowlers.Count > 0) { CurrentWeekBowlers.Clear(); - CurrentWeekBowlers = CurrentWeek.Bowlers.ToCurrentWeekListItemViewModelList().ToObservableCollection(); + CurrentWeekBowlers = CurrentWeek.Bowlers.OrderBy(b => b.Person.Name).ToCurrentWeekListItemViewModelList().ToObservableCollection(); CurrentWeekBowlers.SetLowestBowlerHangCount(); } @@ -248,36 +278,6 @@ private async Task SetBowlerStatusAsync(CurrentWeekListItemViewModel? vm, Enums. await GetCurrentWeekAsync(); } - [RelayCommand] - private async Task BowlerHangCountChangedAsync(CurrentWeekListItemViewModel? vm) - { - if (vm is null) - { - return; - } - - var result = await _bowlerService.UpdateAsync(vm.ToBowler()); - if (result.IsFailed) - { - await _dialogService.AlertAsync("Error", "Unable to update bowler hang count", "Ok"); - return; - } - - var newHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); - var isIncrease = newHangTotal > TeamHangTotal; - TeamHangTotal = CurrentWeekBowlers.Sum(b => b.HangCount); - CurrentWeekBowlers.SetLowestBowlerHangCount(); - - if (isIncrease) - { - PlayPopperAnimation = true; - await Task.Delay(1000); - PlayPopperAnimation = false; - } - - _messenger.Send(new BowlerHangCountChangedMessage(vm.BowlerId, vm.HangCount)); - } - public async void Receive(SystemResetMessage message) { CurrentWeekBowlers.Clear(); From 272064e749cd9bd124ba77f12add5df0067d2f91 Mon Sep 17 00:00:00 2001 From: jdubar <55170139+jdubar@users.noreply.github.com> Date: Thu, 13 Nov 2025 07:26:46 -0500 Subject: [PATCH 12/12] removed delete functionality from person edit page --- HangTab/ViewModels/PersonAddEditViewModel.cs | 32 -------------------- HangTab/Views/PersonAddEditPage.xaml | 14 +-------- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/HangTab/ViewModels/PersonAddEditViewModel.cs b/HangTab/ViewModels/PersonAddEditViewModel.cs index e7bb804..5ca2cd2 100644 --- a/HangTab/ViewModels/PersonAddEditViewModel.cs +++ b/HangTab/ViewModels/PersonAddEditViewModel.cs @@ -78,9 +78,6 @@ public PersonAddEditViewModel( [ObservableProperty] private bool _isSub; - [ObservableProperty] - private bool _isDeleted; - [ObservableProperty] private ObservableCollection _errors = []; @@ -112,33 +109,6 @@ await Loading( }); } - [RelayCommand] - private async Task DeletePersonAsync() - { - if (_person is null) - { - await _dialogService.AlertAsync("Error", "No bowler selected to delete.", "Ok"); - return; - } - - if (!await _dialogService.Ask("Delete", "Are you sure you want to delete this bowler?")) - { - return; - } - - var result = await _personService.DeleteAsync(Id); - if (result.IsSuccess) - { - _messenger.Send(new PersonDeletedMessage(_person.Id)); - } - else - { - await _dialogService.AlertAsync("Critical Error", "Error occurred while deleting the bowler!", "Ok"); - } - - await _navigationService.GoBack(); - } - [RelayCommand] private async Task ShowAvatarOptionsBottomSheetAsync(ITextInput view, CancellationToken token) { @@ -208,7 +178,6 @@ private Person MapDataToPerson() Name = Name, ImageUrl = ImageUrl, IsSub = BowlerTypeIndex == (int)BowlerType.Sub, - IsDeleted = IsDeleted }; } @@ -220,7 +189,6 @@ private void MapPerson(Person? model) Name = model.Name; ImageUrl = model.ImageUrl; IsSub = model.IsSub; - IsDeleted = model.IsDeleted; Initials = model.Id > 0 ? model.Name.GetInitials() : string.Empty; BowlerTypeIndex = model.IsSub diff --git a/HangTab/Views/PersonAddEditPage.xaml b/HangTab/Views/PersonAddEditPage.xaml index b673062..f18e3d0 100644 --- a/HangTab/Views/PersonAddEditPage.xaml +++ b/HangTab/Views/PersonAddEditPage.xaml @@ -131,19 +131,7 @@ - - - -