Добавлены тесты для групп. Участник может выйти
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m56s
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m56s
This commit is contained in:
@@ -432,6 +432,7 @@ public class GroupsControllerTests
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMemberRole_OtherUsersGroup_ReturnsNotFound()
|
||||
{
|
||||
@@ -460,6 +461,42 @@ public class GroupsControllerTests
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMemberRole_CannotDemoteCreator_ByCreator_ReturnsNotFound()
|
||||
{
|
||||
var (client, _, creatorId, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_member_creator_self_demote");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(client, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
// Creator tries to remove Creator flag from themselves -> not allowed
|
||||
var membershipRequest = new GroupMembershipRequest(creatorId, GroupMembershipRole.Administrator);
|
||||
|
||||
var response = await client.PostAsJsonAsync($"groups/{groupId}/members", membershipRequest, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMemberRole_CannotDemoteCreator_ByOtherAdmin_ReturnsNotFound()
|
||||
{
|
||||
// Owner creates group
|
||||
var (ownerClient, _, ownerId, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_member_creator_protected_owner");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(ownerClient, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
// Create and join second user
|
||||
var (adminClient, _, userId2, _) = await TestClientHelper.CreateGroupMemberAsync(_fixture, ownerClient, groupId, "groups_member_creator_protected_admin");
|
||||
|
||||
// Owner promotes second user to Administrator
|
||||
var promoteRequest = new GroupMembershipRequest(userId2, GroupMembershipRole.Administrator);
|
||||
var promoteResponse = await ownerClient.PostAsJsonAsync($"groups/{groupId}/members", promoteRequest, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, promoteResponse.StatusCode);
|
||||
|
||||
// Second user (now admin) tries to demote the owner (remove Creator flag)
|
||||
var demoteRequest = new GroupMembershipRequest(ownerId, GroupMembershipRole.Administrator);
|
||||
var response = await adminClient.PostAsJsonAsync($"groups/{groupId}/members", demoteRequest, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMember_ExistingMember_ReturnsNoContent()
|
||||
{
|
||||
@@ -517,6 +554,108 @@ public class GroupsControllerTests
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMember_CannotRemoveCreator_ByCreator_ReturnsNotFound()
|
||||
{
|
||||
var (client, _, creatorId, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_remove_creator_self");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(client, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
// Creator tries to remove themselves
|
||||
var response = await client.DeleteAsync($"groups/{groupId}/members/{creatorId}", TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMember_CannotRemoveCreator_ByOtherAdmin_ReturnsNotFound()
|
||||
{
|
||||
var (ownerClient, _, ownerId, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_remove_creator_protection_owner");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(ownerClient, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
var (adminClient, _, userId2, _) = await TestClientHelper.CreateGroupMemberAsync(_fixture, ownerClient, groupId, "groups_remove_creator_protection_admin");
|
||||
|
||||
// Owner promotes second user to Administrator
|
||||
var promoteRequest = new GroupMembershipRequest(userId2, GroupMembershipRole.Administrator);
|
||||
var promoteResponse = await ownerClient.PostAsJsonAsync($"groups/{groupId}/members", promoteRequest, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, promoteResponse.StatusCode);
|
||||
|
||||
// Admin tries to remove creator
|
||||
var response = await adminClient.DeleteAsync($"groups/{groupId}/members/{ownerId}", TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMember_Self_AsMember_ReturnsNoContent()
|
||||
{
|
||||
var (ownerClient, _, _, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_remove_self_owner");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(ownerClient, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
// Create and join a member
|
||||
var (memberClient, _, memberId, _) = await TestClientHelper.CreateGroupMemberAsync(_fixture, ownerClient, groupId, "groups_remove_self_member");
|
||||
|
||||
// Member removes themselves
|
||||
var response = await memberClient.DeleteAsync($"groups/{groupId}/members/{memberId}", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||
|
||||
// Verify member was removed
|
||||
var getResponse = await ownerClient.GetAsync($"groups/{groupId}", TestContext.Current.CancellationToken);
|
||||
var group = await getResponse.Content.ReadFromJsonAsync<GroupResponse>(JsonOptions, TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(group);
|
||||
Assert.DoesNotContain(group!.Members, m => m.UserId == memberId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMember_Self_AsAdmin_ReturnsNoContent()
|
||||
{
|
||||
var (ownerClient, _, _, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_remove_self_owner2");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(ownerClient, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
// Create and join a member
|
||||
var (adminClient, _, adminId, _) = await TestClientHelper.CreateGroupMemberAsync(_fixture, ownerClient, groupId, "groups_remove_self_admin");
|
||||
|
||||
// Owner promotes them to Administrator
|
||||
var promoteRequest = new GroupMembershipRequest(adminId, GroupMembershipRole.Administrator);
|
||||
var promoteResponse = await ownerClient.PostAsJsonAsync($"groups/{groupId}/members", promoteRequest, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, promoteResponse.StatusCode);
|
||||
|
||||
// Admin removes themselves
|
||||
var response = await adminClient.DeleteAsync($"groups/{groupId}/members/{adminId}", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||
|
||||
// Verify admin was removed
|
||||
var getResponse = await ownerClient.GetAsync($"groups/{groupId}", TestContext.Current.CancellationToken);
|
||||
var group = await getResponse.Content.ReadFromJsonAsync<GroupResponse>(JsonOptions, TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(group);
|
||||
Assert.DoesNotContain(group!.Members, m => m.UserId == adminId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveMember_AdminRemovesMember_ReturnsNoContent()
|
||||
{
|
||||
var (ownerClient, _, _, _) = await TestClientHelper.CreateAuthenticatedUserAsync(_fixture, "groups_admin_remove_owner");
|
||||
var (groupId, _) = await TestClientHelper.CreateGroupAsync(ownerClient, TestDataGenerator.UniqueGroupName());
|
||||
|
||||
// Create a regular member
|
||||
var (memberClient, _, memberId, _) = await TestClientHelper.CreateGroupMemberAsync(_fixture, ownerClient, groupId, "groups_admin_remove_member");
|
||||
|
||||
// Create and promote another user to Administrator
|
||||
var (adminClient, _, adminId, _) = await TestClientHelper.CreateGroupMemberAsync(_fixture, ownerClient, groupId, "groups_admin_remove_admin");
|
||||
var promoteRequest = new GroupMembershipRequest(adminId, GroupMembershipRole.Administrator);
|
||||
var promoteResponse = await ownerClient.PostAsJsonAsync($"groups/{groupId}/members", promoteRequest, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, promoteResponse.StatusCode);
|
||||
|
||||
// Admin removes the regular member
|
||||
var response = await adminClient.DeleteAsync($"groups/{groupId}/members/{memberId}", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||
|
||||
// Verify member was removed
|
||||
var getResponse = await ownerClient.GetAsync($"groups/{groupId}", TestContext.Current.CancellationToken);
|
||||
var group = await getResponse.Content.ReadFromJsonAsync<GroupResponse>(JsonOptions, TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(group);
|
||||
Assert.DoesNotContain(group!.Members, m => m.UserId == memberId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Join Link Tests
|
||||
|
||||
@@ -174,20 +174,28 @@ public class GroupService : IGroupService
|
||||
var group = await _groupRepository.FindWithDetailsAsync(groupId, includeSoftDeleted: false, cancellationToken);
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
if (!IsAdmin(group, requesterId))
|
||||
return false;
|
||||
|
||||
var membership = group.Memberships.FirstOrDefault(m => m.UserId == targetUserId);
|
||||
if (membership == null)
|
||||
return false;
|
||||
|
||||
// Creator cannot be removed
|
||||
if (membership.Role.HasFlag(GroupMembershipRole.Creator))
|
||||
{
|
||||
_logger.LogWarning("Attempt to remove creator {UserId} from group {GroupId}", targetUserId, groupId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow users to remove themselves (leave group) unless they are the creator
|
||||
if (requesterId == targetUserId)
|
||||
{
|
||||
await _groupRepository.RemoveMembershipAsync(groupId, targetUserId, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, only admins can remove other members
|
||||
if (!IsAdmin(group, requesterId))
|
||||
return false;
|
||||
|
||||
await _groupRepository.RemoveMembershipAsync(groupId, targetUserId, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
1794
LiquidCode/Migrations/20251115192754_Anything.Designer.cs
generated
Normal file
1794
LiquidCode/Migrations/20251115192754_Anything.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
LiquidCode/Migrations/20251115192754_Anything.cs
Normal file
22
LiquidCode/Migrations/20251115192754_Anything.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiquidCode.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Anything : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user