Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-13013] add delete many async method to i user repository and i user service for bulk user deletion #5035

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Core/Repositories/IUserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ public interface IUserRepository : IRepository<User, Guid>
/// <param name="updateDataActions">Registered database calls to update re-encrypted data.</param>
Task UpdateUserKeyAndEncryptedDataAsync(User user,
IEnumerable<UpdateEncryptedDataForKeyRotation> updateDataActions);
Task DeleteManyAsync(IEnumerable<User> users);
}
14 changes: 13 additions & 1 deletion src/Infrastructure.Dapper/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,21 @@
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(

Check failure on line 168 in src/Infrastructure.Dapper/Repositories/UserRepository.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Microsoft.Data.SqlClient.SqlException : The procedure "User_DeleteById" has no parameter named "@ids".
Raw output
Microsoft.Data.SqlClient.SqlException : The procedure "User_DeleteById" has no parameter named "@Ids".
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at Microsoft.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader(Boolean isInternal, Boolean forDescribeParameterEncryption)
   at Microsoft.Data.SqlClient.SqlCommand.InternalEndExecuteNonQuery(IAsyncResult asyncResult, Boolean isInternal, String endMethod)
   at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult)
   at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult)
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<InternalExecuteNonQueryAsync>b__193_1(IAsyncResult asyncResult)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location ---
   at Dapper.SqlMapper.ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, Object param) in /_/Dapper/SqlMapper.Async.cs:line 662
   at Bit.Infrastructure.Dapper.Repositories.UserRepository.DeleteAsync(User user) in /home/runner/work/server/server/src/Infrastructure.Dapper/Repositories/UserRepository.cs:line 168
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 20
--- End of stack trace from previous location ---
$"[{Schema}].[{Table}_DeleteById]",
BTreston marked this conversation as resolved.
Show resolved Hide resolved
new { Id = user.Id },
new { Ids = new List<Guid> { user.Id }.ToGuidIdArrayTVP() },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this change to pass the single Id. The failing integration test will pass then.

commandType: CommandType.StoredProcedure,
commandTimeout: 180);
}
}

Check warning on line 174 in src/Infrastructure.Dapper/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/Repositories/UserRepository.cs#L170-L174

Added lines #L170 - L174 were not covered by tests
public async Task DeleteManyAsync(IEnumerable<User> users)
{
var ids = users.Select(user => user.Id);
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(

Check failure on line 180 in src/Infrastructure.Dapper/Repositories/UserRepository.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteManyAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Microsoft.Data.SqlClient.SqlException : Could not find stored procedure 'dbo.User_DeleteByIds'.
Raw output
Microsoft.Data.SqlClient.SqlException : Could not find stored procedure 'dbo.User_DeleteByIds'.
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at Microsoft.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader(Boolean isInternal, Boolean forDescribeParameterEncryption)
   at Microsoft.Data.SqlClient.SqlCommand.InternalEndExecuteNonQuery(IAsyncResult asyncResult, Boolean isInternal, String endMethod)
   at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult)
   at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult)
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<InternalExecuteNonQueryAsync>b__193_1(IAsyncResult asyncResult)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location ---
   at Dapper.SqlMapper.ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, Object param) in /_/Dapper/SqlMapper.Async.cs:line 662
   at Bit.Infrastructure.Dapper.Repositories.UserRepository.DeleteManyAsync(IEnumerable`1 users) in /home/runner/work/server/server/src/Infrastructure.Dapper/Repositories/UserRepository.cs:line 180
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteManyAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 46
--- End of stack trace from previous location ---
$"[{Schema}].[{Table}_DeleteByIds]",
new { Ids = JsonSerializer.Serialize(ids) },

Check warning on line 182 in src/Infrastructure.Dapper/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.Dapper/Repositories/UserRepository.cs#L176-L182

Added lines #L176 - L182 were not covered by tests
commandType: CommandType.StoredProcedure,
commandTimeout: 180);
}
Expand Down
47 changes: 47 additions & 0 deletions src/Infrastructure.EntityFramework/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,53 @@
var mappedUser = Mapper.Map<User>(user);
dbContext.Users.Remove(mappedUser);

await transaction.CommitAsync();
await dbContext.SaveChangesAsync();
}
}

Check warning on line 267 in src/Infrastructure.EntityFramework/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Repositories/UserRepository.cs#L264-L267

Added lines #L264 - L267 were not covered by tests

public async Task DeleteManyAsync(IEnumerable<Core.Entities.User> users)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);

Check warning on line 273 in src/Infrastructure.EntityFramework/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Repositories/UserRepository.cs#L270-L273

Added lines #L270 - L273 were not covered by tests

var transaction = await dbContext.Database.BeginTransactionAsync();

Check warning on line 275 in src/Infrastructure.EntityFramework/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Repositories/UserRepository.cs#L275

Added line #L275 was not covered by tests

var targetIds = users.Select(u => u.Id).ToList();

Check warning on line 277 in src/Infrastructure.EntityFramework/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Repositories/UserRepository.cs#L277

Added line #L277 was not covered by tests

await dbContext.WebAuthnCredentials.Where(wa => targetIds.Contains(wa.UserId)).ExecuteDeleteAsync();
await dbContext.Ciphers.Where(c => targetIds.Contains(c.UserId ?? default)).ExecuteDeleteAsync();
await dbContext.Folders.Where(f => targetIds.Contains(f.UserId)).ExecuteDeleteAsync();
await dbContext.AuthRequests.Where(a => targetIds.Contains(a.UserId)).ExecuteDeleteAsync();
await dbContext.Devices.Where(d => targetIds.Contains(d.UserId)).ExecuteDeleteAsync();
var collectionUsers = from cu in dbContext.CollectionUsers
join ou in dbContext.OrganizationUsers on cu.OrganizationUserId equals ou.Id
where targetIds.Contains(ou.UserId ?? default)
select cu;
dbContext.CollectionUsers.RemoveRange(collectionUsers);
var groupUsers = from gu in dbContext.GroupUsers
join ou in dbContext.OrganizationUsers on gu.OrganizationUserId equals ou.Id
where targetIds.Contains(ou.UserId ?? default)
select gu;
dbContext.GroupUsers.RemoveRange(groupUsers);
await dbContext.UserProjectAccessPolicy.Where(ap => targetIds.Contains(ap.OrganizationUser.UserId ?? default)).ExecuteDeleteAsync();
await dbContext.UserServiceAccountAccessPolicy.Where(ap => targetIds.Contains(ap.OrganizationUser.UserId ?? default)).ExecuteDeleteAsync();
await dbContext.OrganizationUsers.Where(ou => targetIds.Contains(ou.UserId ?? default)).ExecuteDeleteAsync();
await dbContext.ProviderUsers.Where(pu => targetIds.Contains(pu.UserId ?? default)).ExecuteDeleteAsync();
await dbContext.SsoUsers.Where(su => targetIds.Contains(su.UserId)).ExecuteDeleteAsync();
await dbContext.EmergencyAccesses.Where(ea => targetIds.Contains(ea.GrantorId) || targetIds.Contains(ea.GranteeId ?? default)).ExecuteDeleteAsync();
await dbContext.Sends.Where(s => targetIds.Contains(s.UserId ?? default)).ExecuteDeleteAsync();
await dbContext.NotificationStatuses.Where(ns => targetIds.Contains(ns.UserId)).ExecuteDeleteAsync();
await dbContext.Notifications.Where(n => targetIds.Contains(n.UserId ?? default)).ExecuteDeleteAsync();

Check warning on line 302 in src/Infrastructure.EntityFramework/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Repositories/UserRepository.cs#L279-L302

Added lines #L279 - L302 were not covered by tests

foreach (var u in users)
{
var mappedUser = Mapper.Map<User>(u);
dbContext.Users.Remove(mappedUser);
}

Check warning on line 308 in src/Infrastructure.EntityFramework/Repositories/UserRepository.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Repositories/UserRepository.cs#L305-L308

Added lines #L305 - L308 were not covered by tests


await transaction.CommitAsync();
await dbContext.SaveChangesAsync();
}
Expand Down
158 changes: 158 additions & 0 deletions src/Sql/dbo/Stored Procedures/User_DeleteByIds.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
CREATE PROCEDURE [dbo].[User_DeleteByIds]
@Ids NVARCHAR(MAX)
WITH RECOMPILE
AS
BEGIN
SET NOCOUNT ON
-- Declare a table variable to hold the parsed JSON data
DECLARE @ParsedIds TABLE (Id UNIQUEIDENTIFIER);

-- Parse the JSON input into the table variable
INSERT INTO @ParsedIds (Id)
SELECT value
FROM OPENJSON(@Ids);

-- Check if the input table is empty
IF (SELECT COUNT(1) FROM @ParsedIds) < 1
BEGIN
RETURN(-1);
END

DECLARE @BatchSize INT = 100

-- Delete ciphers
WHILE @BatchSize > 0
BEGIN
BEGIN TRANSACTION User_DeleteById_Ciphers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may want a big transaction that scopes deleting everything related to each single user. @rkac-bw will have much better insights

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a single migration on release night I like a large transaction, it is a one time script and must succeed. As a reoccurring script that runs at any time creating a large transaction will lead to lots of locking and potential deadlocks on a busy db like ours, I would prefer a retry on error with smaller transactions and possibly
give it a deadlock priority of high so it is not the victim


DELETE TOP(@BatchSize)
FROM
[dbo].[Cipher]
WHERE
[UserId] IN (@ParsedIds)

SET @BatchSize = @@ROWCOUNT

COMMIT TRANSACTION User_DeleteById_Ciphers
END

BEGIN TRANSACTION User_DeleteById

-- Delete WebAuthnCredentials
DELETE
FROM
[dbo].[WebAuthnCredential]
WHERE
[UserId] IN (@ParsedIds)

-- Delete folders
DELETE
FROM
[dbo].[Folder]
WHERE
[UserId] IN (@ParsedIds)

-- Delete AuthRequest, must be before Device
DELETE
FROM
[dbo].[AuthRequest]
WHERE
[UserId] IN (@ParsedIds)

-- Delete devices
DELETE
FROM
[dbo].[Device]
WHERE
[UserId] IN (@ParsedIds)

-- Delete collection users
DELETE
CU
FROM
[dbo].[CollectionUser] CU
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.[Id] = CU.[OrganizationUserId]
WHERE
OU.[UserId] IN (@ParsedIds)

-- Delete group users
DELETE
GU
FROM
[dbo].[GroupUser] GU
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.[Id] = GU.[OrganizationUserId]
WHERE
OU.[UserId] IN (@ParsedIds)

-- Delete AccessPolicy
DELETE
AP
FROM
[dbo].[AccessPolicy] AP
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.[Id] = AP.[OrganizationUserId]
WHERE
[UserId] IN (@ParsedIds)

-- Delete organization users
DELETE
FROM
[dbo].[OrganizationUser]
WHERE
[UserId] IN (@ParsedIds)

-- Delete provider users
DELETE
FROM
[dbo].[ProviderUser]
WHERE
[UserId] IN (@ParsedIds)

-- Delete SSO Users
DELETE
FROM
[dbo].[SsoUser]
WHERE
[UserId] IN (@ParsedIds)

-- Delete Emergency Accesses
DELETE
FROM
[dbo].[EmergencyAccess]
WHERE
[GrantorId] in (@ParsedIds)
OR
[GranteeId] in (@ParsedIds)

-- Delete Sends
DELETE
FROM
[dbo].[Send]
WHERE
[UserId] IN (@ParsedIds)

-- Delete Notification Status
DELETE
FROM
[dbo].[NotificationStatus]
WHERE
[UserId] IN (@ParsedIds)

-- Delete Notification
DELETE
FROM
[dbo].[Notification]
WHERE
[UserId] IN (@ParsedIds)

-- Finally, delete the user
DELETE
FROM
[dbo].[User]
WHERE
[Id] in (@ParsedIds)

COMMIT TRANSACTION User_DeleteById
END
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
๏ปฟusing Bit.Core.Entities;
using Bit.Core.Repositories;
using Xunit;

namespace Bit.Infrastructure.IntegrationTest.Repositories;

public class UserRepositoryTests
{
[DatabaseTheory, DatabaseData]
public async Task DeleteAsync_Works(IUserRepository userRepository)
{
var user = await userRepository.CreateAsync(new User
{
Name = "Test User",
Email = $"test+{Guid.NewGuid()}@example.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});

await userRepository.DeleteAsync(user);

var newUser = await userRepository.GetByIdAsync(user.Id);
Assert.NotNull(newUser);

Check failure on line 23 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 23
--- End of stack trace from previous location ---

Check failure on line 23 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 23
--- End of stack trace from previous location ---

Check failure on line 23 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 23
--- End of stack trace from previous location ---

Check failure on line 23 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 23
--- End of stack trace from previous location ---
Assert.NotEqual(newUser.AccountRevisionDate, user.AccountRevisionDate);
}

[DatabaseTheory, DatabaseData]
public async Task DeleteManyAsync_Works(IUserRepository userRepository)
{
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",
});

await userRepository.DeleteManyAsync(new List<User>
{
user1,
user2
});

var updatedUser1 = await userRepository.GetByIdAsync(user1.Id);
Assert.NotNull(updatedUser1);

Check failure on line 53 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteManyAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteManyAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 53
--- End of stack trace from previous location ---

Check failure on line 53 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteManyAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteManyAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 53
--- End of stack trace from previous location ---

Check failure on line 53 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteManyAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteManyAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 53
--- End of stack trace from previous location ---

Check failure on line 53 in test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs

View workflow job for this annotation

GitHub Actions / Test Results

Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests โ–บ DeleteManyAsync_Works(userRepository: UserRepository { })

Failed test found in: test/Infrastructure.IntegrationTest/TestResults/infrastructure-test-results.trx Error: Assert.NotNull() Failure: Value is null
Raw output
Assert.NotNull() Failure: Value is null
   at Bit.Infrastructure.IntegrationTest.Repositories.UserRepositoryTests.DeleteManyAsync_Works(IUserRepository userRepository) in /home/runner/work/server/server/test/Infrastructure.IntegrationTest/Auth/Repositories/UserRepoistoryTests.cs:line 53
--- End of stack trace from previous location ---
var updatedUser2 = await userRepository.GetByIdAsync(user2.Id);
Assert.NotNull(updatedUser2);

Assert.NotEqual(updatedUser1.AccountRevisionDate, user1.AccountRevisionDate);
Assert.NotEqual(updatedUser2.AccountRevisionDate, user2.AccountRevisionDate);
}

}
Loading