mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
127 lines
5.8 KiB
TypeScript
127 lines
5.8 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import * as ChannelConstants from '@fluxer/constants/src/ChannelConstants';
|
|
import {
|
|
ChannelTypes,
|
|
GUILD_TEXT_BASED_CHANNEL_TYPES,
|
|
isMessageTypeDeletable,
|
|
MessageTypes,
|
|
TEXT_BASED_CHANNEL_TYPES,
|
|
} from '@fluxer/constants/src/ChannelConstants';
|
|
import * as ThreadConstants from '@fluxer/constants/src/ThreadConstants';
|
|
import {
|
|
ChannelFlags,
|
|
isValidThreadMemberSettingsFlags,
|
|
publicThreadTypeFor,
|
|
resolveTextThreadType,
|
|
settableChannelFlags,
|
|
THREAD_CHANNEL_TYPES,
|
|
THREAD_FEATURE_CHANNEL_TYPES,
|
|
THREAD_MESSAGE_FLAG_MASK,
|
|
THREAD_ONLY_CHANNEL_TYPES,
|
|
THREAD_PARENT_CHANNEL_TYPES,
|
|
ThreadMemberFlags,
|
|
} from '@fluxer/constants/src/ThreadConstants';
|
|
import {describe, expect, it} from 'vitest';
|
|
|
|
function numericValues(value: unknown): Array<number> {
|
|
if (typeof value === 'number') return [value];
|
|
if (value instanceof Set) return [...value].flatMap(numericValues);
|
|
if (Array.isArray(value)) return value.flatMap(numericValues);
|
|
if (value !== null && typeof value === 'object') return Object.values(value).flatMap(numericValues);
|
|
return [];
|
|
}
|
|
|
|
describe('thread channel and message types', () => {
|
|
it('uses the expected channel type numbers', () => {
|
|
expect(ChannelTypes.ANNOUNCEMENT_THREAD).toBe(10);
|
|
expect(ChannelTypes.PUBLIC_THREAD).toBe(11);
|
|
expect(ChannelTypes.PRIVATE_THREAD).toBe(12);
|
|
expect(ChannelTypes.GUILD_FORUM).toBe(15);
|
|
expect(ChannelTypes.GUILD_MEDIA).toBe(16);
|
|
expect(MessageTypes.THREAD_CREATED).toBe(18);
|
|
expect(MessageTypes.THREAD_STARTER_MESSAGE).toBe(21);
|
|
});
|
|
|
|
it('keeps the text type sets unchanged', () => {
|
|
expect([...GUILD_TEXT_BASED_CHANNEL_TYPES].sort((a, b) => a - b)).toEqual([
|
|
ChannelTypes.GUILD_TEXT,
|
|
ChannelTypes.GUILD_VOICE,
|
|
ChannelTypes.GUILD_ANNOUNCEMENT,
|
|
]);
|
|
expect([...TEXT_BASED_CHANNEL_TYPES].sort((a, b) => a - b)).toEqual([0, 1, 2, 3, 5, 999]);
|
|
for (const type of THREAD_FEATURE_CHANNEL_TYPES) {
|
|
expect(TEXT_BASED_CHANNEL_TYPES.has(type)).toBe(false);
|
|
}
|
|
});
|
|
|
|
it('makes announcement channels thread parents of announcement threads', () => {
|
|
expect(THREAD_CHANNEL_TYPES.has(ChannelTypes.ANNOUNCEMENT_THREAD)).toBe(true);
|
|
expect(THREAD_PARENT_CHANNEL_TYPES.has(ChannelTypes.GUILD_ANNOUNCEMENT)).toBe(true);
|
|
expect(THREAD_ONLY_CHANNEL_TYPES.has(ChannelTypes.GUILD_ANNOUNCEMENT)).toBe(false);
|
|
expect(publicThreadTypeFor(ChannelTypes.GUILD_ANNOUNCEMENT)).toBe(ChannelTypes.ANNOUNCEMENT_THREAD);
|
|
expect(publicThreadTypeFor(ChannelTypes.GUILD_TEXT)).toBe(ChannelTypes.PUBLIC_THREAD);
|
|
expect(publicThreadTypeFor(ChannelTypes.GUILD_FORUM)).toBe(ChannelTypes.PUBLIC_THREAD);
|
|
});
|
|
|
|
it('resolves the thread type a text or announcement parent creates', () => {
|
|
const {GUILD_TEXT, GUILD_ANNOUNCEMENT, GUILD_FORUM, ANNOUNCEMENT_THREAD, PUBLIC_THREAD, PRIVATE_THREAD} =
|
|
ChannelTypes;
|
|
expect(resolveTextThreadType(GUILD_ANNOUNCEMENT, ANNOUNCEMENT_THREAD)).toBe(ANNOUNCEMENT_THREAD);
|
|
expect(resolveTextThreadType(GUILD_ANNOUNCEMENT, PUBLIC_THREAD)).toBe(ANNOUNCEMENT_THREAD);
|
|
expect(resolveTextThreadType(GUILD_ANNOUNCEMENT, PRIVATE_THREAD)).toBeNull();
|
|
expect(resolveTextThreadType(GUILD_TEXT, PUBLIC_THREAD)).toBe(PUBLIC_THREAD);
|
|
expect(resolveTextThreadType(GUILD_TEXT, PRIVATE_THREAD)).toBe(PRIVATE_THREAD);
|
|
expect(resolveTextThreadType(GUILD_TEXT, ANNOUNCEMENT_THREAD)).toBeNull();
|
|
expect(resolveTextThreadType(GUILD_FORUM, PUBLIC_THREAD)).toBeNull();
|
|
});
|
|
|
|
it('lets thread created messages be deleted but never the synthesized starter', () => {
|
|
expect(isMessageTypeDeletable(MessageTypes.THREAD_CREATED)).toBe(true);
|
|
expect(isMessageTypeDeletable(MessageTypes.THREAD_STARTER_MESSAGE)).toBe(false);
|
|
});
|
|
|
|
it('masks both server-only message flags', () => {
|
|
expect(THREAD_MESSAGE_FLAG_MASK).toBe(0x120);
|
|
expect(THREAD_MESSAGE_FLAG_MASK & ChannelConstants.SENDABLE_MESSAGE_FLAGS).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('thread constants', () => {
|
|
it('keeps every numeric value inside a non-negative int32', () => {
|
|
for (const value of numericValues(ThreadConstants)) {
|
|
expect(value).toBeGreaterThanOrEqual(0);
|
|
expect(value).toBeLessThan(2 ** 31);
|
|
}
|
|
});
|
|
|
|
it('allows at most one notification bit on thread member settings', () => {
|
|
expect(isValidThreadMemberSettingsFlags(0)).toBe(true);
|
|
expect(isValidThreadMemberSettingsFlags(ThreadMemberFlags.ALL_MESSAGES)).toBe(true);
|
|
expect(isValidThreadMemberSettingsFlags(ThreadMemberFlags.NO_MESSAGES)).toBe(true);
|
|
expect(isValidThreadMemberSettingsFlags(ThreadMemberFlags.ALL_MESSAGES | ThreadMemberFlags.ONLY_MENTIONS)).toBe(
|
|
false,
|
|
);
|
|
expect(isValidThreadMemberSettingsFlags(ThreadMemberFlags.HAS_INTERACTED)).toBe(false);
|
|
expect(isValidThreadMemberSettingsFlags(1 << 4)).toBe(false);
|
|
expect(isValidThreadMemberSettingsFlags(-1)).toBe(false);
|
|
expect(isValidThreadMemberSettingsFlags(2 ** 32 + 2)).toBe(false);
|
|
expect(isValidThreadMemberSettingsFlags(2 ** 32)).toBe(false);
|
|
});
|
|
|
|
it('limits settable channel flags by type', () => {
|
|
expect(settableChannelFlags(ChannelTypes.GUILD_TEXT)).toBe(0);
|
|
expect(settableChannelFlags(ChannelTypes.PRIVATE_THREAD)).toBe(0);
|
|
expect(settableChannelFlags(ChannelTypes.PUBLIC_THREAD)).toBe(0);
|
|
expect(settableChannelFlags(ChannelTypes.PUBLIC_THREAD, ChannelTypes.GUILD_TEXT)).toBe(0);
|
|
expect(settableChannelFlags(ChannelTypes.PUBLIC_THREAD, ChannelTypes.GUILD_FORUM)).toBe(ChannelFlags.PINNED);
|
|
expect(settableChannelFlags(ChannelTypes.PUBLIC_THREAD, ChannelTypes.GUILD_MEDIA)).toBe(ChannelFlags.PINNED);
|
|
expect(settableChannelFlags(ChannelTypes.PRIVATE_THREAD, ChannelTypes.GUILD_TEXT)).toBe(0);
|
|
expect(settableChannelFlags(ChannelTypes.ANNOUNCEMENT_THREAD, ChannelTypes.GUILD_ANNOUNCEMENT)).toBe(0);
|
|
expect(settableChannelFlags(ChannelTypes.GUILD_FORUM)).toBe(ChannelFlags.REQUIRE_TAG);
|
|
expect(settableChannelFlags(ChannelTypes.GUILD_MEDIA)).toBe(
|
|
ChannelFlags.REQUIRE_TAG | ChannelFlags.HIDE_MEDIA_DOWNLOAD_OPTIONS,
|
|
);
|
|
});
|
|
});
|