Files
spouliot dbe4170986 Add unit tests for 9 new services/controllers and expand existing test coverage
116 tests passing: JobPhotoService, MeasurementConversionService, PlatformSettingsService,
QuoteApprovalController, QuotePhotoService, ShopCapabilityCalculator, StorageMigrationService,
TenantContext, UsageQuotaController — plus expanded PricingCalculation, Registration, and
Subscription tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:27:30 -04:00

301 lines
10 KiB
C#

using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Moq;
using PowderCoating.Core.Entities;
using PowderCoating.Infrastructure.Data;
using PowderCoating.Infrastructure.Services;
namespace PowderCoating.UnitTests;
public class TenantContextTests
{
[Fact]
public void GetCurrentCompanyId_WhenUnauthenticated_ReturnsNull()
{
using var context = CreateContext();
var userManager = CreateUserManagerMock();
var accessor = CreateHttpContextAccessor(new ClaimsPrincipal(new ClaimsIdentity()));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var companyId = tenantContext.GetCurrentCompanyId();
Assert.Null(companyId);
}
[Fact]
public void GetCurrentCompanyId_WhenSuperAdminIsImpersonating_ReturnsSessionOverride()
{
using var context = CreateContext();
var userManager = CreateUserManagerMock();
var session = new TestSession();
session.SetInt32("ImpersonatingCompanyId", 42);
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, name: "admin@example.com", roles: ["SuperAdmin"]),
session);
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var companyId = tenantContext.GetCurrentCompanyId();
Assert.Equal(42, companyId);
}
[Fact]
public void GetCurrentCompanyId_PrefersCompanyClaim()
{
using var context = CreateContext();
var userManager = CreateUserManagerMock();
userManager.Setup(x => x.Users).Returns(Enumerable.Empty<ApplicationUser>().AsQueryable());
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, name: "user@example.com", companyIdClaim: 9));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var companyId = tenantContext.GetCurrentCompanyId();
Assert.Equal(9, companyId);
}
[Fact]
public async Task GetCurrentCompanyId_WhenClaimMissing_FallsBackToUserLookup()
{
await using var context = CreateContext();
context.Users.Add(new ApplicationUser
{
Id = "user-1",
UserName = "legacy@example.com",
Email = "legacy@example.com",
FirstName = "Legacy",
LastName = "User",
CompanyId = 17
});
await context.SaveChangesAsync();
var userManager = CreateUserManagerMock();
userManager.Setup(x => x.Users).Returns(context.Users);
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, name: "legacy@example.com"));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var companyId = tenantContext.GetCurrentCompanyId();
Assert.Equal(17, companyId);
}
[Fact]
public void IsPlatformAdmin_ReturnsTrue_ForSuperAdminWithoutTenantScope()
{
using var context = CreateContext();
var userManager = CreateUserManagerMock();
userManager.Setup(x => x.Users).Returns(Enumerable.Empty<ApplicationUser>().AsQueryable());
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, roles: ["SuperAdmin"]));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var isPlatformAdmin = tenantContext.IsPlatformAdmin();
Assert.True(isPlatformAdmin);
}
[Fact]
public void IsPlatformAdmin_ReturnsFalse_ForSuperAdminImpersonatingCompany()
{
using var context = CreateContext();
var userManager = CreateUserManagerMock();
var session = new TestSession();
session.SetInt32("ImpersonatingCompanyId", 2);
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, name: "admin@example.com", roles: ["SuperAdmin"]),
session);
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var isPlatformAdmin = tenantContext.IsPlatformAdmin();
Assert.False(isPlatformAdmin);
}
[Fact]
public async Task UseMetricSystemAsync_ReturnsStoredPreference()
{
await using var context = CreateContext();
context.CompanyPreferences.Add(new CompanyPreferences
{
Id = 1,
CompanyId = 25,
UseMetricSystem = true
});
await context.SaveChangesAsync();
var userManager = CreateUserManagerMock();
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, name: "metric@example.com", companyIdClaim: 25));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var useMetric = await tenantContext.UseMetricSystemAsync();
Assert.True(useMetric);
}
[Fact]
public async Task GetCurrentCompanyAsync_ReturnsCompanyFromUserManager()
{
await using var context = CreateContext();
var company = new Company
{
Id = 31,
CompanyId = 31,
CompanyName = "Current Co",
PrimaryContactName = "Owner",
PrimaryContactEmail = "owner@example.com"
};
var principal = CreatePrincipal(isAuthenticated: true, name: "current@example.com", companyIdClaim: 31);
var user = new ApplicationUser
{
Id = "user-31",
UserName = "current@example.com",
Email = "current@example.com",
FirstName = "Current",
LastName = "User",
CompanyId = 31,
Company = company
};
var userManager = CreateUserManagerMock();
userManager.Setup(x => x.GetUserAsync(principal)).ReturnsAsync(user);
var accessor = CreateHttpContextAccessor(principal);
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var currentCompany = await tenantContext.GetCurrentCompanyAsync();
Assert.NotNull(currentCompany);
Assert.Equal("Current Co", currentCompany!.CompanyName);
}
[Fact]
public void IsPlatformAdmin_ReturnsTrue_ForSuperAdminOnCompanyOne()
{
using var context = CreateContext();
var userManager = CreateUserManagerMock();
var accessor = CreateHttpContextAccessor(
CreatePrincipal(isAuthenticated: true, name: "platform@example.com", companyIdClaim: 1, roles: ["SuperAdmin"]));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var isPlatformAdmin = tenantContext.IsPlatformAdmin();
Assert.True(isPlatformAdmin);
}
[Fact]
public async Task UseMetricSystemAsync_WhenNoCompanyContext_ReturnsFalse()
{
await using var context = CreateContext();
var userManager = CreateUserManagerMock();
var accessor = CreateHttpContextAccessor(CreatePrincipal(isAuthenticated: true, name: "nocompany@example.com"));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var useMetric = await tenantContext.UseMetricSystemAsync();
Assert.False(useMetric);
}
[Fact]
public async Task GetCurrentCompanyAsync_WhenUnauthenticated_ReturnsNull()
{
await using var context = CreateContext();
var userManager = CreateUserManagerMock();
var accessor = CreateHttpContextAccessor(new ClaimsPrincipal(new ClaimsIdentity()));
var tenantContext = new TenantContext(accessor.Object, userManager.Object, context);
var currentCompany = await tenantContext.GetCurrentCompanyAsync();
Assert.Null(currentCompany);
}
private static Mock<IHttpContextAccessor> CreateHttpContextAccessor(ClaimsPrincipal principal, ISession? session = null)
{
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(x => x.User).Returns(principal);
httpContext.SetupGet(x => x.Session).Returns(session ?? new TestSession());
var accessor = new Mock<IHttpContextAccessor>();
accessor.SetupGet(x => x.HttpContext).Returns(httpContext.Object);
return accessor;
}
private static ClaimsPrincipal CreatePrincipal(
bool isAuthenticated,
string? name = null,
int? companyIdClaim = null,
string[]? roles = null)
{
if (!isAuthenticated)
{
return new ClaimsPrincipal(new ClaimsIdentity());
}
var claims = new List<Claim>();
if (!string.IsNullOrWhiteSpace(name))
{
claims.Add(new Claim(ClaimTypes.Name, name));
}
if (companyIdClaim.HasValue)
{
claims.Add(new Claim("CompanyId", companyIdClaim.Value.ToString()));
}
foreach (var role in roles ?? [])
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
var identity = new ClaimsIdentity(claims, "TestAuth", ClaimTypes.Name, ClaimTypes.Role);
return new ClaimsPrincipal(identity);
}
private static Mock<UserManager<ApplicationUser>> CreateUserManagerMock()
{
var store = new Mock<IUserStore<ApplicationUser>>();
return new Mock<UserManager<ApplicationUser>>(
store.Object,
null!,
null!,
null!,
null!,
null!,
null!,
null!,
null!);
}
private static ApplicationDbContext CreateContext()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
return new ApplicationDbContext(options);
}
private sealed class TestSession : ISession
{
private readonly Dictionary<string, byte[]> _values = new();
public IEnumerable<string> Keys => _values.Keys;
public string Id => "test-session";
public bool IsAvailable => true;
public void Clear() => _values.Clear();
public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public Task LoadAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public void Remove(string key) => _values.Remove(key);
public void Set(string key, byte[] value) => _values[key] = value;
public bool TryGetValue(string key, out byte[] value) => _values.TryGetValue(key, out value!);
}
}