refactor progress

This commit is contained in:
Hampus Kraft
2026-02-17 12:22:36 +00:00
parent cb31608523
commit d5abd1a7e4
8257 changed files with 1190207 additions and 761040 deletions

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {createChannelInvite} from '@fluxer/api/src/channel/tests/ChannelTestUtils';
import {banUser, createBannedUserSetup} from '@fluxer/api/src/moderation/tests/ModerationTestUtils';
import {type ApiTestHarness, createApiTestHarness} from '@fluxer/api/src/test/ApiTestHarness';
import {HTTP_STATUS} from '@fluxer/api/src/test/TestConstants';
import {createBuilder} from '@fluxer/api/src/test/TestRequestBuilder';
import {afterEach, beforeEach, describe, it} from 'vitest';
describe('Banned user restrictions', () => {
let harness: ApiTestHarness;
beforeEach(async () => {
harness = await createApiTestHarness();
});
afterEach(async () => {
await harness?.shutdown();
});
it('prevents banned users from accessing guild', async () => {
const {owner, target, guild} = await createBannedUserSetup(harness);
await banUser(harness, owner.token, guild.id, target.userId, 0);
await createBuilder(harness, target.token).get(`/guilds/${guild.id}`).expect(HTTP_STATUS.FORBIDDEN).execute();
});
it('prevents banned users from sending messages', async () => {
const {owner, target, guild, channelId} = await createBannedUserSetup(harness);
await banUser(harness, owner.token, guild.id, target.userId, 0);
await createBuilder(harness, target.token)
.post(`/channels/${channelId}/messages`)
.body({content: "I'm banned"})
.expect(HTTP_STATUS.FORBIDDEN)
.execute();
});
it('prevents banned users from rejoining via invite', async () => {
const {owner, target, guild} = await createBannedUserSetup(harness);
await banUser(harness, owner.token, guild.id, target.userId, 0);
const invite = await createChannelInvite(harness, owner.token, guild.system_channel_id!);
await createBuilder(harness, target.token)
.post(`/invites/${invite.code}`)
.body(null)
.expect(HTTP_STATUS.FORBIDDEN)
.execute();
});
});

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {createTestAccount} from '@fluxer/api/src/auth/tests/AuthTestUtils';
import {blockUser} from '@fluxer/api/src/channel/tests/ChannelTestUtils';
import {type ApiTestHarness, createApiTestHarness} from '@fluxer/api/src/test/ApiTestHarness';
import {HTTP_STATUS} from '@fluxer/api/src/test/TestConstants';
import {createBuilder} from '@fluxer/api/src/test/TestRequestBuilder';
import {afterAll, beforeAll, beforeEach, describe, it} from 'vitest';
describe('Blocked user cannot interact', () => {
let harness: ApiTestHarness;
beforeAll(async () => {
harness = await createApiTestHarness();
});
beforeEach(async () => {
await harness.reset();
});
afterAll(async () => {
await harness?.shutdown();
});
it('prevents blocked users from sending friend requests', async () => {
const user1 = await createTestAccount(harness);
const user2 = await createTestAccount(harness);
await blockUser(harness, user1, user2.userId);
await createBuilder(harness, user2.token)
.post(`/users/@me/relationships/${user1.userId}`)
.body(null)
.expect(HTTP_STATUS.BAD_REQUEST)
.execute();
});
it('prevents creating DM channels with blocked users', async () => {
const user1 = await createTestAccount(harness);
const user2 = await createTestAccount(harness);
await blockUser(harness, user1, user2.userId);
await createBuilder(harness, user2.token)
.post('/users/@me/channels')
.body({recipients: [user1.userId]})
.expect(HTTP_STATUS.BAD_REQUEST)
.execute();
});
});

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {createTestAccount, type TestAccount} from '@fluxer/api/src/auth/tests/AuthTestUtils';
import {
acceptInvite,
createChannelInvite,
createGuild,
getChannel,
setupTestGuildWithMembers,
} from '@fluxer/api/src/channel/tests/ChannelTestUtils';
import type {ApiTestHarness} from '@fluxer/api/src/test/ApiTestHarness';
import {createBuilder} from '@fluxer/api/src/test/TestRequestBuilder';
export async function banUser(
harness: ApiTestHarness,
moderatorToken: string,
guildId: string,
targetUserId: string,
deleteMessageDays = 0,
): Promise<void> {
await createBuilder<void>(harness, moderatorToken)
.put(`/guilds/${guildId}/bans/${targetUserId}`)
.body({
delete_message_days: deleteMessageDays,
})
.expect(204)
.execute();
}
export async function createTimeoutModerationSetup(harness: ApiTestHarness): Promise<{
owner: TestAccount;
moderator: TestAccount;
targetModerator: TestAccount;
higherMember: TestAccount;
guild: {id: string; name: string; system_channel_id?: string | null};
moderatorRole: {id: string};
juniorModeratorRole: {id: string};
higherRole: {id: string};
}> {
const {owner, members, guild} = await setupTestGuildWithMembers(harness, 3);
const [moderator, targetModerator, higherMember] = members;
const moderatorRole = await createRoleWithPermissions(
harness,
owner.token,
guild.id,
'Timeout Moderator Role',
1n << 40n,
);
const juniorModeratorRole = await createRoleWithPermissions(
harness,
owner.token,
guild.id,
'Junior Moderator Role',
1n << 40n,
);
const higherRole = await createRoleWithPermissions(harness, owner.token, guild.id, 'Higher Role', 0n);
await updateRolePositions(harness, owner.token, guild.id, [
{id: higherRole.id, position: 3},
{id: moderatorRole.id, position: 2},
{id: juniorModeratorRole.id, position: 1},
]);
await addMemberRole(harness, owner.token, guild.id, moderator.userId, moderatorRole.id);
await addMemberRole(harness, owner.token, guild.id, targetModerator.userId, juniorModeratorRole.id);
await addMemberRole(harness, owner.token, guild.id, higherMember.userId, higherRole.id);
return {
owner,
moderator,
targetModerator,
higherMember,
guild,
moderatorRole,
juniorModeratorRole,
higherRole,
};
}
async function createRoleWithPermissions(
harness: ApiTestHarness,
token: string,
guildId: string,
name: string,
permissions: bigint,
): Promise<{id: string}> {
return createBuilder<{id: string}>(harness, token)
.post(`/guilds/${guildId}/roles`)
.body({
name,
permissions: permissions.toString(),
})
.execute();
}
async function updateRolePositions(
harness: ApiTestHarness,
token: string,
guildId: string,
roles: Array<{id: string; position: number}>,
): Promise<void> {
await createBuilder<void>(harness, token).patch(`/guilds/${guildId}/roles`).body(roles).expect(204).execute();
}
async function addMemberRole(
harness: ApiTestHarness,
token: string,
guildId: string,
userId: string,
roleId: string,
): Promise<void> {
await createBuilder<void>(harness, token)
.put(`/guilds/${guildId}/members/${userId}/roles/${roleId}`)
.expect(204)
.execute();
}
export async function timeoutMember(
harness: ApiTestHarness,
token: string,
guildId: string,
targetUserId: string,
minutesIntoFuture: number,
expectedStatus: number = 200,
): Promise<{response: Response}> {
const timeoutDate = new Date(Date.now() + minutesIntoFuture * 60 * 1000).toISOString();
return await createBuilder<unknown>(harness, token)
.patch(`/guilds/${guildId}/members/${targetUserId}`)
.body({
communication_disabled_until: timeoutDate,
})
.expect(expectedStatus)
.executeWithResponse();
}
export async function createBannedUserSetup(harness: ApiTestHarness): Promise<{
owner: TestAccount;
target: TestAccount;
guild: {id: string; name: string; system_channel_id?: string | null};
channelId: string;
}> {
const owner = await createTestAccount(harness);
const target = await createTestAccount(harness);
const guild = await createGuild(harness, owner.token, `Ban Test Guild ${Date.now()}`);
const channel = await getChannel(harness, owner.token, guild.system_channel_id!);
const invite = await createChannelInvite(harness, owner.token, channel.id);
await acceptInvite(harness, target.token, invite.code);
return {owner, target, guild, channelId: channel.id};
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {createTimeoutModerationSetup, timeoutMember} from '@fluxer/api/src/moderation/tests/ModerationTestUtils';
import {type ApiTestHarness, createApiTestHarness} from '@fluxer/api/src/test/ApiTestHarness';
import {afterAll, beforeAll, beforeEach, describe, expect, it} from 'vitest';
describe('Timeout moderation guards', () => {
let harness: ApiTestHarness;
beforeAll(async () => {
harness = await createApiTestHarness();
});
beforeEach(async () => {
await harness.reset();
});
afterAll(async () => {
await harness?.shutdown();
});
it('prevents moderators from timing out other moderators at same or higher level', async () => {
const {moderator, targetModerator, higherMember, guild} = await createTimeoutModerationSetup(harness);
const {response: timeoutModeratorResponse} = await timeoutMember(
harness,
moderator.token,
guild.id,
targetModerator.userId,
15,
403,
);
expect(timeoutModeratorResponse.status).toBe(403);
const {response: timeoutHigherResponse} = await timeoutMember(
harness,
moderator.token,
guild.id,
higherMember.userId,
15,
403,
);
expect(timeoutHigherResponse.status).toBe(403);
});
});