diff --git a/PhoneAssistant.Model.Tests/Repositories/PhonesRepositoryTests.cs b/PhoneAssistant.Model.Tests/Repositories/PhonesRepositoryTests.cs index 350a4ed..83b08da 100644 --- a/PhoneAssistant.Model.Tests/Repositories/PhonesRepositoryTests.cs +++ b/PhoneAssistant.Model.Tests/Repositories/PhonesRepositoryTests.cs @@ -113,28 +113,6 @@ public async Task Exists_ShouldReturnTrue_WhenPhoneDoesExistAsync() await Assert.That(actual).IsTrue(); } - [Test] - public async Task GetPhonebyUser_should_only_return_lastest_In_Stock_phone() - { - Phone[] phones = [ - new Phone() { Imei = "1" , AssetTag = "Tag A1", Model = "", Condition = "N", OEM = Manufacturer.Nokia, Status = "In Repair", NewUser = "new user"}, - new Phone() { Imei = "2" , AssetTag = "Tag B2", Model = "", Condition = "N", OEM = Manufacturer.Samsung, Status = "Production", NewUser = "new user"}, - new Phone() { Imei = "3" , AssetTag = "Tag C3", Model = "", Condition = "N", OEM = Manufacturer.Nokia, Status = "Production", NewUser = "new user"}, - new Phone() { Imei = "4" , AssetTag = "Tag D4", Model = "", Condition = "N", OEM = Manufacturer.Other, Status = "Decommissioned", NewUser = "new user"}, - new Phone() { Imei = "5" , AssetTag = "Tag E5", Model = "", Condition = "N", OEM = Manufacturer.Apple, Status = "Disposed", NewUser = "new user"} - ]; - for (int i = 0; i < phones.Length; i++) - { - await _repository.CreateAsync(phones[i]); - await Task.Delay(1100); // Ensure LastUpdate will be different on updated record - } - - var actual = await _repository.GetPhonebyUser("new user"); - - await Assert.That(actual).IsNotNull(); - await Assert.That(actual.Imei).IsEqualTo("3"); - } - [Test] public async Task UpdateAsync_WithNullPhone_ThrowsException() { @@ -298,4 +276,56 @@ public async Task UpdateStatusAsync_WithStatusChange_Succeeds() await Assert.That(actual).IsNotNull(); await Assert.That(actual!.Status).IsEqualTo(ApplicationConstants.StatusDisposed); } + + [Test] + public async Task UserHasProductionPhone_should_return_false_when_no_Production_phone_found() + { + Phone[] phones = [ + new Phone() { Imei = "1" , AssetTag = "Tag A1", Model = "", Condition = "N", OEM = Manufacturer.Nokia, Status = "In Repair", NewUser = "new user"}, + new Phone() { Imei = "2" , AssetTag = "Tag B2", Model = "", Condition = "N", OEM = Manufacturer.Samsung, Status = "Decommissioned", NewUser = "new user"}, + new Phone() { Imei = "3" , AssetTag = "Tag C3", Model = "", Condition = "N", OEM = Manufacturer.Nokia, Status = "Disposed", NewUser = "new user"}, + new Phone() { Imei = "4" , AssetTag = "Tag D4", Model = "", Condition = "N", OEM = Manufacturer.Other, Status = "Decommissioned", NewUser = "new user"}, + new Phone() { Imei = "5" , AssetTag = "Tag E5", Model = "", Condition = "N", OEM = Manufacturer.Apple, Status = "Disposed", NewUser = "new user"} + ]; + _helper.DbContext.Phones.AddRange(phones); + _helper.DbContext.SaveChanges(); + + bool actual = await _repository.UserHasProductionPhone("new user"); + + await Assert.That(actual).IsFalse(); + } + + [Test] + public async Task UserHasProductionPhone_should_return_true_when_Production_phone_found() + { + Phone[] phones = [ + new Phone() { Imei = "1" , AssetTag = "Tag A1", Model = "", Condition = "N", OEM = Manufacturer.Nokia, Status = "In Repair", NewUser = "new user"}, + new Phone() { Imei = "2" , AssetTag = "Tag B2", Model = "", Condition = "N", OEM = Manufacturer.Samsung, Status = "Production", NewUser = "new user"}, + new Phone() { Imei = "3" , AssetTag = "Tag C3", Model = "", Condition = "N", OEM = Manufacturer.Nokia, Status = "Production", NewUser = "new user"}, + new Phone() { Imei = "4" , AssetTag = "Tag D4", Model = "", Condition = "N", OEM = Manufacturer.Other, Status = "Decommissioned", NewUser = "new user"}, + new Phone() { Imei = "5" , AssetTag = "Tag E5", Model = "", Condition = "N", OEM = Manufacturer.Apple, Status = "Disposed", NewUser = "new user"} + ]; + _helper.DbContext.Phones.AddRange(phones); + _helper.DbContext.SaveChanges(); + + bool actual = await _repository.UserHasProductionPhone("new user"); + + await Assert.That(actual).IsTrue(); + } + + [Test] + [Arguments("new user")] + [Arguments("New user")] + [Arguments("New User")] + [Arguments("NEW USER")] + public async Task UserHasProductionPhone_is_case_insensitive(string newUser) + { + Phone phone = new() { Imei = "1", AssetTag = "Tag A1", Model = "", Condition = "N", OEM = Manufacturer.Samsung, Status = "Production", NewUser = "NEW USER" }; + await _helper.DbContext.Phones.AddAsync(phone); + _helper.DbContext.SaveChanges(); + + bool actual = await _repository.UserHasProductionPhone(newUser); + + await Assert.That(actual).IsTrue(); + } } diff --git a/PhoneAssistant.Model/Repositories/IPhonesRepository.cs b/PhoneAssistant.Model/Repositories/IPhonesRepository.cs index 8b75d47..51e414b 100644 --- a/PhoneAssistant.Model/Repositories/IPhonesRepository.cs +++ b/PhoneAssistant.Model/Repositories/IPhonesRepository.cs @@ -9,6 +9,7 @@ public interface IPhonesRepository Task> GetActivePhonesAsync(); Task> GetAllPhonesAsync(); Task GetPhoneAsync(string imei); + Task UserHasProductionPhone(string user); Task PhoneNumberExistsAsync(string phoneNumber); Task UpdateAsync(Phone phone); Task UpdateStatusAsync(string imei, string status); diff --git a/PhoneAssistant.Model/Repositories/PhonesRepository.cs b/PhoneAssistant.Model/Repositories/PhonesRepository.cs index 11af180..0e8327c 100644 --- a/PhoneAssistant.Model/Repositories/PhonesRepository.cs +++ b/PhoneAssistant.Model/Repositories/PhonesRepository.cs @@ -58,12 +58,9 @@ public async Task> GetAllPhonesAsync() return await dbContext.Phones.FindAsync(imei); } - public async Task GetPhonebyUser(string user) + public async Task UserHasProductionPhone(string user) { - return await dbContext.Phones.Where(p => p.NewUser == user) - .Where(p => p.Status == "Production") - .OrderByDescending(p => p.LastUpdate) - .FirstOrDefaultAsync(); + return await dbContext.Phones.AnyAsync(p => EF.Functions.Collate(p.NewUser, "NOCASE") == user && p.Status == "Production"); } public async Task PhoneNumberExistsAsync(string phoneNumber) diff --git a/PhoneAssistant.Tests/Features/Phones/PhonesItemViewModelTests.cs b/PhoneAssistant.Tests/Features/Phones/PhonesItemViewModelTests.cs index c25e5a1..54ac6f4 100644 --- a/PhoneAssistant.Tests/Features/Phones/PhonesItemViewModelTests.cs +++ b/PhoneAssistant.Tests/Features/Phones/PhonesItemViewModelTests.cs @@ -1,8 +1,9 @@ -using Moq; +using CommunityToolkit.Mvvm.Messaging; +using Moq; using Moq.AutoMock; - using PhoneAssistant.Model; using PhoneAssistant.WPF.Features.Phones; +using PhoneAssistant.Tests.Shared; namespace PhoneAssistant.Tests.Features.Phones; @@ -27,7 +28,7 @@ public sealed class PhonesItemViewModelTests SerialNumber = "sn", }; - private readonly AutoMocker _mocker = new AutoMocker(); + private readonly AutoMocker _mocker = new(); private readonly PhonesItemViewModel _vm; private readonly Mock _repository; @@ -41,38 +42,22 @@ public PhonesItemViewModelTests() } [Test] - [Arguments("phone number", "sim number", "In Stock")] - [Arguments(null, "sim number", "In Stock")] - [Arguments("phone number", null, "In Stock")] - [Arguments(null, null, "In Stock")] - [Arguments("phone number", "sim number", "Production")] - [Arguments(null, "sim number", "Production")] - [Arguments("phone number", null, "Production")] - [Arguments(null, null, "Production")] - public async Task PhonePropertySet_SetsBoundProperties(string? phoneNumber, string? simNumber, string status) + [Arguments("In Stock", null, null, false)] + [Arguments("In Repair", null, null, false)] + [Arguments("Production", null, null, false)] + [Arguments("Production", 123, null, false)] + [Arguments("Production", null, "new user", false)] + [Arguments("Production", 123, "new user", true)] + public async Task CreateEmailCommand_CanExecuteAsync(string status, int? sr, string? newUser, bool canExecute) { - _phone.PhoneNumber = phoneNumber; - _phone.SimNumber = simNumber; _phone.Status = status; + _phone.Ticket = sr; + _phone.NewUser = newUser; var vm = _mocker.CreateInstance(); - await Assert.That(vm.AssetTag).IsEqualTo(_phone.AssetTag); - await Assert.That(vm.Esim).IsEqualTo(_phone.Esim ?? false); - await Assert.That(vm.FormerUser).IsEqualTo(_phone.FormerUser); - await Assert.That(vm.Imei).IsEqualTo(_phone.Imei); - await Assert.That(vm.Model).IsEqualTo(_phone.Model); - await Assert.That(vm.NewUser).IsEqualTo(_phone.NewUser); - await Assert.That(vm.NorR).IsEqualTo(_phone.Condition); - await Assert.That(vm.Notes).IsEqualTo(_phone.Notes); - await Assert.That(vm.OEM).IsEqualTo(_phone.OEM); - await Assert.That(vm.PhoneNumber).IsEqualTo(_phone.PhoneNumber ?? string.Empty); - await Assert.That(vm.SerialNumber).IsEqualTo(_phone.SerialNumber ?? string.Empty); - await Assert.That(vm.SimNumber).IsEqualTo(_phone.SimNumber ?? string.Empty); - await Assert.That(vm.SR).IsEqualTo(_phone.Ticket.ToString()); - await Assert.That(vm.Status).IsEqualTo(_phone.Status); + await Assert.That(vm.CreateEmailCommand.CanExecute(null)).IsEqualTo(canExecute); } - #region Update [Test] public async Task OnAssetTagChanged_CallsUpdateAsync_WithChangedValueAsync() { @@ -125,9 +110,31 @@ public async Task OnNewUserChanged_CallsUpdateAsync_WithChangedValueAsync(string _repository.Verify(r => r.UpdateAsync(_phone), Times.Once); await Assert.That(_phone.NewUser).IsEqualTo(expected); await Assert.That(_vm.LastUpdate).IsEqualTo(_phone.LastUpdate); + } + + [Test] + public async Task OnNewUserChanged_should_not_send_ProductionPhoneWarning_when_NewUser_has_no_Production_phone() + { + _repository.Setup(r => r.UserHasProductionPhone("phoneuser")).ReturnsAsync(false); + var messenger = _mocker.GetMock(); + + _vm.NewUser = "phoneuser"; + + messenger.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Never); + } + + [Test] + public async Task OnNewUserChanged_should_send_ProductionPhoneWarning_when_NewUser_has_no_Production_phone() + { + _repository.Setup(r => r.UserHasProductionPhone("phoneuser")).ReturnsAsync(true); + var messenger = _mocker.GetMock(); + + _vm.NewUser = "phoneuser"; + messenger.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); } + [Test] public async Task OnNorRChanged_CallsUpdateAsync_WithChangedValueAsync() { @@ -298,9 +305,39 @@ public async Task OnStatusChanged_ShouldSetTicketToDefault_WhenDecommissionedAsy await Assert.That(_vm.SR).IsEqualTo("987654"); } - #endregion + + [Test] + [Arguments("phone number", "sim number", "In Stock")] + [Arguments(null, "sim number", "In Stock")] + [Arguments("phone number", null, "In Stock")] + [Arguments(null, null, "In Stock")] + [Arguments("phone number", "sim number", "Production")] + [Arguments(null, "sim number", "Production")] + [Arguments("phone number", null, "Production")] + [Arguments(null, null, "Production")] + public async Task PhonePropertySet_SetsBoundProperties(string? phoneNumber, string? simNumber, string status) + { + _phone.PhoneNumber = phoneNumber; + _phone.SimNumber = simNumber; + _phone.Status = status; + var vm = _mocker.CreateInstance(); + + await Assert.That(vm.AssetTag).IsEqualTo(_phone.AssetTag); + await Assert.That(vm.Esim).IsEqualTo(_phone.Esim ?? false); + await Assert.That(vm.FormerUser).IsEqualTo(_phone.FormerUser); + await Assert.That(vm.Imei).IsEqualTo(_phone.Imei); + await Assert.That(vm.Model).IsEqualTo(_phone.Model); + await Assert.That(vm.NewUser).IsEqualTo(_phone.NewUser); + await Assert.That(vm.NorR).IsEqualTo(_phone.Condition); + await Assert.That(vm.Notes).IsEqualTo(_phone.Notes); + await Assert.That(vm.OEM).IsEqualTo(_phone.OEM); + await Assert.That(vm.PhoneNumber).IsEqualTo(_phone.PhoneNumber ?? string.Empty); + await Assert.That(vm.SerialNumber).IsEqualTo(_phone.SerialNumber ?? string.Empty); + await Assert.That(vm.SimNumber).IsEqualTo(_phone.SimNumber ?? string.Empty); + await Assert.That(vm.SR).IsEqualTo(_phone.Ticket.ToString()); + await Assert.That(vm.Status).IsEqualTo(_phone.Status); + } - #region RemoveSim [Test] public async Task RemoveSim_SetsBoundPropertiesAsync() { @@ -334,22 +371,4 @@ public async Task RemoveSimCommand_SetsCanExecute_FalseAsync() await Assert.That(_vm.RemoveSimCommand.CanExecute(null)).IsFalse(); } - #endregion - - [Test] - [Arguments("In Stock", null, null, false)] - [Arguments("In Repair", null, null, false)] - [Arguments("Production", null, null, false)] - [Arguments("Production", 123, null, false)] - [Arguments("Production", null, "new user", false)] - [Arguments("Production", 123, "new user", true)] - public async Task CreateEmailCommand_CanExecuteAsync(string status, int? sr, string? newUser, bool canExecute) - { - _phone.Status = status; - _phone.Ticket = sr; - _phone.NewUser = newUser; - var vm = _mocker.CreateInstance(); - - await Assert.That(vm.CreateEmailCommand.CanExecute(null)).IsEqualTo(canExecute); - } } diff --git a/PhoneAssistant.Tests/Features/Phones/PhonesMainViewModelTests.cs b/PhoneAssistant.Tests/Features/Phones/PhonesMainViewModelTests.cs index 9bf4f72..928ed71 100644 --- a/PhoneAssistant.Tests/Features/Phones/PhonesMainViewModelTests.cs +++ b/PhoneAssistant.Tests/Features/Phones/PhonesMainViewModelTests.cs @@ -17,11 +17,11 @@ public sealed class PhonesMainViewModelTests [Test] public async Task ChangingFilterAssetTag_ChangesFilterViewAsync() { - List phones = new List() { + List phones = [ new Phone() { Imei = "1" , AssetTag = "Tag A1", Model = "", Condition = "", OEM = Manufacturer.Apple, Status = ""}, new Phone() { Imei = "2" , AssetTag = "Tag Bb2", Model = "", Condition = "", OEM = Manufacturer.Apple, Status = ""}, new Phone() {Imei = "3", AssetTag = "Tag Ccc3", Model = "", Condition = "", OEM = Manufacturer.Apple, Status = ""} - }; + ]; PhonesMainViewModel vm = ViewModelMockSetup(phones); await vm.LoadAsync(); ICollectionView view = CollectionViewSource.GetDefaultView(vm.PhoneItems); @@ -328,7 +328,7 @@ public async Task LoadAsync_should_call_GetAllPhones_when_IncludeDisposals_true( } [Test] - public async Task Receive_ShouldAddPhoneAsync() + public async Task Receive_Phone_should_add_phone_to_PhoneItems() { AutoMocker mocker = new(); PhonesMainViewModel vm = mocker.CreateInstance(); @@ -338,6 +338,17 @@ public async Task Receive_ShouldAddPhoneAsync() await Assert.That(vm.PhoneItems.Any()).IsTrue(); } + [Test] + public async Task Receive_ProductionPhoneWarning_should_make_warning_visible() + { + AutoMocker mocker = new(); + PhonesMainViewModel vm = mocker.CreateInstance(); + + vm.Receive(new ProductionPhoneWarning("message")); + + await Assert.That(vm.ProductionPhoneWarning).IsEqualTo(Visibility.Visible); + } + [Test] public async Task SelectedPhone_should_leave_ConcurrentUpdateWarning_as_collapsed_when_ConcurrentChange_false() { diff --git a/PhoneAssistant.WPF/Features/Phones/PhonesItemViewModel.cs b/PhoneAssistant.WPF/Features/Phones/PhonesItemViewModel.cs index 83bb389..65ca256 100644 --- a/PhoneAssistant.WPF/Features/Phones/PhonesItemViewModel.cs +++ b/PhoneAssistant.WPF/Features/Phones/PhonesItemViewModel.cs @@ -1,6 +1,9 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using System.Windows; + +using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; + using PhoneAssistant.Model; using PhoneAssistant.WPF.Shared; @@ -121,17 +124,16 @@ async partial void OnNewUserChanged(string value) if (string.IsNullOrEmpty(value)) { if (_phone.NewUser is null) - { return; - } else - { _phone.NewUser = null; - } } else { _phone.NewUser = value; + + if (await _repository.UserHasProductionPhone(value)) + _messenger.Send(new ProductionPhoneWarning("User has phone in production")); } await UpdatePhone(); @@ -291,6 +293,9 @@ async partial void OnStatusChanged(string value) _phone.Status = value; + //if (await _repository.UserHasProductionPhone(value)) + // _messenger.Send(new ProductionPhoneWarning("User has phone in production")); + await UpdatePhone(); } diff --git a/PhoneAssistant.WPF/Features/Phones/PhonesMainView.xaml b/PhoneAssistant.WPF/Features/Phones/PhonesMainView.xaml index 824606a..135deb3 100644 --- a/PhoneAssistant.WPF/Features/Phones/PhonesMainView.xaml +++ b/PhoneAssistant.WPF/Features/Phones/PhonesMainView.xaml @@ -26,14 +26,7 @@ - - - +