mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-08-28 19:48:01 +08:00
* perf: paginate user addresses and optimize mail ownership queries * docs: document user address pagination * fix: address pagination review feedback * fix: cover user address pagination flows * test: fix user mailbox tab selector * test: stabilize remote address search flow * test: stabilize user address browser flow * fix: preserve address pagination compatibility * refactor: simplify bound address query types * refactor: reuse list query for bound addresses * fix: preserve paginated address totals * fix: preserve bound address helper contracts * fix: require pagination for user addresses * refactor: keep shared pagination behavior unchanged * fix: align pagination docs and tests * fix: clear stale address selections * refactor: simplify user address pagination * refactor: limit user address changes to pagination * test: select a visible mailbox address * fix: preserve bound address response fields
171 lines
6.6 KiB
TypeScript
171 lines
6.6 KiB
TypeScript
import { test, expect, type APIRequestContext } from '@playwright/test';
|
|
import {
|
|
WORKER_URL,
|
|
createTestAddress,
|
|
deleteAddress,
|
|
hashPassword,
|
|
seedTestMail,
|
|
} from '../../fixtures/test-helpers';
|
|
|
|
async function createUser(request: APIRequestContext) {
|
|
const email = `address-page-${Date.now()}@test.example.com`;
|
|
const password = hashPassword('test-password-123');
|
|
|
|
const registerRes = await request.post(`${WORKER_URL}/user_api/register`, {
|
|
data: { email, password },
|
|
});
|
|
expect(registerRes.ok()).toBe(true);
|
|
|
|
const loginRes = await request.post(`${WORKER_URL}/user_api/login`, {
|
|
data: { email, password },
|
|
});
|
|
expect(loginRes.ok()).toBe(true);
|
|
const { jwt } = await loginRes.json();
|
|
const payload = JSON.parse(Buffer.from(jwt.split('.')[1], 'base64url').toString('utf8'));
|
|
return { jwt, userId: payload.user_id as number };
|
|
}
|
|
|
|
test.describe('User address pagination', () => {
|
|
test('paginates addresses and enforces mail ownership', async ({ request }) => {
|
|
const addresses: Awaited<ReturnType<typeof createTestAddress>>[] = [];
|
|
let outsider: Awaited<ReturnType<typeof createTestAddress>> | undefined;
|
|
let originalUserSettings: Record<string, unknown> | undefined;
|
|
let userId: number | undefined;
|
|
|
|
try {
|
|
const settingsRes = await request.get(`${WORKER_URL}/admin/user_settings`);
|
|
expect(settingsRes.ok()).toBe(true);
|
|
originalUserSettings = await settingsRes.json();
|
|
const enableUserRes = await request.post(`${WORKER_URL}/admin/user_settings`, {
|
|
data: {
|
|
...originalUserSettings,
|
|
enable: true,
|
|
enableMailVerify: false,
|
|
maxAddressCount: 0,
|
|
},
|
|
});
|
|
expect(enableUserRes.ok()).toBe(true);
|
|
|
|
const user = await createUser(request);
|
|
const userJwt = user.jwt;
|
|
userId = user.userId;
|
|
addresses.push(...await Promise.all([
|
|
createTestAddress(request, 'user-page-a'),
|
|
createTestAddress(request, 'user-page-b'),
|
|
createTestAddress(request, 'user-page-c'),
|
|
]));
|
|
outsider = await createTestAddress(request, 'user-page-outsider');
|
|
|
|
for (const item of addresses) {
|
|
const bindRes = await request.post(`${WORKER_URL}/user_api/bind_address`, {
|
|
headers: {
|
|
Authorization: `Bearer ${item.jwt}`,
|
|
'x-user-token': userJwt,
|
|
},
|
|
});
|
|
expect(bindRes.ok()).toBe(true);
|
|
}
|
|
|
|
const defaultPageRes = await request.get(`${WORKER_URL}/user_api/bind_address`, {
|
|
headers: { 'x-user-token': userJwt },
|
|
});
|
|
expect(defaultPageRes.ok()).toBe(true);
|
|
const defaultPage = await defaultPageRes.json();
|
|
expect(defaultPage.count).toBe(3);
|
|
expect(defaultPage.results).toHaveLength(3);
|
|
|
|
const firstPageRes = await request.get(
|
|
`${WORKER_URL}/user_api/bind_address?limit=2&offset=0`,
|
|
{ headers: { 'x-user-token': userJwt } },
|
|
);
|
|
expect(firstPageRes.ok()).toBe(true);
|
|
const firstPage = await firstPageRes.json();
|
|
expect(firstPage.count).toBe(3);
|
|
expect(firstPage.results).toHaveLength(2);
|
|
expect(firstPage.results[0].mail_count).toBe(0);
|
|
expect(firstPage.results[0].send_count).toBe(0);
|
|
expect(firstPage.results[0]).toHaveProperty('source_meta');
|
|
expect(firstPage.results[0]).not.toHaveProperty('password');
|
|
|
|
const secondPageRes = await request.get(
|
|
`${WORKER_URL}/user_api/bind_address?limit=2&offset=2`,
|
|
{ headers: { 'x-user-token': userJwt } },
|
|
);
|
|
expect(secondPageRes.ok()).toBe(true);
|
|
const secondPage = await secondPageRes.json();
|
|
expect(secondPage.count).toBe(0);
|
|
expect(secondPage.results).toHaveLength(1);
|
|
|
|
const invalidLimitRes = await request.get(
|
|
`${WORKER_URL}/user_api/bind_address?limit=101&offset=0`,
|
|
{ headers: { 'x-user-token': userJwt } },
|
|
);
|
|
expect(invalidLimitRes.status()).toBe(400);
|
|
|
|
const invalidOffsetRes = await request.get(
|
|
`${WORKER_URL}/user_api/bind_address?limit=20&offset=-1`,
|
|
{ headers: { 'x-user-token': userJwt } },
|
|
);
|
|
expect(invalidOffsetRes.status()).toBe(400);
|
|
|
|
await seedTestMail(request, addresses[0].address, { subject: 'Bound mail' });
|
|
await seedTestMail(request, outsider.address, { subject: 'Outsider mail' });
|
|
|
|
const userMailsRes = await request.get(`${WORKER_URL}/user_api/mails?limit=20&offset=0`, {
|
|
headers: { 'x-user-token': userJwt },
|
|
});
|
|
expect(userMailsRes.ok()).toBe(true);
|
|
const userMails = await userMailsRes.json();
|
|
expect(userMails.count).toBe(1);
|
|
expect(userMails.results[0].address).toBe(addresses[0].address);
|
|
|
|
const filteredOutsiderMailsRes = await request.get(
|
|
`${WORKER_URL}/user_api/mails?limit=20&offset=0&address=${encodeURIComponent(outsider.address)}`,
|
|
{ headers: { 'x-user-token': userJwt } },
|
|
);
|
|
expect(filteredOutsiderMailsRes.ok()).toBe(true);
|
|
const filteredOutsiderMails = await filteredOutsiderMailsRes.json();
|
|
expect(filteredOutsiderMails.count).toBe(0);
|
|
expect(filteredOutsiderMails.results).toHaveLength(0);
|
|
|
|
const outsiderMailsRes = await request.get(`${WORKER_URL}/api/mails?limit=20&offset=0`, {
|
|
headers: { Authorization: `Bearer ${outsider.jwt}` },
|
|
});
|
|
expect(outsiderMailsRes.ok()).toBe(true);
|
|
const outsiderMails = await outsiderMailsRes.json();
|
|
expect(outsiderMails.results).toHaveLength(1);
|
|
|
|
const forbiddenDeleteRes = await request.delete(
|
|
`${WORKER_URL}/user_api/mails/${outsiderMails.results[0].id}`,
|
|
{ headers: { 'x-user-token': userJwt } },
|
|
);
|
|
expect(forbiddenDeleteRes.ok()).toBe(true);
|
|
|
|
const outsiderAfterRes = await request.get(`${WORKER_URL}/api/mails?limit=20&offset=0`, {
|
|
headers: { Authorization: `Bearer ${outsider.jwt}` },
|
|
});
|
|
expect(outsiderAfterRes.ok()).toBe(true);
|
|
const outsiderAfter = await outsiderAfterRes.json();
|
|
expect(outsiderAfter.results).toHaveLength(1);
|
|
} finally {
|
|
try {
|
|
await Promise.allSettled(
|
|
[...addresses, outsider].filter((item) => item !== undefined)
|
|
.map((item) => deleteAddress(request, item.jwt)),
|
|
);
|
|
if (userId !== undefined) {
|
|
const deleteUserRes = await request.delete(`${WORKER_URL}/admin/users/${userId}`);
|
|
expect(deleteUserRes.ok()).toBe(true);
|
|
}
|
|
} finally {
|
|
if (originalUserSettings) {
|
|
const restoreSettingsRes = await request.post(`${WORKER_URL}/admin/user_settings`, {
|
|
data: originalUserSettings,
|
|
});
|
|
expect(restoreSettingsRes.ok()).toBe(true);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|