Files
Telegram-Panel/tests/TelegramPanel.Web.Tests/GroupAdminKickPersistenceTests.cs
jack b536c73e54 release: 发布 v1.31.75 Issues 修复
合并 dev 集成结果到 main,发布 v1.31.75。云端 dev-latest 部署按用户指示跳过,主分支多架构 Docker 检查当时仍在运行。
2026-08-24 08:18:29 +08:00

254 lines
11 KiB
C#

using Microsoft.AspNetCore.Http;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using TelegramPanel.Core.Interfaces;
using TelegramPanel.Core.Models;
using TelegramPanel.Core.Services;
using TelegramPanel.Data;
using TelegramPanel.Data.Entities;
using TelegramPanel.Data.Repositories;
using TelegramPanel.Web.Api;
using Xunit;
namespace TelegramPanel.Web.Tests;
public sealed class GroupAdminKickPersistenceTests
{
[Fact]
public async Task ()
{
await using var context = await CreateContextAsync();
var service = new StubGroupService
{
Result = new GroupAdminRemovalResult(true, true, true, false, "已撤销管理员权限并踢出成员")
};
var result = await PanelAdminApiEndpoints.KickGroupAdminAsync(
context.GroupId,
context.TargetTelegramUserId,
context.GroupManagement,
service,
CancellationToken.None);
AssertOperationResult(result, StatusCodes.Status200OK, expectedSuccess: true);
Assert.Equal(context.ExecutorAccountId, service.LastAccountId);
Assert.Equal(context.GroupTelegramId, service.LastGroupId);
Assert.Equal(context.TargetTelegramUserId, service.LastTargetUserId);
context.Db.ChangeTracker.Clear();
Assert.Null(await context.Db.AccountGroups.AsNoTracking().SingleOrDefaultAsync(
item => item.AccountId == context.TargetAccountId && item.GroupId == context.GroupId));
Assert.NotNull(await context.Db.AccountGroups.AsNoTracking().SingleOrDefaultAsync(
item => item.AccountId == context.ExecutorAccountId && item.GroupId == context.GroupId));
}
[Fact]
public async Task ()
{
await using var context = await CreateContextAsync();
var service = new StubGroupService
{
Result = new GroupAdminRemovalResult(
false,
true,
false,
false,
"已撤销管理员权限,但尚未踢出成员:权限不足")
};
var result = await PanelAdminApiEndpoints.KickGroupAdminAsync(
context.GroupId,
context.TargetTelegramUserId,
context.GroupManagement,
service,
CancellationToken.None);
AssertOperationResult(result, StatusCodes.Status409Conflict, expectedSuccess: false);
context.Db.ChangeTracker.Clear();
var membership = await context.Db.AccountGroups.AsNoTracking().SingleAsync(
item => item.AccountId == context.TargetAccountId && item.GroupId == context.GroupId);
Assert.False(membership.IsCreator);
Assert.False(membership.IsAdmin);
}
[Fact]
public async Task ()
{
await using var context = await CreateContextAsync();
var service = new StubGroupService
{
Result = new GroupAdminRemovalResult(true, true, false, true, "已撤销管理员权限;目标已不在群组中")
};
var result = await PanelAdminApiEndpoints.KickGroupAdminAsync(
context.GroupId,
context.TargetTelegramUserId,
context.GroupManagement,
service,
CancellationToken.None);
AssertOperationResult(result, StatusCodes.Status200OK, expectedSuccess: true);
context.Db.ChangeTracker.Clear();
Assert.Null(await context.Db.AccountGroups.AsNoTracking().SingleOrDefaultAsync(
item => item.AccountId == context.TargetAccountId && item.GroupId == context.GroupId));
}
private static void AssertOperationResult(IResult result, int expectedStatusCode, bool expectedSuccess)
{
Assert.Equal(
expectedStatusCode,
Assert.IsAssignableFrom<IStatusCodeHttpResult>(result).StatusCode);
var payload = Assert.IsType<OperationResultDto>(
Assert.IsAssignableFrom<IValueHttpResult>(result).Value);
Assert.Equal(expectedSuccess, payload.Success);
}
private static async Task<TestContext> CreateContextAsync()
{
var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync();
var db = new AppDbContext(new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(connection)
.Options);
await db.Database.EnsureCreatedAsync();
var executor = CreateAccount("8613800000001", 10001, 1001);
var target = CreateAccount("8613800000002", 10002, 2002);
db.Accounts.AddRange(executor, target);
await db.SaveChangesAsync();
var group = new Group
{
TelegramId = 90001,
Title = "测试群组",
CreatorAccountId = executor.Id,
SyncedAt = DateTime.UtcNow
};
db.Groups.Add(group);
await db.SaveChangesAsync();
db.AccountGroups.AddRange(
new AccountGroup
{
AccountId = executor.Id,
GroupId = group.Id,
IsCreator = true,
IsAdmin = true,
SyncedAt = DateTime.UtcNow
},
new AccountGroup
{
AccountId = target.Id,
GroupId = group.Id,
IsCreator = false,
IsAdmin = true,
SyncedAt = DateTime.UtcNow
});
await db.SaveChangesAsync();
var groupManagement = new GroupManagementService(
new GroupRepository(db),
new AccountGroupRepository(db));
return new TestContext(
connection,
db,
groupManagement,
group.Id,
group.TelegramId,
executor.Id,
target.Id,
target.UserId);
}
private static Account CreateAccount(string phone, int displayNumber, long userId) => new()
{
Phone = phone,
DisplayNumber = displayNumber,
UserId = userId,
SessionPath = $"sessions/{displayNumber}.session",
ApiId = 1,
ApiHash = "test-api-hash"
};
private sealed class TestContext : IAsyncDisposable
{
public TestContext(
SqliteConnection connection,
AppDbContext db,
GroupManagementService groupManagement,
int groupId,
long groupTelegramId,
int executorAccountId,
int targetAccountId,
long targetTelegramUserId)
{
Connection = connection;
Db = db;
GroupManagement = groupManagement;
GroupId = groupId;
GroupTelegramId = groupTelegramId;
ExecutorAccountId = executorAccountId;
TargetAccountId = targetAccountId;
TargetTelegramUserId = targetTelegramUserId;
}
public SqliteConnection Connection { get; }
public AppDbContext Db { get; }
public GroupManagementService GroupManagement { get; }
public int GroupId { get; }
public long GroupTelegramId { get; }
public int ExecutorAccountId { get; }
public int TargetAccountId { get; }
public long TargetTelegramUserId { get; }
public async ValueTask DisposeAsync()
{
await Db.DisposeAsync();
await Connection.DisposeAsync();
}
}
private sealed class StubGroupService : IGroupService
{
public GroupAdminRemovalResult Result { get; init; } = new(false, false, false, false, "未设置结果");
public int? LastAccountId { get; private set; }
public long? LastGroupId { get; private set; }
public long? LastTargetUserId { get; private set; }
public Task<List<GroupInfo>> GetOwnedGroupsAsync(int accountId) => throw new NotSupportedException();
public Task<GroupInfo> CreateGroupAsync(int accountId, string title, string about, bool isPublic = false, string? username = null) => throw new NotSupportedException();
public Task<GroupInfo> CreatePrivateGroupAsync(int accountId, string title, string about) => throw new NotSupportedException();
public Task<List<GroupInfo>> GetVisibleGroupsAsync(int accountId, CancellationToken cancellationToken = default) => throw new NotSupportedException();
public Task<GroupInfo?> GetGroupInfoAsync(int accountId, long groupId) => throw new NotSupportedException();
public Task<InviteResult> InviteUserAsync(int accountId, long groupId, string username) => throw new NotSupportedException();
public Task<List<InviteResult>> BatchInviteUsersAsync(int accountId, long groupId, List<string> usernames, int delayMs = 2000) => throw new NotSupportedException();
public Task<bool> SetAdminAsync(int accountId, long groupId, string username, AdminRights rights, string title = "Admin") => throw new NotSupportedException();
public Task<List<SetAdminResult>> BatchSetAdminsAsync(int accountId, long groupId, List<AdminRequest> requests) => throw new NotSupportedException();
public Task<bool> KickUserAsync(int accountId, long groupId, string username, bool permanentBan = false) => throw new NotSupportedException();
public Task<bool> KickUserByUserIdAsync(int accountId, long groupId, long userId, bool permanentBan = false) => throw new NotSupportedException();
public Task<GroupAdminRemovalResult> RemoveAdminAndKickAsync(
int accountId,
long groupId,
long targetUserId,
CancellationToken cancellationToken = default)
{
LastAccountId = accountId;
LastGroupId = groupId;
LastTargetUserId = targetUserId;
return Task.FromResult(Result);
}
public Task<bool> LeaveGroupAsync(int accountId, long groupId) => throw new NotSupportedException();
public Task<bool> DisbandGroupAsync(int accountId, long groupId) => throw new NotSupportedException();
public Task<bool> TransferOwnershipAsync(int accountId, long groupId, string targetUsername, string password) => throw new NotSupportedException();
public Task<string> ExportJoinLinkAsync(int accountId, long groupId) => throw new NotSupportedException();
public Task<List<ChannelAdminInfo>> GetAdminsAsync(int accountId, long groupId) => throw new NotSupportedException();
public Task<bool> UpdateGroupInfoAsync(int accountId, long groupId, string title, string? about) => throw new NotSupportedException();
public Task<bool> SetGroupVisibilityAsync(int accountId, long groupId, bool isPublic, string? username = null) => throw new NotSupportedException();
public Task<bool> SetGroupPhotoAsync(int accountId, long groupId, Stream fileStream, string fileName, CancellationToken cancellationToken = default) => throw new NotSupportedException();
}
}