// SPDX-License-Identifier: AGPL-3.0-or-later import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants'; import {MAX_MESSAGE_LENGTH_PREMIUM} from '@fluxer/constants/src/LimitConstants'; import {beforeAll, beforeEach, describe, expect, it} from 'vitest'; import {createTestAccount} from '../../auth/tests/AuthTestUtils'; import {createChannel, createGuild} from '../../guild/tests/GuildTestUtils'; import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness'; import {HTTP_STATUS} from '../../test/TestConstants'; import {createBuilderWithoutAuth} from '../../test/TestRequestBuilder'; import { createWebhook, deleteWebhook, deleteWebhookMessageByToken, executeWebhook, sendChannelMessage, } from './WebhookTestUtils'; describe('Webhook execution', () => { let harness: ApiTestHarness; beforeAll(async () => { harness = await createApiTestHarness(); }); beforeEach(async () => { await harness.reset(); }); it('executes webhook without wait returns 204', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); const result = await executeWebhook(harness, webhook.id, webhook.token, { content: 'Hello from webhook!', }); expect(result.response.status).toBe(204); expect(result.json).toBeNull(); await deleteWebhook(harness, webhook.id, owner.token); }); it('executes webhook with wait=true returns message', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); const result = await executeWebhook( harness, webhook.id, webhook.token, { content: 'Custom user', username: 'Custom Bot', wait: true, }, 200, ); expect(result.response.status).toBe(200); expect(result.json).not.toBeNull(); expect(result.json!.content).toBe('Custom user'); await deleteWebhook(harness, webhook.id, owner.token); }); it('allows webhook message content up to 4000 characters', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); const content = 'w'.repeat(MAX_MESSAGE_LENGTH_PREMIUM); const result = await executeWebhook( harness, webhook.id, webhook.token, { content, wait: true, }, 200, ); expect(result.json!.content).toBe(content); await deleteWebhook(harness, webhook.id, owner.token); }); it('executes webhook in voice channel text chat', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Voice Exec Guild'); const channel = await createChannel(harness, owner.token, guild.id, 'voice-webhook-exec', ChannelTypes.GUILD_VOICE); const webhook = await createWebhook(harness, channel.id, owner.token, 'Voice Exec Webhook'); const result = await executeWebhook( harness, webhook.id, webhook.token, { content: 'Hello from voice text chat', wait: true, }, 200, ); expect(result.response.status).toBe(200); expect(result.json).not.toBeNull(); expect(result.json!.channel_id).toBe(channel.id); expect(result.json!.content).toBe('Hello from voice text chat'); await deleteWebhook(harness, webhook.id, owner.token); }); it('deletes a webhook message by token', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); const result = await executeWebhook( harness, webhook.id, webhook.token, { content: 'Delete me', wait: true, }, 200, ); const messageId = result.json!.id; await deleteWebhookMessageByToken(harness, webhook.id, webhook.token, messageId); await createBuilderWithoutAuth(harness) .get(`/webhooks/${webhook.id}/${webhook.token}/messages/${messageId}`) .expect(HTTP_STATUS.NOT_FOUND) .execute(); await deleteWebhook(harness, webhook.id, owner.token); }); it('rejects deleting a non-webhook message by token', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); const message = await sendChannelMessage(harness, owner.token, channelId, 'Not a webhook message'); await createBuilderWithoutAuth(harness) .delete(`/webhooks/${webhook.id}/${webhook.token}/messages/${message.id}`) .expect(HTTP_STATUS.FORBIDDEN) .execute(); await deleteWebhook(harness, webhook.id, owner.token); }); it('rejects webhook execution without content', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); await createBuilderWithoutAuth(harness) .post(`/webhooks/${webhook.id}/${webhook.token}`) .body({content: ''}) .expect(HTTP_STATUS.BAD_REQUEST, 'CANNOT_SEND_EMPTY_MESSAGE') .execute(); await deleteWebhook(harness, webhook.id, owner.token); }); it('rejects webhook execution with invalid token', async () => { const owner = await createTestAccount(harness); const guild = await createGuild(harness, owner.token, 'Webhook Exec Guild'); const channelId = guild.system_channel_id!; const webhook = await createWebhook(harness, channelId, owner.token, 'Test Webhook'); await createBuilderWithoutAuth(harness) .post(`/webhooks/${webhook.id}/invalid_token`) .body({content: 'Test'}) .expect(HTTP_STATUS.NOT_FOUND) .execute(); await deleteWebhook(harness, webhook.id, owner.token); }); });