mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
208 lines
6.3 KiB
TypeScript
208 lines
6.3 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {createUserID} from '@app/api/BrandedTypes';
|
|
import {Config} from '@app/api/Config';
|
|
import {User} from '@app/api/models/User';
|
|
import {checkGuildVerificationWithResponse} from '@app/api/utils/GuildVerificationUtils';
|
|
import {GuildFeatures, GuildVerificationLevel} from '@fluxer/constants/src/GuildConstants';
|
|
import {ProfileFieldPrivacyFlags} from '@fluxer/constants/src/UserConstants';
|
|
import {GuildEmailVerificationRequiredError} from '@fluxer/errors/src/domains/auth/EmailVerificationRequiredError';
|
|
import {GuildPhoneVerificationRequiredError} from '@fluxer/errors/src/domains/auth/GuildPhoneVerificationRequiredError';
|
|
import {GuildVerificationRequiredError} from '@fluxer/errors/src/domains/guild/GuildVerificationRequiredError';
|
|
import type {GuildMemberResponse} from '@fluxer/schema/src/domains/guild/GuildMemberSchemas';
|
|
import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
|
import {describe, expect, it} from 'vitest';
|
|
|
|
const TEST_USER_ID = createUserID(175928847299117063n);
|
|
const TEST_OWNER_ID = createUserID(275928847299117063n);
|
|
const TEST_JOINED_AT = new Date('2025-01-01T00:00:00.000Z').toISOString();
|
|
|
|
function createUser(params?: {emailVerified?: boolean; hasVerifiedPhone?: boolean}): User {
|
|
return new User({
|
|
user_id: TEST_USER_ID,
|
|
username: 'member',
|
|
discriminator: 1,
|
|
global_name: null,
|
|
bot: false,
|
|
system: false,
|
|
email: '[email protected]',
|
|
email_verified: params?.emailVerified ?? false,
|
|
has_verified_phone: params?.hasVerifiedPhone ?? false,
|
|
email_bounced: false,
|
|
password_hash: 'hashed',
|
|
password_last_changed_at: null,
|
|
totp_secret: null,
|
|
authenticator_types: null,
|
|
avatar_hash: null,
|
|
avatar_color: null,
|
|
banner_hash: null,
|
|
banner_color: null,
|
|
bio: null,
|
|
pronouns: null,
|
|
accent_color: null,
|
|
timezone: null,
|
|
timezone_privacy_flags: ProfileFieldPrivacyFlags.EVERYONE,
|
|
date_of_birth: null,
|
|
locale: null,
|
|
flags: 0n,
|
|
premium_type: null,
|
|
premium_since: null,
|
|
premium_until: null,
|
|
premium_gift_extension_ends_at: null,
|
|
premium_will_cancel: false,
|
|
premium_billing_cycle: null,
|
|
premium_lifetime_sequence: null,
|
|
premium_grace_ends_at: null,
|
|
stripe_subscription_id: null,
|
|
stripe_customer_id: null,
|
|
has_ever_purchased: false,
|
|
suspicious_activity_flags: 0,
|
|
terms_agreed_at: null,
|
|
privacy_agreed_at: null,
|
|
last_active_at: null,
|
|
last_active_ip: null,
|
|
temp_banned_until: null,
|
|
pending_bulk_message_deletion_at: null,
|
|
pending_bulk_message_deletion_channel_count: null,
|
|
pending_bulk_message_deletion_message_count: null,
|
|
pending_deletion_at: null,
|
|
deletion_reason_code: null,
|
|
deletion_public_reason: null,
|
|
deletion_audit_log_reason: null,
|
|
acls: null,
|
|
traits: null,
|
|
first_refund_at: null,
|
|
gift_inventory_server_seq: null,
|
|
gift_inventory_client_seq: null,
|
|
premium_onboarding_dismissed_at: null,
|
|
last_voice_activity_sharing_change_at: null,
|
|
version: 1,
|
|
} as const);
|
|
}
|
|
|
|
function createGuildResponse(features: Array<string>): GuildResponse {
|
|
return {
|
|
id: '1',
|
|
name: 'Guild',
|
|
icon: null,
|
|
banner: null,
|
|
banner_width: null,
|
|
banner_height: null,
|
|
splash: null,
|
|
splash_width: null,
|
|
splash_height: null,
|
|
embed_splash: null,
|
|
embed_splash_width: null,
|
|
embed_splash_height: null,
|
|
splash_card_alignment: 0,
|
|
vanity_url_code: null,
|
|
owner_id: TEST_OWNER_ID.toString(),
|
|
system_channel_id: null,
|
|
system_channel_flags: 0,
|
|
rules_channel_id: null,
|
|
afk_channel_id: null,
|
|
afk_timeout: 0,
|
|
features,
|
|
verification_level: GuildVerificationLevel.NONE,
|
|
mfa_level: 0,
|
|
nsfw_level: 0,
|
|
explicit_content_filter: 0,
|
|
default_message_notifications: 0,
|
|
disabled_operations: 0,
|
|
message_history_cutoff: null,
|
|
} as GuildResponse;
|
|
}
|
|
|
|
function createMemberResponse(joinedAt = TEST_JOINED_AT): GuildMemberResponse {
|
|
return {
|
|
user: {
|
|
id: TEST_USER_ID.toString(),
|
|
username: 'member',
|
|
discriminator: '0001',
|
|
global_name: null,
|
|
avatar: null,
|
|
avatar_color: null,
|
|
flags: 0,
|
|
},
|
|
nick: null,
|
|
avatar: null,
|
|
banner: null,
|
|
accent_color: null,
|
|
roles: [],
|
|
joined_at: joinedAt,
|
|
mute: false,
|
|
deaf: false,
|
|
communication_disabled_until: null,
|
|
profile_flags: undefined,
|
|
} as GuildMemberResponse;
|
|
}
|
|
|
|
const member = createMemberResponse();
|
|
|
|
describe('GuildVerificationUtils', () => {
|
|
it('treats discoverable guilds as requiring at least verified email', () => {
|
|
expect(() =>
|
|
checkGuildVerificationWithResponse({
|
|
user: createUser(),
|
|
guild: createGuildResponse([GuildFeatures.DISCOVERABLE]),
|
|
member,
|
|
}),
|
|
).toThrow(GuildEmailVerificationRequiredError);
|
|
});
|
|
it('keeps NONE unrestricted for non-discoverable guilds', () => {
|
|
expect(() =>
|
|
checkGuildVerificationWithResponse({
|
|
user: createUser(),
|
|
guild: createGuildResponse([]),
|
|
member,
|
|
}),
|
|
).not.toThrow();
|
|
});
|
|
it('allows very high verification with only a verified phone', () => {
|
|
const guild = createGuildResponse([]);
|
|
guild.verification_level = GuildVerificationLevel.VERY_HIGH;
|
|
expect(() =>
|
|
checkGuildVerificationWithResponse({
|
|
user: createUser({emailVerified: false, hasVerifiedPhone: true}),
|
|
guild,
|
|
member: createMemberResponse(new Date().toISOString()),
|
|
}),
|
|
).not.toThrow();
|
|
});
|
|
it('rejects very high verification without a verified phone', () => {
|
|
const guild = createGuildResponse([]);
|
|
guild.verification_level = GuildVerificationLevel.VERY_HIGH;
|
|
expect(() =>
|
|
checkGuildVerificationWithResponse({
|
|
user: createUser({emailVerified: true, hasVerifiedPhone: false}),
|
|
guild,
|
|
member,
|
|
}),
|
|
).toThrow(GuildPhoneVerificationRequiredError);
|
|
});
|
|
it('treats very high as high when phone verification is unavailable', () => {
|
|
const saved = Config.instance.phoneVerificationEnabled;
|
|
Config.instance.phoneVerificationEnabled = false;
|
|
try {
|
|
const guild = createGuildResponse([]);
|
|
guild.verification_level = GuildVerificationLevel.VERY_HIGH;
|
|
expect(() =>
|
|
checkGuildVerificationWithResponse({
|
|
user: createUser({emailVerified: true, hasVerifiedPhone: false}),
|
|
guild,
|
|
member,
|
|
}),
|
|
).not.toThrow();
|
|
expect(() =>
|
|
checkGuildVerificationWithResponse({
|
|
user: createUser({emailVerified: true, hasVerifiedPhone: false}),
|
|
guild,
|
|
member: createMemberResponse(new Date().toISOString()),
|
|
}),
|
|
).toThrow(GuildVerificationRequiredError);
|
|
} finally {
|
|
Config.instance.phoneVerificationEnabled = saved;
|
|
}
|
|
});
|
|
});
|