mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-11 05:02:24 +09:00
197 lines
9.7 KiB
TypeScript
197 lines
9.7 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {createTestAccount} from '@app/api/auth/tests/AuthTestUtils';
|
|
import {createFriendship} from '@app/api/channel/tests/ChannelTestUtils';
|
|
import {acceptInvite, createChannelInvite, createGuild, getChannel} from '@app/api/guild/tests/GuildTestUtils';
|
|
import {getInstanceConfigRepository} from '@app/api/middleware/ServiceSingletons';
|
|
import {type ApiTestHarness, createApiTestHarness} from '@app/api/test/ApiTestHarness';
|
|
import {createBuilder} from '@app/api/test/TestRequestBuilder';
|
|
import {
|
|
ProfileFieldPrivacyFlags,
|
|
type ProfilePrivacyLevel,
|
|
ProfilePrivacyLevels,
|
|
} from '@fluxer/constants/src/UserConstants';
|
|
import {getCurrentTimeZoneOffsetMinutes} from '@fluxer/date_utils/src/TimeZoneUtils';
|
|
import {DEFAULT_PROFILE_TIMEZONE_CONFIG} from '@fluxer/schema/src/domains/admin/ProfileTimezoneSchemas';
|
|
import type {UserPrivateResponse, UserProfileFullResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
|
import {afterAll, beforeAll, beforeEach, describe, expect, it} from 'vitest';
|
|
|
|
const TEST_TIMEZONE = 'America/New_York';
|
|
const TEST_TIMEZONE_OFFSET = getCurrentTimeZoneOffsetMinutes(TEST_TIMEZONE);
|
|
|
|
if (TEST_TIMEZONE_OFFSET == null) {
|
|
throw new Error(`Expected ${TEST_TIMEZONE} to have a current UTC offset`);
|
|
}
|
|
|
|
async function updateProfileTimezone(
|
|
harness: ApiTestHarness,
|
|
token: string,
|
|
data: {timezone?: string | null; timezone_privacy_flags?: number},
|
|
): Promise<UserPrivateResponse> {
|
|
return createBuilder<UserPrivateResponse>(harness, token).patch('/users/@me').body(data).execute();
|
|
}
|
|
|
|
async function setProfileTimezoneUsers(userIds: Array<string>): Promise<void> {
|
|
await getInstanceConfigRepository().setProfileTimezoneConfig({
|
|
...DEFAULT_PROFILE_TIMEZONE_CONFIG,
|
|
enabled: true,
|
|
included_user_ids: userIds,
|
|
});
|
|
}
|
|
|
|
async function updateProfilePrivacy(
|
|
harness: ApiTestHarness,
|
|
token: string,
|
|
profilePrivacy: ProfilePrivacyLevel,
|
|
): Promise<void> {
|
|
await createBuilder(harness, token).patch('/users/@me/settings').body({profile_privacy: profilePrivacy}).execute();
|
|
}
|
|
|
|
async function getUserProfile(
|
|
harness: ApiTestHarness,
|
|
token: string,
|
|
userId: string,
|
|
): Promise<UserProfileFullResponse> {
|
|
return createBuilder<UserProfileFullResponse>(harness, token).get(`/users/${userId}/profile`).execute();
|
|
}
|
|
|
|
async function createSharedGuild(harness: ApiTestHarness, ownerToken: string, memberToken: string): Promise<void> {
|
|
const guild = await createGuild(harness, ownerToken, 'Timezone Test Guild');
|
|
const systemChannel = await getChannel(harness, ownerToken, guild.system_channel_id!);
|
|
const invite = await createChannelInvite(harness, ownerToken, systemChannel.id);
|
|
await acceptInvite(harness, memberToken, invite.code);
|
|
}
|
|
|
|
describe('User Profile Timezone Visibility', () => {
|
|
let harness: ApiTestHarness;
|
|
beforeAll(async () => {
|
|
harness = await createApiTestHarness();
|
|
});
|
|
beforeEach(async () => {
|
|
await harness.reset();
|
|
});
|
|
afterAll(async () => {
|
|
await harness?.shutdown();
|
|
});
|
|
it('defaults timezone visibility to everyone when a timezone is set', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
const viewerAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
const updated = await updateProfileTimezone(harness, targetAccount.token, {timezone: TEST_TIMEZONE});
|
|
expect(updated.timezone).toBe(TEST_TIMEZONE);
|
|
expect(updated.timezone_privacy_flags).toBe(ProfileFieldPrivacyFlags.EVERYONE);
|
|
await createFriendship(harness, targetAccount, viewerAccount);
|
|
const profile = await getUserProfile(harness, viewerAccount.token, targetAccount.userId);
|
|
expect(profile.timezone_offset).toBe(TEST_TIMEZONE_OFFSET);
|
|
});
|
|
it('restores default timezone visibility when a timezone is set again without explicit flags', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
await updateProfileTimezone(harness, targetAccount.token, {
|
|
timezone: TEST_TIMEZONE,
|
|
timezone_privacy_flags: 0,
|
|
});
|
|
await updateProfileTimezone(harness, targetAccount.token, {timezone: null});
|
|
const updated = await updateProfileTimezone(harness, targetAccount.token, {timezone: TEST_TIMEZONE});
|
|
expect(updated.timezone_privacy_flags).toBe(ProfileFieldPrivacyFlags.EVERYONE);
|
|
});
|
|
it('hides timezone from the public profile when privacy flags are unset', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
await updateProfileTimezone(harness, targetAccount.token, {
|
|
timezone: TEST_TIMEZONE,
|
|
timezone_privacy_flags: 0,
|
|
});
|
|
const profile = await getUserProfile(harness, targetAccount.token, targetAccount.userId);
|
|
expect(profile.timezone_offset).toBeNull();
|
|
});
|
|
it('shows timezone to friends only when the friends flag is set', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
const friendAccount = await createTestAccount(harness);
|
|
const guildMemberAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
await updateProfileTimezone(harness, targetAccount.token, {
|
|
timezone: TEST_TIMEZONE,
|
|
timezone_privacy_flags: ProfileFieldPrivacyFlags.FRIENDS,
|
|
});
|
|
await createFriendship(harness, targetAccount, friendAccount);
|
|
await createSharedGuild(harness, targetAccount.token, guildMemberAccount.token);
|
|
const friendProfile = await getUserProfile(harness, friendAccount.token, targetAccount.userId);
|
|
const guildMemberProfile = await getUserProfile(harness, guildMemberAccount.token, targetAccount.userId);
|
|
expect(friendProfile.timezone_offset).toBe(TEST_TIMEZONE_OFFSET);
|
|
expect(guildMemberProfile.timezone_offset).toBeNull();
|
|
});
|
|
it('shows timezone to mutual community members only when the mutual communities flag is set', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
const friendAccount = await createTestAccount(harness);
|
|
const guildMemberAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
await updateProfileTimezone(harness, targetAccount.token, {
|
|
timezone: TEST_TIMEZONE,
|
|
timezone_privacy_flags: ProfileFieldPrivacyFlags.MUTUAL_GUILDS,
|
|
});
|
|
await createFriendship(harness, targetAccount, friendAccount);
|
|
await createSharedGuild(harness, targetAccount.token, guildMemberAccount.token);
|
|
const friendProfile = await getUserProfile(harness, friendAccount.token, targetAccount.userId);
|
|
const guildMemberProfile = await getUserProfile(harness, guildMemberAccount.token, targetAccount.userId);
|
|
expect(friendProfile.timezone_offset).toBeNull();
|
|
expect(guildMemberProfile.timezone_offset).toBe(TEST_TIMEZONE_OFFSET);
|
|
});
|
|
it('hides timezone when full profile privacy restricts the viewer', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
const guildMemberAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
await updateProfileTimezone(harness, targetAccount.token, {timezone: TEST_TIMEZONE});
|
|
await updateProfilePrivacy(harness, targetAccount.token, ProfilePrivacyLevels.FRIENDS_ONLY);
|
|
await createSharedGuild(harness, targetAccount.token, guildMemberAccount.token);
|
|
const profile = await getUserProfile(harness, guildMemberAccount.token, targetAccount.userId);
|
|
expect(profile.profile_limited).toBe(true);
|
|
expect(profile.timezone_offset).toBeNull();
|
|
});
|
|
it('ignores profile timezone updates from users outside the experiment', async () => {
|
|
const targetAccount = await createTestAccount(harness, {skipEmailVerification: true});
|
|
const updated = await updateProfileTimezone(harness, targetAccount.token, {
|
|
timezone: TEST_TIMEZONE,
|
|
timezone_privacy_flags: ProfileFieldPrivacyFlags.FRIENDS,
|
|
});
|
|
expect(updated.timezone).toBeNull();
|
|
expect(updated.timezone_privacy_flags).toBe(ProfileFieldPrivacyFlags.EVERYONE);
|
|
});
|
|
it('ignores profile timezone updates from users excluded from a full rollout', async () => {
|
|
const targetAccount = await createTestAccount(harness, {skipEmailVerification: true});
|
|
await getInstanceConfigRepository().setProfileTimezoneConfig({
|
|
...DEFAULT_PROFILE_TIMEZONE_CONFIG,
|
|
enabled: true,
|
|
rollout_basis_points: 10000,
|
|
excluded_user_ids: [targetAccount.userId],
|
|
});
|
|
const updated = await updateProfileTimezone(harness, targetAccount.token, {timezone: TEST_TIMEZONE});
|
|
expect(updated.timezone).toBeNull();
|
|
});
|
|
it('lets members of an included guild set and show a timezone', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
const viewerAccount = await createTestAccount(harness);
|
|
const guild = await createGuild(harness, targetAccount.token, 'Timezone Rollout Guild');
|
|
await getInstanceConfigRepository().setProfileTimezoneConfig({
|
|
...DEFAULT_PROFILE_TIMEZONE_CONFIG,
|
|
enabled: true,
|
|
included_guild_ids: [guild.id],
|
|
});
|
|
const updated = await updateProfileTimezone(harness, targetAccount.token, {timezone: TEST_TIMEZONE});
|
|
expect(updated.timezone).toBe(TEST_TIMEZONE);
|
|
await createFriendship(harness, targetAccount, viewerAccount);
|
|
const profile = await getUserProfile(harness, viewerAccount.token, targetAccount.userId);
|
|
expect(profile.timezone_offset).toBe(TEST_TIMEZONE_OFFSET);
|
|
});
|
|
it('hides stored profile timezone after the user leaves the experiment', async () => {
|
|
const targetAccount = await createTestAccount(harness);
|
|
const viewerAccount = await createTestAccount(harness);
|
|
await setProfileTimezoneUsers([targetAccount.userId]);
|
|
await updateProfileTimezone(harness, targetAccount.token, {timezone: TEST_TIMEZONE});
|
|
await setProfileTimezoneUsers([]);
|
|
await createFriendship(harness, targetAccount, viewerAccount);
|
|
const profile = await getUserProfile(harness, viewerAccount.token, targetAccount.userId);
|
|
expect(profile.timezone_offset).toBeNull();
|
|
});
|
|
});
|