Skip to content

Commit

Permalink
Test a lot more scenarios in UserServiceTest
Browse files Browse the repository at this point in the history
  • Loading branch information
rmunn committed Nov 19, 2024
1 parent 4dcdfda commit af79417
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 30 deletions.
161 changes: 131 additions & 30 deletions backend/Testing/LexCore/Services/UserServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
using LexBoxApi.Auth;
using LexBoxApi.Services;
using LexBoxApi.Services.Email;
using LexCore.Auth;
using LexCore.Entities;
using LexCore.ServiceInterfaces;
using LexData;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Npgsql;
using Shouldly;
using Testing.Fixtures;
using FluentAssertions;

namespace Testing.LexCore.Services;

[Collection(nameof(TestingServicesFixture))]
public class UserServiceTest
public class UserServiceTest : IAsyncLifetime
{
private readonly UserService _userService;

private readonly LexBoxDbContext _lexBoxDbContext;
private List<Project> ManagedProjects { get; } = [];
private List<User> ManagedUsers { get; } = [];

// Users created for this test
private User? Robin { get; set; }
private User? John { get; set; }
private User? Marian { get; set; }
private User? Tuck { get; set; }
private User? Sheriff { get; set; }
// Projects created for this test
private Project? Sherwood { get; set; }
private Project? Nottingham { get; set; }

public UserServiceTest(TestingServicesFixture testing)
{
Expand All @@ -34,38 +41,132 @@ public UserServiceTest(TestingServicesFixture testing)
_lexBoxDbContext = serviceProvider.GetRequiredService<LexBoxDbContext>();
}

public Task InitializeAsync()
{
Robin = CreateUser("Robin Hood");
John = CreateUser("Little John");
Marian = CreateUser("Maid Marian");
Tuck = CreateUser("Friar Tuck");
Sheriff = CreateUser("Sheriff of Nottingham");

Nottingham = CreateProject([Sheriff.Id], [Marian.Id]);
Sherwood = CreateConfidentialProject([Robin.Id, Marian.Id], [John.Id]);

return _lexBoxDbContext.SaveChangesAsync();
}

public Task DisposeAsync()
{
foreach (var project in ManagedProjects)
{
_lexBoxDbContext.Remove(project);
}
foreach (var user in ManagedUsers)
{
_lexBoxDbContext.Remove(user);
}
return _lexBoxDbContext.SaveChangesAsync();
}

[Fact]
public async Task ManagerCanSeeAllUsersEvenInConfidentialProjects()
{
var manager = await _lexBoxDbContext.Users.Include(u => u.Organizations).Include(u => u.Projects).FirstOrDefaultAsync(user => user.Email == "manager@test.com");
manager.ShouldNotBeNull();
await using var privateProject = await TempProjectWithoutRepo.Create(_lexBoxDbContext, true, manager.Id);
privateProject.Project.ShouldNotBeNull();
var qaAdmin = await _lexBoxDbContext.Users.Include(u => u.Organizations).Include(u => u.Projects).FirstOrDefaultAsync(user => user.Email == "qa@test.com");
qaAdmin.ShouldNotBeNull();
privateProject.Project.Users.Add(new ProjectUsers() { UserId = qaAdmin.Id, Role = ProjectRole.Editor });
await _lexBoxDbContext.SaveChangesAsync();
var authUser = new LexAuthUser(manager);
var authUser = new LexAuthUser(Robin!);
var users = await _userService.UserQueryForTypeahead(authUser).ToArrayAsync();
users.ShouldNotBeEmpty();
users.ShouldContain(u => u.Id == qaAdmin.Id);
users.Should().BeEquivalentTo([Robin, Marian, John]);
}

[Fact]
public async Task NonManagerCanNotSeeUsersInConfidentialProjects()
{
var manager = await _lexBoxDbContext.Users.Include(u => u.Organizations).Include(u => u.Projects).FirstOrDefaultAsync(user => user.Email == "manager@test.com");
manager.ShouldNotBeNull();
var editor = await _lexBoxDbContext.Users.Include(u => u.Organizations).Include(u => u.Projects).FirstOrDefaultAsync(user => user.Email == "editor@test.com");
editor.ShouldNotBeNull();
await using var privateProject = await TempProjectWithoutRepo.Create(_lexBoxDbContext, true, manager.Id);
privateProject.Project.ShouldNotBeNull();
var qaAdmin = await _lexBoxDbContext.Users.Include(u => u.Organizations).Include(u => u.Projects).FirstOrDefaultAsync(user => user.Email == "qa@test.com");
qaAdmin.ShouldNotBeNull();
privateProject.Project.Users.Add(new ProjectUsers() { UserId = qaAdmin.Id, Role = ProjectRole.Editor });
await _lexBoxDbContext.SaveChangesAsync();
var authUser = new LexAuthUser(editor);
var authUser = new LexAuthUser(John!);
var users = await _userService.UserQueryForTypeahead(authUser).ToArrayAsync();
users.ShouldNotContain(u => u.Id == qaAdmin.Id);
users.Should().BeEmpty();
}

[Fact]
public async Task ManagerOfOneProjectAndMemberOfAnotherPublicProjectCanSeeUsersInBoth()
{
var authUser = new LexAuthUser(Marian!);
var users = await _userService.UserQueryForTypeahead(authUser).ToArrayAsync();
users.Should().BeEquivalentTo([Robin, Marian, John, Sheriff]);
}

[Fact]
public async Task ManagerOfOneProjectAndMemberOfAnotherConfidentialProjectCanNotSeeUsersInConfidentialProject()
{
try
{
// Sheriff tries to sneak into Sherwood...
await AddUserToProject(Sherwood!, Sheriff!);
// ... but can still only see the users in Nottingham
var authUser = new LexAuthUser(Sheriff!);
var users = await _userService.UserQueryForTypeahead(authUser).ToArrayAsync();
users.Should().BeEquivalentTo([Sheriff, Marian]);
}
finally
{
await RemoveUserFromProject(Sherwood!, Sheriff!);
}
}

private User CreateUser(string name)
{
var email = name.ToLowerInvariant().Replace(' ', '_') + "@example.com";
var user = new User
{
Name = name,
Email = email,
CanCreateProjects = true,
EmailVerified = true,
IsAdmin = name.Contains("Admin"),
PasswordHash = "",
Salt = ""
};
_lexBoxDbContext.Add(user);
ManagedUsers.Add(user);
return user; // Caller must call SaveChanges after all users and projects are added
}

private Project CreateProject(IEnumerable<Guid> managers, IEnumerable<Guid> members, bool isConfidential = false)
{
var config = Testing.Services.Utils.GetNewProjectConfig();
var project = new Project
{
Name = config.Name,
Code = config.Code,
IsConfidential = isConfidential,
LastCommit = null,
Organizations = [],
Users = [],
RetentionPolicy = RetentionPolicy.Test,
Type = ProjectType.FLEx,
Id = config.Id,
};
project.Users.AddRange(managers.Select(userId => new ProjectUsers { UserId = userId, Role = ProjectRole.Manager }));
project.Users.AddRange(members.Select(userId => new ProjectUsers { UserId = userId, Role = ProjectRole.Editor }));
_lexBoxDbContext.Add(project);
ManagedProjects.Add(project);
return project; // Caller must call SaveChanges after all users and projects are added
}

private Project CreateConfidentialProject(IEnumerable<Guid> managers, IEnumerable<Guid> members)
{
return CreateProject(managers, members, true);
}

private async Task AddUserToProject(Project project, User user, ProjectRole role = ProjectRole.Editor)
{
var pu = project.Users.FirstOrDefault(pu => pu.UserId == user.Id);
if (pu is null) project.Users.Add(new ProjectUsers { UserId = user.Id, Role = role });
else pu.Role = role;
await _lexBoxDbContext.SaveChangesAsync();
}

private async Task RemoveUserFromProject(Project project, User user)
{
var pu = project.Users.FirstOrDefault(pu => pu.UserId == user.Id);
if (pu is not null) project.Users.Remove(pu);
await _lexBoxDbContext.SaveChangesAsync();
}
}
1 change: 1 addition & 0 deletions backend/Testing/Testing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.8.0" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.9.1" />
<PackageReference Include="Squidex.Assets.TusClient" Version="6.6.4" />
Expand Down

0 comments on commit af79417

Please sign in to comment.