mirror of
https://github.com/bitwarden/server
synced 2026-01-26 14:23:21 +00:00
Refine tests (failing)
This commit is contained in:
@@ -1,11 +1,5 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.CollectionRepository;
|
||||
@@ -14,113 +8,87 @@ public class CreateDefaultCollectionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test that CreateDefaultCollectionsAsync successfully creates default collections for new users
|
||||
/// with correct permissions
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task UpsertDefaultCollectionsAsync_CreatesDefaultCollections_Success(
|
||||
[Theory, DatabaseData]
|
||||
public async Task CreateDefaultCollectionsAsync_CreatesDefaultCollections_Success(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
// Arrange
|
||||
var user1 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User 1",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var user2 = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User 2",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user1.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user2.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
var user1 = await userRepository.CreateTestUserAsync("user1");
|
||||
var user2 = await userRepository.CreateTestUserAsync("user2");
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
var orgUser1 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1);
|
||||
var orgUser2 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user2);
|
||||
|
||||
// Act
|
||||
await collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser1.Id, orgUser2.Id },
|
||||
[orgUser1.Id, orgUser2.Id],
|
||||
"My Items");
|
||||
|
||||
// Assert
|
||||
var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||
var defaultCollections = collections.Where(c => c.Type == CollectionType.DefaultUserCollection).ToList();
|
||||
var collectionsWithAccess = await collectionRepository.GetManyByOrganizationIdWithAccessAsync(organization.Id);
|
||||
var defaultCollections = collectionsWithAccess
|
||||
.Where(c => c.Item1.Type == CollectionType.DefaultUserCollection)
|
||||
.ToList();
|
||||
|
||||
Assert.Equal(2, defaultCollections.Count);
|
||||
Assert.All(defaultCollections, c => Assert.Equal("My Items", c.Name));
|
||||
Assert.All(defaultCollections, c => Assert.Equal(organization.Id, c.OrganizationId));
|
||||
Assert.All(defaultCollections, c => Assert.Equal("My Items", c.Item1.Name));
|
||||
Assert.All(defaultCollections, c => Assert.Equal(organization.Id, c.Item1.OrganizationId));
|
||||
|
||||
// Verify each user has exactly 1 collection with correct permissions
|
||||
var orgUser1Collection = Assert.Single(defaultCollections,
|
||||
c => c.Item2.Users.FirstOrDefault()?.Id == orgUser1.Id);
|
||||
|
||||
Assert.Empty(orgUser1Collection.Item2.Groups);
|
||||
|
||||
var orgUser1CollectionUser = orgUser1Collection.Item2.Users.Single();
|
||||
Assert.False(orgUser1CollectionUser.ReadOnly);
|
||||
Assert.False(orgUser1CollectionUser.HidePasswords);
|
||||
Assert.True(orgUser1CollectionUser.Manage);
|
||||
|
||||
// Verify each user has exactly 1 collection with correct permissions
|
||||
var orgUser2Collection = Assert.Single(defaultCollections,
|
||||
c => c.Item2.Users.FirstOrDefault()?.Id == orgUser2.Id);
|
||||
|
||||
Assert.Empty(orgUser2Collection.Item2.Groups);
|
||||
|
||||
var orgUser2CollectionUser = orgUser2Collection.Item2.Users.Single();
|
||||
Assert.False(orgUser2CollectionUser.ReadOnly);
|
||||
Assert.False(orgUser2CollectionUser.HidePasswords);
|
||||
Assert.True(orgUser2CollectionUser.Manage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that calling CreateDefaultCollectionsAsync multiple times does NOT create duplicates
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task UpsertDefaultCollectionsAsync_CalledMultipleTimes_DoesNotCreateDuplicates(
|
||||
[Theory, DatabaseData]
|
||||
public async Task CreateDefaultCollectionsAsync_CalledMultipleTimes_DoesNotCreateDuplicates(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
// Arrange
|
||||
var user = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
var user = await userRepository.CreateTestUserAsync();
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user);
|
||||
|
||||
// Act - Call twice
|
||||
await collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser.Id },
|
||||
[orgUser.Id],
|
||||
"My Items");
|
||||
|
||||
// Second call should not create duplicate
|
||||
// Second call should throw and should not create duplicate
|
||||
await Assert.ThrowsAnyAsync<Exception>(() =>
|
||||
collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser.Id },
|
||||
[orgUser.Id],
|
||||
"My Items"));
|
||||
|
||||
// Assert - Only one collection should exist
|
||||
@@ -128,251 +96,12 @@ public class CreateDefaultCollectionsTests
|
||||
var defaultCollections = collections.Where(c => c.Type == CollectionType.DefaultUserCollection).ToList();
|
||||
|
||||
Assert.Single(defaultCollections);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that UpsertDefaultCollectionsBulkAsync creates semaphores before collections
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task UpsertDefaultCollectionsBulkAsync_CreatesSemaphoresBeforeCollections_Success(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
DatabaseContext databaseContext)
|
||||
{
|
||||
// Arrange
|
||||
var user = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
// Act
|
||||
await collectionRepository.UpsertDefaultCollectionsBulkAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser.Id },
|
||||
"My Items");
|
||||
|
||||
// Assert - Verify semaphore was created
|
||||
var semaphore = await databaseContext.DefaultCollectionSemaphores
|
||||
.FirstOrDefaultAsync(s => s.OrganizationId == organization.Id && s.OrganizationUserId == orgUser.Id);
|
||||
|
||||
Assert.NotNull(semaphore);
|
||||
Assert.Equal(organization.Id, semaphore.OrganizationId);
|
||||
Assert.Equal(orgUser.Id, semaphore.OrganizationUserId);
|
||||
|
||||
// Verify collection was created
|
||||
var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||
var defaultCollections = collections.Where(c => c.Type == CollectionType.DefaultUserCollection).ToList();
|
||||
|
||||
Assert.Single(defaultCollections);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that deleting an OrganizationUser cascades to DefaultCollectionSemaphore
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task DeleteOrganizationUser_CascadesToSemaphore_Success(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
DatabaseContext databaseContext)
|
||||
{
|
||||
// Arrange
|
||||
var user = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
await collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser.Id },
|
||||
"My Items");
|
||||
|
||||
// Verify semaphore exists
|
||||
var semaphoreBefore = await databaseContext.DefaultCollectionSemaphores
|
||||
.FirstOrDefaultAsync(s => s.OrganizationUserId == orgUser.Id);
|
||||
Assert.NotNull(semaphoreBefore);
|
||||
|
||||
// Act - Delete organization user
|
||||
await organizationUserRepository.DeleteAsync(orgUser);
|
||||
|
||||
// Assert - Semaphore should be cascade deleted
|
||||
var semaphoreAfter = await databaseContext.DefaultCollectionSemaphores
|
||||
.FirstOrDefaultAsync(s => s.OrganizationUserId == orgUser.Id);
|
||||
Assert.Null(semaphoreAfter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that deleting an Organization cascades through OrganizationUser to DefaultCollectionSemaphore
|
||||
/// Note: Cascade path is Organization -> OrganizationUser -> DefaultCollectionSemaphore (not direct)
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task DeleteOrganization_CascadesThroughOrganizationUser_Success(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
DatabaseContext databaseContext)
|
||||
{
|
||||
// Arrange
|
||||
var user = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
await collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser.Id },
|
||||
"My Items");
|
||||
|
||||
// Verify semaphore exists
|
||||
var semaphoreBefore = await databaseContext.DefaultCollectionSemaphores
|
||||
.FirstOrDefaultAsync(s => s.OrganizationId == organization.Id);
|
||||
Assert.NotNull(semaphoreBefore);
|
||||
|
||||
// Act - Delete organization (which cascades to OrganizationUser, which cascades to semaphore)
|
||||
await organizationRepository.DeleteAsync(organization);
|
||||
|
||||
// Assert - Semaphore should be cascade deleted via OrganizationUser
|
||||
var semaphoreAfter = await databaseContext.DefaultCollectionSemaphores
|
||||
.FirstOrDefaultAsync(s => s.OrganizationId == organization.Id);
|
||||
Assert.Null(semaphoreAfter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that CreateDefaultCollectionsAsync with empty user list does nothing
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task UpsertDefaultCollectionsAsync_WithEmptyList_DoesNothing(
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
// Act
|
||||
await collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
Array.Empty<Guid>(),
|
||||
"My Items");
|
||||
|
||||
// Assert - No collections should be created
|
||||
var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||
Assert.Empty(collections);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that CreateDefaultCollectionsAsync creates CollectionUser entries with correct permissions
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task UpsertDefaultCollectionsAsync_CreatesCollectionUsersWithCorrectPermissions(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
// Arrange
|
||||
var user = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
// Act
|
||||
await collectionRepository.CreateDefaultCollectionsAsync(
|
||||
organization.Id,
|
||||
new[] { orgUser.Id },
|
||||
"My Items");
|
||||
|
||||
// Assert
|
||||
var collections = await collectionRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||
var defaultCollection = collections.First(c => c.Type == CollectionType.DefaultUserCollection);
|
||||
|
||||
var collectionUsers = await collectionRepository.GetManyUsersByIdAsync(defaultCollection.Id);
|
||||
var collectionUser = collectionUsers.Single();
|
||||
|
||||
Assert.Equal(orgUser.Id, collectionUser.Id);
|
||||
Assert.False(collectionUser.ReadOnly);
|
||||
Assert.False(collectionUser.HidePasswords);
|
||||
Assert.True(collectionUser.Manage);
|
||||
var access = await collectionRepository.GetManyUsersByIdAsync(defaultCollections.Single().Id);
|
||||
var userAccess = Assert.Single(access);
|
||||
Assert.Equal(orgUser.Id, userAccess.Id);
|
||||
Assert.False(userAccess.ReadOnly);
|
||||
Assert.False(userAccess.HidePasswords);
|
||||
Assert.True(userAccess.Manage);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user