Files
pos-system/apps/web-client-tpos-net/tests/WebClientTpos.ComponentTests/Services/AuthStateServiceTests.cs
Ho Ngoc Hai 7a752f4a82 fix(qa): resolve build failures and test issues found in QA verification
- packages/logger: upgrade tsconfig target to ES2022 to support Array.at()
- packages/http-client: exclude test files from tsc build to prevent noUnusedLocals errors
- packages/http-client/test: use vi.hoisted() for mock functions (vi.mock hoisting fix)
- services/goodgo-mcp-server/tests: use vi.hoisted() for all 4 test files (catalog, inventory, analytics, recipe)
- web-client-tpos: remove stale @using WebClientTpos.Client.Components.Auth from 10 auth pages (moved to blazor-ui RCL)
- web-client-tpos: remove AttachToken() calls in PosDataService (auth via BFF httpOnly cookie)
- web-client-tpos: fix IamApiService.SetAuthHeader() and MerchantApiService.AttachTokenAsync() — make no-op, remove _auth dependency
- web-client-tpos: fix Profile.razor — remove AttachToken() method and calls
- web-client-tpos: fix OnboardingReady.razor — escape @keyframes → @@keyframes in Razor style block
- web-client-tpos: fix PosDataService.GetListFromApiAsync() — check array before property lookup to fix plain array deserialization
- web-client-tpos/tests: update AuthStateServiceTests to new AuthStateService.Login(email, role) signature (no token param)
- web-client-tpos/tests: update PosDataServiceTests to new PosDataService(http) constructor (no authState param)

All 113 Node.js tests pass. All 30 .NET component tests pass. All .NET builds succeed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 12:07:58 +07:00

203 lines
5.4 KiB
C#

using FluentAssertions;
using WebClientTpos.Client.Services;
using Xunit;
namespace WebClientTpos.ComponentTests.Services;
/// <summary>
/// EN: Unit tests for AuthStateService — singleton tracking auth state in the POS app.
/// Token is managed by BFF httpOnly cookie; this service only tracks email, role, expiry.
/// VI: Unit tests cho AuthStateService — singleton theo dõi trạng thái auth trong POS app.
/// Token được quản lý bởi BFF httpOnly cookie; service này chỉ lưu email, role, expiry.
/// </summary>
public class AuthStateServiceTests
{
private static AuthStateService CreateSut() => new();
// -----------------------------------------------------------------------
// Initial state
// -----------------------------------------------------------------------
[Fact]
public void InitialState_ShouldNotBeAuthenticated()
{
// Arrange & Act
var sut = CreateSut();
// Assert
sut.IsAuthenticated.Should().BeFalse();
sut.UserEmail.Should().BeNull();
sut.UserRole.Should().BeNull();
sut.TokenExpiry.Should().BeNull();
}
// -----------------------------------------------------------------------
// Login
// -----------------------------------------------------------------------
[Fact]
public void Login_WithValidCredentials_ShouldSetAuthenticatedState()
{
// Arrange
var sut = CreateSut();
// Act
sut.Login("owner@goodgo.vn", "owner");
// Assert
sut.IsAuthenticated.Should().BeTrue();
sut.UserEmail.Should().Be("owner@goodgo.vn");
sut.UserRole.Should().Be("owner");
}
[Theory]
[InlineData("owner")]
[InlineData("staff")]
[InlineData("customer")]
[InlineData("branch")]
public void Login_WithAnyRole_ShouldSetRole(string role)
{
// Arrange
var sut = CreateSut();
// Act
sut.Login("user@goodgo.vn", role);
// Assert
sut.UserRole.Should().Be(role);
}
[Fact]
public void Login_CalledTwiceWithSameData_ShouldNotFireOnChange()
{
// Arrange
var sut = CreateSut();
sut.Login("user@goodgo.vn", "owner");
var changeCount = 0;
sut.OnChange += () => changeCount++;
// Act — same email + same role again
sut.Login("user@goodgo.vn", "owner");
// Assert
// EN: Idempotent login must not trigger OnChange / VI: Login idempotent không kích hoạt OnChange
changeCount.Should().Be(0);
}
[Fact]
public void Login_WithNewRole_ShouldFireOnChange()
{
// Arrange
var sut = CreateSut();
sut.Login("user@goodgo.vn", "staff");
var changeCount = 0;
sut.OnChange += () => changeCount++;
// Act
sut.Login("user@goodgo.vn", "owner");
// Assert
changeCount.Should().BeGreaterThan(0);
}
// -----------------------------------------------------------------------
// Logout
// -----------------------------------------------------------------------
[Fact]
public void Logout_AfterLogin_ShouldClearState()
{
// Arrange
var sut = CreateSut();
sut.Login("owner@goodgo.vn", "owner");
// Act
sut.Logout();
// Assert
sut.IsAuthenticated.Should().BeFalse();
sut.UserEmail.Should().BeNull();
sut.UserRole.Should().BeNull();
}
[Fact]
public void Logout_ShouldFireOnChange()
{
// Arrange
var sut = CreateSut();
sut.Login("user@goodgo.vn", "staff");
var fired = false;
sut.OnChange += () => fired = true;
// Act
sut.Logout();
// Assert
fired.Should().BeTrue();
}
// -----------------------------------------------------------------------
// GetPortalUrl
// -----------------------------------------------------------------------
[Theory]
[InlineData("owner", "/admin")]
[InlineData("admin", "/admin")]
[InlineData("staff", "/staff/dashboard")]
[InlineData("branch", "/admin")]
[InlineData("customer", "/app")]
[InlineData("unknown", "/auth/login")]
[InlineData(null, "/auth/login")]
public void GetPortalUrl_ShouldReturnCorrectPathForRole(string? role, string expectedUrl)
{
// Arrange
var sut = CreateSut();
if (role != null)
{
sut.Login("user@goodgo.vn", role);
}
// Act
var url = sut.GetPortalUrl();
// Assert
url.Should().Be(expectedUrl);
}
// -----------------------------------------------------------------------
// OnChange event
// -----------------------------------------------------------------------
[Fact]
public void OnChange_ShouldBeRaisedOnLogin()
{
// Arrange
var sut = CreateSut();
var raised = false;
sut.OnChange += () => raised = true;
// Act
sut.Login("new@goodgo.vn", "owner");
// Assert
raised.Should().BeTrue();
}
[Fact]
public void OnChange_MultipleSubscribers_ShouldAllBeNotified()
{
// Arrange
var sut = CreateSut();
var count = 0;
sut.OnChange += () => count++;
sut.OnChange += () => count++;
sut.OnChange += () => count++;
// Act
sut.Logout();
// Assert
count.Should().Be(3);
}
}