This repository has no description
Something went wrong. Try again.
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430// SPDX-License-Identifier: AGPL-3.0-or-later
import type {GuildAdminResponse} from '@fluxer/schema/src/domains/admin/AdminGuildSchemas';import type {UserAdminResponse} from '@fluxer/schema/src/domains/admin/AdminUserSchemas';import {afterEach, beforeEach, describe, expect, test} from 'vitest';import {createTestAccount, setUserACLs} from '../../auth/tests/AuthTestUtils';import {createGuild} from '../../channel/tests/ChannelTestUtils';import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';import {HTTP_STATUS} from '../../test/TestConstants';import {createBuilder, createBuilderWithoutAuth} from '../../test/TestRequestBuilder';
interface UserSearchResponse { users: Array<UserAdminResponse>; total: number;}
interface GuildSearchResponse { guilds: Array<GuildAdminResponse>; total: number;}
async function setContactInfo( harness: ApiTestHarness, userId: string, data: { has_verified_phone?: boolean; email?: string | null; },): Promise<void> { await createBuilderWithoutAuth(harness) .post(`/test/users/${userId}/set-contact-info`) .body(data) .expect(HTTP_STATUS.OK) .execute();}
async function setLastActiveIp(harness: ApiTestHarness, token: string, ip: string): Promise<void> { await createBuilder(harness, `${token}`) .get('/users/@me') .header('x-forwarded-for', ip) .expect(HTTP_STATUS.OK) .execute();}
describe('Admin Search Field Coverage', () => { let harness: ApiTestHarness; beforeEach(async () => { harness = await createApiTestHarness({search: 'enabled'}); }); afterEach(async () => { await harness.shutdown(); }); describe('user search by email', () => { test('searching by exact email returns only the matching user', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup', 'user:view:email']); const uniqueEmail = `precise-email-${Date.now()}@searchtest.example`; const targetUser = await createTestAccount(harness, {email: uniqueEmail}); await createTestAccount(harness, {email: `other-user-${Date.now()}@different.example`}); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: uniqueEmail, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const found = result.users.find((u) => u.id === targetUser.userId); expect(found).toBeDefined(); expect(found!.email).toBe(uniqueEmail); }); test('searching by email does not return users with different emails', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const emailA = `alpha-${Date.now()}@emailsearch.example`; const emailB = `bravo-${Date.now()}@emailsearch.example`; const userA = await createTestAccount(harness, {email: emailA}); const userB = await createTestAccount(harness, {email: emailB}); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: emailA, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const foundA = result.users.find((u) => u.id === userA.userId); const foundB = result.users.find((u) => u.id === userB.userId); expect(foundA).toBeDefined(); expect(foundB).toBeUndefined(); }); test('searching by partial email domain returns matching users', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const domain = `partialdomain${Date.now()}.example`; const user = await createTestAccount(harness, {email: `user@${domain}`}); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: domain, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const found = result.users.find((u) => u.id === user.userId); expect(found).toBeDefined(); }); }); describe('user search by username', () => { test('searching by exact username returns only the matching user', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const uniqueUsername = `xuniq_${Date.now()}`; const targetUser = await createTestAccount(harness, {username: uniqueUsername}); await createTestAccount(harness, {username: `yother_${Date.now()}`}); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: uniqueUsername, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const found = result.users.find((u) => u.id === targetUser.userId); expect(found).toBeDefined(); expect(found!.username).toBe(uniqueUsername); }); test('searching by username does not return users with completely different usernames', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const ts = Date.now(); const usernameA = `zephyrfox_${ts}`; const usernameB = `quasarmoon_${ts}`; const userA = await createTestAccount(harness, {username: usernameA}); const userB = await createTestAccount(harness, {username: usernameB}); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: usernameA, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const foundA = result.users.find((u) => u.id === userA.userId); const foundB = result.users.find((u) => u.id === userB.userId); expect(foundA).toBeDefined(); expect(foundB).toBeUndefined(); }); }); describe('user search by user ID', () => { test('searching by exact user ID returns only the matching user', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const targetUser = await createTestAccount(harness); await createTestAccount(harness); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: targetUser.userId, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const found = result.users.find((u) => u.id === targetUser.userId); expect(found).toBeDefined(); expect(found!.id).toBe(targetUser.userId); }); test('user ID search returns the user even when the search index has no match via direct DB lookup', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const targetUser = await createTestAccount(harness); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: targetUser.userId, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const found = result.users.find((u) => u.id === targetUser.userId); expect(found).toBeDefined(); }); test('user ID search does not duplicate user when found by both search index and direct lookup', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const targetUser = await createTestAccount(harness); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: targetUser.userId, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const matchingUsers = result.users.filter((u) => u.id === targetUser.userId); expect(matchingUsers).toHaveLength(1); }); }); describe('user search by last active IP', () => { test('searching by last active IP returns users that share that IP', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup', 'user:view:ip']); const matchingIp = '198.51.100.24'; const nonMatchingIp = '203.0.113.24'; const targetUser = await createTestAccount(harness); const otherUser = await createTestAccount(harness); await setLastActiveIp(harness, targetUser.token, matchingIp); await setLastActiveIp(harness, otherUser.token, nonMatchingIp); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({last_active_ip: matchingIp, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); expect(result.users.find((u) => u.id === targetUser.userId)).toBeDefined(); expect(result.users.find((u) => u.id === otherUser.userId)).toBeUndefined(); }); }); describe('user search response fields', () => { test('user search response includes all expected admin fields', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup', 'user:view:email']); const email = `fields-check-${Date.now()}@fieldtest.example`; const username = `fieldcheck_${Date.now()}`; const targetUser = await createTestAccount(harness, {email, username}); await setContactInfo(harness, targetUser.userId, {has_verified_phone: true}); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: email, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const found = result.users.find((u) => u.id === targetUser.userId); expect(found).toBeDefined(); expect(found!.id).toBe(targetUser.userId); expect(found!.username).toBe(username); expect(found!.email).toBe(email); expect(found!.has_verified_phone).toBe(true); expect(found!).toHaveProperty('discriminator'); expect(found!).toHaveProperty('global_name'); expect(found!).toHaveProperty('flags'); expect(found!).toHaveProperty('email_verified'); expect(found!).toHaveProperty('email_bounced'); expect(found!).toHaveProperty('premium_type'); expect(found!).toHaveProperty('acls'); expect(found!).toHaveProperty('suspicious_activity_flags'); }); }); describe('user search isolation across fields', () => { test('each searchable field returns only the correct user', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const ts = Date.now(); const emailA = `vortexfind-${ts}@alphadomain.example`; const emailB = `nebulaseek-${ts}@betadomain.example`; const usernameA = `vortexfox_${ts}`; const usernameB = `nebulawolf_${ts}`; const userA = await createTestAccount(harness, {email: emailA, username: usernameA}); const userB = await createTestAccount(harness, {email: emailB, username: usernameB}); const searchByEmailA = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: emailA, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(searchByEmailA.users.find((u) => u.id === userA.userId)).toBeDefined(); expect(searchByEmailA.users.find((u) => u.id === userB.userId)).toBeUndefined(); const searchByUsernameB = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: usernameB, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(searchByUsernameB.users.find((u) => u.id === userB.userId)).toBeDefined(); expect(searchByUsernameB.users.find((u) => u.id === userA.userId)).toBeUndefined(); const searchByIdB = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: userB.userId, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(searchByIdB.users.find((u) => u.id === userB.userId)).toBeDefined(); expect(searchByIdB.users.find((u) => u.id === userA.userId)).toBeUndefined(); }); }); describe('guild search by name', () => { test('searching by exact guild name returns only the matching guild', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const ts = Date.now(); const nameA = `Zephyrion Stronghold ${ts}`; const nameB = `Quasarwave Citadel ${ts}`; const guildA = await createGuild(harness, admin.token, nameA); const guildB = await createGuild(harness, admin.token, nameB); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: nameA, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const foundA = result.guilds.find((g) => g.id === guildA.id); const foundB = result.guilds.find((g) => g.id === guildB.id); expect(foundA).toBeDefined(); expect(foundA!.name).toBe(nameA); expect(foundB).toBeUndefined(); }); test('searching by guild name does not return guilds with different names', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const ts = Date.now(); const uniqueNameA = `Xanthium ${ts}`; const uniqueNameB = `Ytterbium ${ts}`; const guildA = await createGuild(harness, admin.token, uniqueNameA); const guildB = await createGuild(harness, admin.token, uniqueNameB); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: uniqueNameA, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const foundA = result.guilds.find((g) => g.id === guildA.id); const foundB = result.guilds.find((g) => g.id === guildB.id); expect(foundA).toBeDefined(); expect(foundB).toBeUndefined(); }); }); describe('guild search by ID', () => { test('searching by exact guild ID returns only the matching guild', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const guildA = await createGuild(harness, admin.token, `ID Search A ${Date.now()}`); const guildB = await createGuild(harness, admin.token, `ID Search B ${Date.now()}`); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: guildA.id, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); const foundA = result.guilds.find((g) => g.id === guildA.id); const foundB = result.guilds.find((g) => g.id === guildB.id); expect(foundA).toBeDefined(); expect(foundA!.id).toBe(guildA.id); expect(foundB).toBeUndefined(); }); test('guild ID search at non-zero offset does not perform direct DB lookup', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const guild = await createGuild(harness, admin.token, `Offset Guild ${Date.now()}`); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: guild.id, limit: 10, offset: 1}) .expect(HTTP_STATUS.OK) .execute(); expect(result.guilds).toEqual([]); expect(result.total).toBe(0); }); }); describe('guild search response fields', () => { test('guild search response includes all expected admin fields', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const guildName = `Field Check Guild ${Date.now()}`; const guild = await createGuild(harness, admin.token, guildName); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: guildName, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const found = result.guilds.find((g) => g.id === guild.id); expect(found).toBeDefined(); expect(found!.id).toBe(guild.id); expect(found!.name).toBe(guildName); expect(found!.owner_id).toBe(admin.userId); expect(found!).toHaveProperty('features'); expect(found!).toHaveProperty('icon'); expect(found!).toHaveProperty('banner'); expect(found!).toHaveProperty('member_count'); expect(found!.member_count).toBeGreaterThanOrEqual(1); }); test('guild search returns correct owner_id for each guild', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const otherOwner = await createTestAccount(harness); const ts = Date.now(); const guildByAdmin = await createGuild(harness, admin.token, `Admin Owned ${ts}`); const guildByOther = await createGuild(harness, otherOwner.token, `Other Owned ${ts}`); const resultAdmin = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: `Admin Owned ${ts}`, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const foundAdmin = resultAdmin.guilds.find((g) => g.id === guildByAdmin.id); expect(foundAdmin).toBeDefined(); expect(foundAdmin!.owner_id).toBe(admin.userId); const resultOther = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: `Other Owned ${ts}`, limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); const foundOther = resultOther.guilds.find((g) => g.id === guildByOther.id); expect(foundOther).toBeDefined(); expect(foundOther!.owner_id).toBe(otherOwner.userId); }); }); describe('omitted and no-match queries', () => { test('omitted query on user search returns users', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); await createTestAccount(harness); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); expect(result.users.length).toBeGreaterThanOrEqual(1); }); test('omitted query on guild search returns guilds', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); await createGuild(harness, admin.token, `Omitted Query Guild ${Date.now()}`); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.total).toBeGreaterThanOrEqual(1); expect(result.guilds.length).toBeGreaterThanOrEqual(1); }); test('completely unrelated query returns no users', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); const result = await createBuilder<UserSearchResponse>(harness, `${admin.token}`) .post('/admin/users/search') .body({query: 'zzz-impossible-match-query-xyzzy-99999', limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.users).toEqual([]); expect(result.total).toBe(0); }); test('completely unrelated query returns no guilds', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'guild:lookup']); const result = await createBuilder<GuildSearchResponse>(harness, `${admin.token}`) .post('/admin/guilds/search') .body({query: 'zzz-impossible-match-query-xyzzy-99999', limit: 10, offset: 0}) .expect(HTTP_STATUS.OK) .execute(); expect(result.guilds).toEqual([]); expect(result.total).toBe(0); }); });});