In progress
Some checks failed
Code Coverage / Generate Coverage Report (push) Has been cancelled
Tests / Run Tests (9.0.x) (push) Has been cancelled
Tests / Docker Build Test (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled

This commit is contained in:
Yaojia Wang
2025-11-03 14:00:24 +01:00
parent fe8ad1c1f9
commit 1f66b25f30
74 changed files with 9609 additions and 28 deletions

View File

@@ -0,0 +1,171 @@
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
using ColaFlow.Modules.Identity.Infrastructure.Persistence;
using ColaFlow.Modules.Identity.Infrastructure.Services;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Moq;
namespace ColaFlow.Modules.Identity.Infrastructure.Tests.Persistence;
public class GlobalQueryFilterTests : IDisposable
{
private readonly Mock<ITenantContext> _mockTenantContext;
private readonly IdentityDbContext _context;
public GlobalQueryFilterTests()
{
var options = new DbContextOptionsBuilder<IdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_mockTenantContext = new Mock<ITenantContext>();
_context = new IdentityDbContext(options, _mockTenantContext.Object);
}
[Fact]
public async Task GlobalQueryFilter_ShouldFilterByTenant()
{
// Arrange - Create 2 users from different tenants
var tenant1Id = TenantId.CreateUnique();
var tenant2Id = TenantId.CreateUnique();
// Setup mock to filter for tenant1
var mockTenantContext = new Mock<ITenantContext>();
mockTenantContext.Setup(x => x.IsSet).Returns(true);
mockTenantContext.Setup(x => x.TenantId).Returns(tenant1Id);
var options = new DbContextOptionsBuilder<IdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var context = new IdentityDbContext(options, mockTenantContext.Object);
var user1 = User.CreateLocal(
tenant1Id,
Email.Create("user1@tenant1.com"),
"password123",
FullName.Create("User One"));
var user2 = User.CreateLocal(
tenant2Id,
Email.Create("user2@tenant2.com"),
"password123",
FullName.Create("User Two"));
await context.Users.AddAsync(user1);
await context.Users.AddAsync(user2);
await context.SaveChangesAsync();
// Act - Query users (should be filtered by tenant1)
var filteredUsers = await context.Users.ToListAsync();
// Assert - Should only return tenant1's user
filteredUsers.Should().HaveCount(1);
filteredUsers[0].Email.Value.Should().Be("user1@tenant1.com");
}
[Fact]
public async Task WithoutTenantFilter_ShouldReturnAllUsers()
{
// Arrange - Create 2 users from different tenants
var tenant1Id = TenantId.CreateUnique();
var tenant2Id = TenantId.CreateUnique();
// Setup mock to filter for tenant1
var mockTenantContext = new Mock<ITenantContext>();
mockTenantContext.Setup(x => x.IsSet).Returns(true);
mockTenantContext.Setup(x => x.TenantId).Returns(tenant1Id);
var options = new DbContextOptionsBuilder<IdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var context = new IdentityDbContext(options, mockTenantContext.Object);
var user1 = User.CreateLocal(tenant1Id, Email.Create("admin@tenant1.com"), "pass", FullName.Create("Admin One"));
var user2 = User.CreateLocal(tenant2Id, Email.Create("admin@tenant2.com"), "pass", FullName.Create("Admin Two"));
await context.Users.AddAsync(user1);
await context.Users.AddAsync(user2);
await context.SaveChangesAsync();
// Act - Use WithoutTenantFilter to bypass filter
var allUsers = await context.WithoutTenantFilter<User>().ToListAsync();
// Assert - Should return all users
allUsers.Should().HaveCount(2);
}
[Fact]
public async Task GlobalQueryFilter_ShouldNotFilter_WhenTenantContextNotSet()
{
// Arrange - Tenant context not set
var tenant1Id = TenantId.CreateUnique();
var tenant2Id = TenantId.CreateUnique();
var mockTenantContext = new Mock<ITenantContext>();
mockTenantContext.Setup(x => x.IsSet).Returns(false); // NOT set
var options = new DbContextOptionsBuilder<IdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var context = new IdentityDbContext(options, mockTenantContext.Object);
var user1 = User.CreateLocal(tenant1Id, Email.Create("user1@test.com"), "pass", FullName.Create("User One"));
var user2 = User.CreateLocal(tenant2Id, Email.Create("user2@test.com"), "pass", FullName.Create("User Two"));
await context.Users.AddAsync(user1);
await context.Users.AddAsync(user2);
await context.SaveChangesAsync();
// Act - Query without tenant filter (because IsSet = false)
var allUsers = await context.Users.ToListAsync();
// Assert - Should return all users (no filtering)
allUsers.Should().HaveCount(2);
}
[Fact]
public async Task UserRepository_ShouldRespectTenantFilter()
{
// Arrange
var tenant1Id = TenantId.CreateUnique();
var tenant2Id = TenantId.CreateUnique();
var mockTenantContext = new Mock<ITenantContext>();
mockTenantContext.Setup(x => x.IsSet).Returns(true);
mockTenantContext.Setup(x => x.TenantId).Returns(tenant1Id);
var options = new DbContextOptionsBuilder<IdentityDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var context = new IdentityDbContext(options, mockTenantContext.Object);
var user1 = User.CreateLocal(tenant1Id, Email.Create("john@tenant1.com"), "pass", FullName.Create("John Doe"));
var user2 = User.CreateLocal(tenant2Id, Email.Create("jane@tenant2.com"), "pass", FullName.Create("Jane Doe"));
await context.Users.AddAsync(user1);
await context.Users.AddAsync(user2);
await context.SaveChangesAsync();
// Act
var repository = new ColaFlow.Modules.Identity.Infrastructure.Persistence.Repositories.UserRepository(context);
var retrievedUser = await repository.GetByIdAsync(UserId.Create(user1.Id));
// Assert - Should find user1 (same tenant)
retrievedUser.Should().NotBeNull();
retrievedUser!.Email.Value.Should().Be("john@tenant1.com");
// Trying to get user2 (from different tenant) should return null due to filter
var user2Attempt = await repository.GetByIdAsync(UserId.Create(user2.Id));
user2Attempt.Should().BeNull();
}
public void Dispose()
{
_context.Dispose();
}
}