diff --git a/EstateManagementUI.BlazorServer.Tests/EstateManagementUI.BlazorServer.Tests.csproj b/EstateManagementUI.BlazorServer.Tests/EstateManagementUI.BlazorServer.Tests.csproj
index 335d3e57..22656015 100644
--- a/EstateManagementUI.BlazorServer.Tests/EstateManagementUI.BlazorServer.Tests.csproj
+++ b/EstateManagementUI.BlazorServer.Tests/EstateManagementUI.BlazorServer.Tests.csproj
@@ -38,4 +38,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs b/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs
index 7f8b9676..1ec82ac5 100644
--- a/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs
+++ b/EstateManagementUI.BlazorServer.Tests/Pages/BaseTest.cs
@@ -3,6 +3,7 @@
using Bunit.TestDoubles;
using EstateManagementUI.BlazorServer.Components.Permissions;
using EstateManagementUI.BlazorServer.Permissions;
+using EstateManagementUI.BlazorServer.UIServices;
using MediatR;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
@@ -20,6 +21,7 @@ protected BaseTest() {
this._mockPermissionService = new Mock();
this._mockPermissionStore = new Mock();
this._fakeNavigationManager = new FakeNavigationManager();
+ this.EstateUIService = new Mock();
this._mockPermissionKeyProvider.Setup(x => x.GetKey()).Returns("test-key");
this._mockPermissionService.Setup(x => x.HasPermissionAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true);
@@ -31,6 +33,7 @@ protected BaseTest() {
this.Services.AddSingleton(this._mockPermissionService.Object);
this.Services.AddSingleton(this._mockAuthStateProvider.Object);
this.Services.AddSingleton(this._mockPermissionStore.Object);
+ this.Services.AddSingleton(this.EstateUIService.Object);
// Add required permission components that render their children
@@ -50,6 +53,7 @@ protected BaseTest() {
protected readonly Mock _mockAuthStateProvider;
protected readonly Mock _mockPermissionStore;
protected readonly FakeNavigationManager _fakeNavigationManager;
+ protected readonly Mock EstateUIService;
///
/// Minimal test double for NavigationManager.
diff --git a/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs b/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs
index 4b6dd9d5..b1cb3081 100644
--- a/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs
+++ b/EstateManagementUI.BlazorServer.Tests/Pages/Estate/EstateIndexPageTests.cs
@@ -1,8 +1,10 @@
using AngleSharp.Dom;
using Bunit;
+using Castle.Components.DictionaryAdapter;
using EstateManagementUI.BlazorServer.Common;
+using EstateManagementUI.BlazorServer.Models;
using EstateManagementUI.BlazorServer.Tests.Pages.FileProcessing;
-using EstateManagementUI.BusinessLogic.Models;
+using EstateManagementUI.BusinessLogic.BackendAPI.DataTransferObjects;
using EstateManagementUI.BusinessLogic.Requests;
using Microsoft.AspNetCore.Components.Web;
using Moq;
@@ -18,22 +20,19 @@ public class EstateIndexPageTests : BaseTest
public void EstateIndex_RendersCorrectly()
{
// Arrange
- EstateModel estate = new() {
- EstateId = Guid.NewGuid(),
- EstateName = "Test Estate",
- Reference = "EST001"
- };
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(estate));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
+ BlazorServer.Models.EstateModel estate = new(Guid.NewGuid(), "Test Estate", "EST001");
+ estate = estate with {
+ ContractCount = 0,
+ RecentContracts = new List(),
+ OperatorCount = 0,
+ AllOperators = new List(),
+ AssignedOperators = new List(),
+ MerchantCount = 0,
+ RecentMerchants = new List(),
+ UserCount = 0
+ };
+
+ this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate));
// Act
IRenderedComponent cut = RenderComponent();
@@ -46,23 +45,19 @@ public void EstateIndex_RendersCorrectly()
public void EstateIndex_DisplaysEstateDetails()
{
// Arrange
- EstateModel estate = new() {
- EstateId = Guid.NewGuid(),
- EstateName = "Test Estate",
- Reference = "EST001",
- Operators = new List()
+ BlazorServer.Models.EstateModel estate = new(Guid.NewGuid(), "Test Estate", "EST001");
+ estate = estate with
+ {
+ ContractCount = 0,
+ RecentContracts = new List(),
+ OperatorCount = 0,
+ AllOperators = new List(),
+ AssignedOperators = new List(),
+ MerchantCount = 0,
+ RecentMerchants = new List(),
+ UserCount = 0
};
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(estate));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
+ this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate));
// Act
IRenderedComponent cut = RenderComponent();
@@ -75,16 +70,19 @@ public void EstateIndex_DisplaysEstateDetails()
public void EstateIndex_HasCorrectPageTitle()
{
// Arrange
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new EstateModel { EstateId = Guid.NewGuid() }));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
+ BlazorServer.Models.EstateModel estate = new(Guid.NewGuid(), "Test Estate", "EST001");
+ estate = estate with
+ {
+ ContractCount = 0,
+ RecentContracts = new List(),
+ OperatorCount = 0,
+ AllOperators = new List(),
+ AssignedOperators = new List(),
+ MerchantCount = 0,
+ RecentMerchants = new List(),
+ UserCount = 0
+ };
+ this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate));
// Act
IRenderedComponent cut = RenderComponent();
@@ -150,13 +148,13 @@ public void EstateIndex_AddOperator_Success_UpdatesAssignedOperators()
RequireCustomMerchantNumber = true,
RequireCustomTerminalNumber = false
};
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(operatorDetails));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success());
-
+
+ this.EstateUIService.Setup(e => e.AddOperatorToEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success);
+
IRenderedComponent cut = RenderComponent();
+ cut.Instance.SetDelayOverride(0);
+ cut.Render(); // required to trigger re-render
+
cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
// Switch to operators tab
@@ -176,7 +174,7 @@ public void EstateIndex_AddOperator_Success_UpdatesAssignedOperators()
addButton.Click();
// Assert
- cut.WaitForAssertion(() => cut.Markup.ShouldContain("Operator added successfully"), timeout: TimeSpan.FromSeconds(5));
+ cut.WaitForAssertion(() => cut.Markup.ShouldContain("Operator added successfully"), timeout: TimeSpan.FromSeconds(10));
}
@@ -191,10 +189,9 @@ public void EstateIndex_AddOperator_Failure_ShowsErrorMessage()
};
SetupSuccessfulDataLoadWithOperators(new List { operatorToAdd });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to add operator"));
-
+
+ this.EstateUIService.Setup(e => e.AddOperatorToEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Failure);
+
IRenderedComponent cut = RenderComponent();
cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
@@ -215,7 +212,6 @@ public void EstateIndex_AddOperator_Failure_ShowsErrorMessage()
addButton.Click();
// Assert
- _mockMediator.Verify(x => x.Send(It.IsAny(), default), Times.AtLeastOnce());
cut.WaitForAssertion(() => cut.Markup.ShouldContain("Failed to add operator"), timeout: TimeSpan.FromSeconds(5));
}
@@ -232,11 +228,12 @@ public void EstateIndex_RemoveOperator_Success_RemovesFromList()
};
SetupSuccessfulDataLoadWithAssignedOperators(new List { assignedOperator });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success());
-
+
+ this.EstateUIService.Setup(e => e.RemoveOperatorFromEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success);
+
IRenderedComponent cut = RenderComponent();
+ cut.Instance.SetDelayOverride(0);
+ cut.Render(); // required to trigger re-render
cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
// Switch to operators tab
@@ -266,10 +263,9 @@ public void EstateIndex_RemoveOperator_Failure_ShowsErrorMessage()
};
SetupSuccessfulDataLoadWithAssignedOperators(new List { assignedOperator });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to remove operator"));
-
+
+ this.EstateUIService.Setup(e => e.RemoveOperatorFromEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Failure(String.Empty));
+
IRenderedComponent cut = RenderComponent();
cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
@@ -362,117 +358,15 @@ public void EstateIndex_DisplaysNoContracts_WhenEmpty()
// Assert
cut.Markup.ShouldContain("No contracts found");
}
-
- [Fact]
- public void EstateIndex_LoadEstateData_EstateQueryFails_NavigatesToError()
- {
- // Arrange
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to load estate"));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
-
- // Act
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Assert
- _fakeNavigationManager.Uri.ShouldContain("error");
- }
-
- [Fact]
- public void EstateIndex_LoadEstateData_MerchantQueryFails_NavigatesToError()
- {
- // Arrange
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new EstateModel { EstateId = Guid.NewGuid() }));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to load merchants"));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
-
- // Act
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Assert
- _fakeNavigationManager.Uri.ShouldContain("error");
- }
-
- [Fact]
- public void EstateIndex_LoadEstateData_ContractQueryFails_NavigatesToError()
- {
- // Arrange
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new EstateModel { EstateId = Guid.NewGuid() }));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to load contracts"));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
-
- // Act
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Assert
- _fakeNavigationManager.Uri.ShouldContain("error");
- }
-
+
[Fact]
- public void EstateIndex_LoadEstateData_AssignedOperatorsQueryFails_NavigatesToError()
+ public void EstateIndex_LoadEstateData_LoadFails_NavigatesToError()
{
// Arrange
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new EstateModel { EstateId = Guid.NewGuid() }));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to load assigned operators"));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
-
- // Act
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Assert
- _fakeNavigationManager.Uri.ShouldContain("error");
- }
+ this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Failure());
- [Fact]
- public void EstateIndex_LoadEstateData_AllOperatorsQueryFails_NavigatesToError()
- {
- // Arrange
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new EstateModel { EstateId = Guid.NewGuid() }));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to load operators"));
-
// Act
IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
// Assert
_fakeNavigationManager.Uri.ShouldContain("error");
@@ -524,119 +418,7 @@ public void EstateIndex_DisplaysNoOperators_WhenNoneAssigned()
// Assert
cut.WaitForAssertion(() => cut.Markup.ShouldContain("No operators assigned"), timeout: TimeSpan.FromSeconds(5));
}
-
- [Fact]
- public void EstateIndex_AddOperator_WhenGetOperatorQueryFails_NavigatesToError()
- {
- // Arrange
- Guid operatorId = Guid.NewGuid();
- OperatorDropDownModel operatorToAdd = new() {
- OperatorId = operatorId,
- OperatorName = "Test Operator"
- };
-
- SetupSuccessfulDataLoadWithOperators(new List { operatorToAdd });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success());
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Failure("Failed to get operator details"));
-
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Switch to operators tab
- IRefreshableElementCollection buttons = cut.FindAll("button");
- IElement? operatorsButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Operators"));
- operatorsButton?.Click();
-
- // Click Add Operator button
- IElement addOperatorButton = cut.Find("#addOperatorButton");
- addOperatorButton.Click();
-
- // Act - Select operator and add
- IElement selectElement = cut.Find("select");
- selectElement.Change(operatorId.ToString());
- IElement addButton = cut.FindAll("button")
- .First(b => b.TextContent.Trim() == "Add" && (b.GetAttribute("id") ?? "") != "addOperatorButton");
- addButton.Click();
-
- // Assert - Should navigate to error page
- _fakeNavigationManager.Uri.ShouldContain("error");
- }
-
- [Fact]
- public void EstateIndex_AddOperator_WhenException_ShowsErrorMessage()
- {
- // Arrange
- Guid operatorId = Guid.NewGuid();
- OperatorDropDownModel operatorToAdd = new() {
- OperatorId = operatorId,
- OperatorName = "Test Operator"
- };
-
- SetupSuccessfulDataLoadWithOperators(new List { operatorToAdd });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ThrowsAsync(new Exception("Test exception"));
-
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Switch to operators tab
- IRefreshableElementCollection buttons = cut.FindAll("button");
- IElement? operatorsButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Operators"));
- operatorsButton?.Click();
-
- // Click Add Operator button
- IElement addOperatorButton = cut.Find("#addOperatorButton");
- addOperatorButton.Click();
-
- // Act - Select operator and add
- IElement selectElement = cut.Find("select");
- selectElement.Change(operatorId.ToString());
- IElement addButton = cut.FindAll("button")
- .First(b => b.TextContent.Trim() == "Add" && (b.GetAttribute("id") ?? "") != "addOperatorButton");
- addButton.Click();
-
- // Assert
- cut.WaitForAssertion(() => cut.Markup.ShouldContain("An error occurred: Test exception"), timeout: TimeSpan.FromSeconds(5));
- }
-
- [Fact]
- public void EstateIndex_RemoveOperator_WhenException_ShowsErrorMessage()
- {
- // Arrange
- Guid operatorId = Guid.NewGuid();
- OperatorModel assignedOperator = new() {
- OperatorId = operatorId,
- Name = "Test Operator",
- RequireCustomMerchantNumber = true,
- RequireCustomTerminalNumber = false
- };
-
- SetupSuccessfulDataLoadWithAssignedOperators(new List { assignedOperator });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ThrowsAsync(new Exception("Test exception"));
-
- IRenderedComponent cut = RenderComponent();
- cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
-
- // Switch to operators tab
- IRefreshableElementCollection buttons = cut.FindAll("button");
- IElement? operatorsButton = buttons.FirstOrDefault(b => b.TextContent.Contains("Operators"));
- operatorsButton?.Click();
-
- // Act - Remove operator
- IRefreshableElementCollection removeButtons = cut.FindAll("button");
- IElement? removeButton = removeButtons.FirstOrDefault(b => b.TextContent.Contains("Remove"));
- removeButton?.Click();
-
- // Assert
- cut.WaitForAssertion(() => cut.Markup.ShouldContain("An error occurred: Test exception"), timeout: TimeSpan.FromSeconds(5));
- }
-
+
[Fact]
public void EstateIndex_SuccessMessage_ClearsWhenSwitchingTabs()
{
@@ -650,10 +432,9 @@ public void EstateIndex_SuccessMessage_ClearsWhenSwitchingTabs()
};
SetupSuccessfulDataLoadWithAssignedOperators(new List { assignedOperator });
-
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success());
-
+
+ this.EstateUIService.Setup(e => e.RemoveOperatorFromEstate(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success);
+
IRenderedComponent cut = RenderComponent();
cut.WaitForState(() => !cut.Markup.Contains("animate-spin"), TimeSpan.FromSeconds(5));
@@ -684,16 +465,19 @@ private void SetupSuccessfulDataLoad(List? merchants = nul
List? assignedOperators = null,
List? operators = null)
{
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(new EstateModel { EstateId = Guid.NewGuid(), EstateName = "Test Estate" }));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(merchants ?? new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(contracts ?? new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(assignedOperators ?? new List()));
- _mockMediator.Setup(x => x.Send(It.IsAny(), default))
- .ReturnsAsync(Result.Success(operators ?? new List()));
+ BlazorServer.Models.EstateModel estate = new(Guid.NewGuid(), "Test Estate", "EST001");
+ estate = estate with
+ {
+ ContractCount = 5,
+ RecentContracts = contracts ?? new List(),
+ OperatorCount = 3,
+ AllOperators = operators ?? new List(),
+ AssignedOperators = assignedOperators ?? new List(),
+ MerchantCount = 10,
+ RecentMerchants = merchants ?? new List(),
+ UserCount = 2
+ };
+ this.EstateUIService.Setup(e => e.LoadEstate(It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(estate));
}
private void SetupSuccessfulDataLoadWithOperators(List operators)
diff --git a/EstateManagementUI.BlazorServer.Tests/UIServices/EstateUIServiceTests.cs b/EstateManagementUI.BlazorServer.Tests/UIServices/EstateUIServiceTests.cs
new file mode 100644
index 00000000..199c35ed
--- /dev/null
+++ b/EstateManagementUI.BlazorServer.Tests/UIServices/EstateUIServiceTests.cs
@@ -0,0 +1,301 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using EstateManagementUI.BlazorServer.UIServices;
+using EstateManagementUI.BusinessLogic.Models;
+using EstateManagementUI.BusinessLogic.Requests;
+using Moq;
+using Shouldly;
+using SimpleResults;
+using Xunit;
+using MediatR;
+
+namespace EstateManagementUI.BlazorServer.Tests.UIServices
+{
+ public class EstateUIServiceTests
+ {
+ private readonly Mock _mockMediator;
+ private readonly EstateUIService _service;
+
+ public EstateUIServiceTests()
+ {
+ _mockMediator = new Mock();
+ _service = new EstateUIService(_mockMediator.Object);
+ }
+
+ [Fact]
+ public async Task LoadEstate_ReturnsMappedModel_WhenAllQueriesSucceed()
+ {
+ // Arrange
+ var estateId = Guid.NewGuid();
+ var correlationId = CorrelationIdHelper.New();
+
+ var bizEstate = new BusinessLogic.Models.EstateModel
+ {
+ EstateId = estateId,
+ EstateName = "Test Estate",
+ Reference = "REF001",
+ Merchants = new List { new() { MerchantId = Guid.NewGuid(), Name = "M1", Reference = "R1" } },
+ Contracts = new List { new() { ContractId = Guid.NewGuid(), Name = "C1", OperatorName = "Op" } },
+ Operators = new List { new() { OperatorId = Guid.NewGuid(), Name = "OpAssigned" } },
+ Users = new List { new() { UserId = Guid.NewGuid(), EmailAddress = "u@x" } }
+ };
+
+ var recentMerchants = new List
+ {
+ new() { MerchantId = Guid.NewGuid(), Name = "RecentM", Reference = "RM", CreatedDateTime = DateTime.UtcNow }
+ };
+
+ var recentContracts = new List
+ {
+ new() { ContractId = Guid.NewGuid(), Description = "RecentC", OperatorName = "Op" }
+ };
+
+ var assignedOperators = new List
+ {
+ new() { OperatorId = bizEstate.Operators!.First().OperatorId, Name = "OpAssigned" }
+ };
+
+ var allOperators = new List
+ {
+ new() { OperatorId = Guid.NewGuid(), OperatorName = "OpA" },
+ new() { OperatorId = assignedOperators[0].OperatorId, OperatorName = "OpAssigned" }
+ };
+
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(bizEstate));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(recentMerchants));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(recentContracts));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(assignedOperators));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(allOperators));
+
+ // Act
+ var result = await _service.LoadEstate(correlationId, estateId);
+
+ // Assert
+ result.IsSuccess.ShouldBeTrue();
+ var model = result.Data!;
+ model.EstateId.ShouldBe(estateId);
+ model.EstateName.ShouldBe("Test Estate");
+ model.MerchantCount.ShouldBe(1);
+ model.ContractCount.ShouldBe(1);
+ model.UserCount.ShouldBe(1);
+ model.AllOperators.ShouldNotBeNull();
+ model.AllOperators.ShouldContain(op => op.OperatorName == "OpA");
+ model.AssignedOperators.ShouldNotBeEmpty();
+ model.RecentMerchants.ShouldNotBeEmpty();
+ model.RecentContracts.ShouldNotBeEmpty();
+ }
+
+ [Fact]
+ public async Task LoadEstate_ReturnsFailure_WhenEstateQueryFails()
+ {
+ // Arrange
+ var estateId = Guid.NewGuid();
+ var correlationId = CorrelationIdHelper.New();
+
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Failure("estate fail"));
+
+ // Provide success for other queries so the service behaviour is isolated
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+
+ // Act
+ var result = await _service.LoadEstate(correlationId, estateId);
+
+ // Assert
+ result.IsFailed.ShouldBeTrue();
+ }
+
+ [Fact]
+ public async Task LoadEstate_ReturnsFailure_WhenRecentMerchantQueryFails()
+ {
+ // Arrange
+ var estateId = Guid.NewGuid();
+ var correlationId = CorrelationIdHelper.New();
+
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success());
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Failure());
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+
+ // Act
+ var result = await _service.LoadEstate(correlationId, estateId);
+
+ // Assert
+ result.IsFailed.ShouldBeTrue();
+ }
+
+ [Fact]
+ public async Task LoadEstate_ReturnsFailure_WhenRecentContractsQueryFails()
+ {
+ // Arrange
+ var estateId = Guid.NewGuid();
+ var correlationId = CorrelationIdHelper.New();
+
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new EstateModel()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Failure());
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+
+ // Act
+ var result = await _service.LoadEstate(correlationId, estateId);
+
+ // Assert
+ result.IsFailed.ShouldBeTrue();
+ }
+
+ [Fact]
+ public async Task LoadEstate_ReturnsFailure_WhenGetAssignedOperatorsQueryFails()
+ {
+ // Arrange
+ var estateId = Guid.NewGuid();
+ var correlationId = CorrelationIdHelper.New();
+
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new EstateModel()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(Result.Success(new List()));
+ _mockMediator.Setup(m => m.Send(It.IsAny