- Added a `cooldownSeconds` field to the `Tag` model, allowing for customizable cooldown periods between tag responses. - Updated tag command handling to incorporate cooldown logic, preventing spam and improving user experience. - Enhanced the WebUI to support cooldown configuration for tags, including input validation and localization for cooldown messages. - Refactored tag-related service functions to manage cooldowns effectively, ensuring accurate tracking and enforcement. - Improved localization for new cooldown-related messages in both English and German.
211 lines
6.6 KiB
TypeScript
211 lines
6.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
AutomodConfigDashboardPatchSchema,
|
|
CaseListQuerySchema,
|
|
EconomyConfigDashboardPatchSchema,
|
|
FunConfigDashboardSchema,
|
|
GiveawayCreateDashboardSchema,
|
|
LoggingConfigDashboardPatchSchema,
|
|
ModerationDashboardPatchSchema,
|
|
OptionalSnowflakeSchema,
|
|
ScheduledMessageDashboardCreateSchema,
|
|
SelfRolePanelDashboardCreateSchema,
|
|
SocialFeedDashboardCreateSchema,
|
|
StarboardConfigDashboardPatchSchema,
|
|
SuggestionActionSchema,
|
|
TagDashboardCreateSchema,
|
|
TicketCategoryDashboardCreateSchema,
|
|
TicketCategoryDashboardUpdateSchema,
|
|
TicketConfigDashboardPatchSchema,
|
|
WelcomeConfigDashboardPatchSchema
|
|
} from './dashboard.js';
|
|
|
|
describe('dashboard schemas', () => {
|
|
it('accepts moderation patches with escalations', () => {
|
|
const parsed = ModerationDashboardPatchSchema.parse({
|
|
moderationEnabled: true,
|
|
escalations: [{ warnCount: 3, action: 'TIMEOUT', durationMs: 60_000, enabled: true }]
|
|
});
|
|
expect(parsed.escalations?.[0]?.action).toBe('TIMEOUT');
|
|
});
|
|
|
|
it('rejects empty moderation patches', () => {
|
|
expect(() => ModerationDashboardPatchSchema.parse({})).toThrow();
|
|
});
|
|
|
|
it('validates automod patches', () => {
|
|
const parsed = AutomodConfigDashboardPatchSchema.parse({
|
|
antiRaidEnabled: true,
|
|
antiRaidJoinThreshold: 15
|
|
});
|
|
expect(parsed.antiRaidJoinThreshold).toBe(15);
|
|
});
|
|
|
|
it('validates logging patches with channel mappings', () => {
|
|
const parsed = LoggingConfigDashboardPatchSchema.parse({
|
|
logChannels: [{ eventType: 'MEMBER_JOIN', channelId: '123456789012345678' }]
|
|
});
|
|
expect(parsed.logChannels?.[0]?.eventType).toBe('MEMBER_JOIN');
|
|
});
|
|
|
|
it('rejects invalid log channel ids', () => {
|
|
expect(() =>
|
|
LoggingConfigDashboardPatchSchema.parse({
|
|
logChannels: [{ eventType: 'MEMBER_JOIN', channelId: 'not-a-snowflake' }]
|
|
})
|
|
).toThrow();
|
|
});
|
|
|
|
it('accepts empty string or a valid snowflake as optional id', () => {
|
|
expect(OptionalSnowflakeSchema.parse('')).toBe('');
|
|
expect(OptionalSnowflakeSchema.parse('123456789012345678')).toBe('123456789012345678');
|
|
expect(() => OptionalSnowflakeSchema.parse('abc')).toThrow();
|
|
});
|
|
|
|
it('validates welcome patches', () => {
|
|
const parsed = WelcomeConfigDashboardPatchSchema.parse({
|
|
welcomeEnabled: true,
|
|
welcomeChannelId: '123456789012345678'
|
|
});
|
|
expect(parsed.welcomeEnabled).toBe(true);
|
|
});
|
|
|
|
it('validates economy patches', () => {
|
|
const parsed = EconomyConfigDashboardPatchSchema.parse({ currencyName: 'Gems', dailyAmount: 100 });
|
|
expect(parsed.currencyName).toBe('Gems');
|
|
});
|
|
|
|
it('validates fun config', () => {
|
|
const parsed = FunConfigDashboardSchema.parse({ enabled: true, mediaEnabled: false });
|
|
expect(parsed.mediaEnabled).toBe(false);
|
|
});
|
|
|
|
it('applies default limit for case list queries', () => {
|
|
const parsed = CaseListQuerySchema.parse({});
|
|
expect(parsed.limit).toBe(25);
|
|
});
|
|
|
|
it('validates ticket config patches', () => {
|
|
const parsed = TicketConfigDashboardPatchSchema.parse({ mode: 'THREAD', inactivityHours: 24 });
|
|
expect(parsed.mode).toBe('THREAD');
|
|
});
|
|
|
|
it('validates ticket category creation', () => {
|
|
const parsed = TicketCategoryDashboardCreateSchema.parse({
|
|
name: 'Support',
|
|
description: null,
|
|
emoji: '🎫',
|
|
supportRoleIds: ['123456789012345678']
|
|
});
|
|
expect(parsed.supportRoleIds).toHaveLength(1);
|
|
});
|
|
|
|
it('validates ticket category update with support roles', () => {
|
|
const parsed = TicketCategoryDashboardUpdateSchema.parse({
|
|
supportRoleIds: ['123456789012345678', '234567890123456789']
|
|
});
|
|
expect(parsed.supportRoleIds).toEqual(['123456789012345678', '234567890123456789']);
|
|
});
|
|
|
|
it('rejects invalid support role ids on ticket category create', () => {
|
|
expect(() =>
|
|
TicketCategoryDashboardCreateSchema.parse({
|
|
name: 'Support',
|
|
supportRoleIds: ['not-a-snowflake']
|
|
})
|
|
).toThrow();
|
|
});
|
|
|
|
it('validates giveaway creation input', () => {
|
|
const parsed = GiveawayCreateDashboardSchema.parse({
|
|
channelId: '123456789012345678',
|
|
prize: 'Nitro',
|
|
winnerCount: 1,
|
|
endsAt: new Date(Date.now() + 60_000).toISOString()
|
|
});
|
|
expect(parsed.prize).toBe('Nitro');
|
|
});
|
|
|
|
it('rejects giveaway creation with invalid date', () => {
|
|
expect(() =>
|
|
GiveawayCreateDashboardSchema.parse({
|
|
channelId: '123456789012345678',
|
|
prize: 'Nitro',
|
|
winnerCount: 1,
|
|
endsAt: 'not-a-date'
|
|
})
|
|
).toThrow();
|
|
});
|
|
|
|
it('validates self role panel creation', () => {
|
|
const parsed = SelfRolePanelDashboardCreateSchema.parse({
|
|
channelId: '123456789012345678',
|
|
title: 'Colors',
|
|
mode: 'BUTTONS',
|
|
behavior: 'NORMAL',
|
|
roles: [{ roleId: '123456789012345678', label: 'Red' }]
|
|
});
|
|
expect(parsed.roles).toHaveLength(1);
|
|
});
|
|
|
|
it('validates tag creation', () => {
|
|
const parsed = TagDashboardCreateSchema.parse({
|
|
name: 'rules',
|
|
content: 'Please read the rules',
|
|
responseType: 'TEXT',
|
|
allowedRoleIds: [],
|
|
allowedChannelIds: []
|
|
});
|
|
expect(parsed.name).toBe('rules');
|
|
expect(parsed.cooldownSeconds).toBe(15);
|
|
});
|
|
|
|
it('accepts custom tag cooldown', () => {
|
|
const parsed = TagDashboardCreateSchema.parse({
|
|
name: 'rules',
|
|
content: 'Please read the rules',
|
|
responseType: 'TEXT',
|
|
cooldownSeconds: 30,
|
|
allowedRoleIds: [],
|
|
allowedChannelIds: []
|
|
});
|
|
expect(parsed.cooldownSeconds).toBe(30);
|
|
});
|
|
|
|
it('validates starboard patches', () => {
|
|
const parsed = StarboardConfigDashboardPatchSchema.parse({ threshold: 5 });
|
|
expect(parsed.threshold).toBe(5);
|
|
});
|
|
|
|
it('validates suggestion staff actions', () => {
|
|
const parsed = SuggestionActionSchema.parse({ status: 'APPROVED', staffReason: 'Great idea' });
|
|
expect(parsed.status).toBe('APPROVED');
|
|
});
|
|
|
|
it('validates social feed creation', () => {
|
|
const parsed = SocialFeedDashboardCreateSchema.parse({
|
|
type: 'TWITCH',
|
|
sourceId: 'somechannel',
|
|
channelId: '123456789012345678',
|
|
enabled: true
|
|
});
|
|
expect(parsed.type).toBe('TWITCH');
|
|
});
|
|
|
|
it('validates scheduled message creation requires cron or runAt', () => {
|
|
expect(() =>
|
|
ScheduledMessageDashboardCreateSchema.parse({
|
|
channelId: '123456789012345678',
|
|
content: 'Hello'
|
|
})
|
|
).toThrow();
|
|
|
|
const parsed = ScheduledMessageDashboardCreateSchema.parse({
|
|
channelId: '123456789012345678',
|
|
content: 'Hello',
|
|
runAt: new Date(Date.now() + 60_000).toISOString()
|
|
});
|
|
expect(parsed.content).toBe('Hello');
|
|
});
|
|
});
|