In progress
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user