mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-06 16:07:14 +08:00
629 lines
26 KiB
TypeScript
629 lines
26 KiB
TypeScript
import { expect, test, type APIRequestContext } from '@playwright/test';
|
|
import {
|
|
WORKER_URL,
|
|
createTestAddress,
|
|
deleteAddress,
|
|
getAddressSender,
|
|
hashPassword,
|
|
} from '../../fixtures/test-helpers';
|
|
|
|
const ADMIN_HEADERS = { 'x-admin-auth': 'e2e-admin-pass' };
|
|
|
|
type RedeemType = 'role' | 'send_balance' | 'address_prefix_once';
|
|
type AddressRedeemResult = {
|
|
type: 'address_prefix_once';
|
|
address: string;
|
|
address_id: number;
|
|
jwt: string;
|
|
password?: string;
|
|
};
|
|
|
|
const uniqueValue = (label: string) => (
|
|
`${label}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
);
|
|
|
|
async function createCode(
|
|
request: APIRequestContext,
|
|
type: RedeemType,
|
|
value: string,
|
|
options: { enabled?: boolean; expiresAt?: string } = {},
|
|
) {
|
|
const response = await request.post(`${WORKER_URL}/admin/redeem_codes/batch`, {
|
|
headers: ADMIN_HEADERS,
|
|
data: {
|
|
count: 1,
|
|
redeem_type: type,
|
|
value,
|
|
enabled: options.enabled ?? true,
|
|
expires_at: options.expiresAt ?? new Date(Date.now() + 3_600_000).toISOString(),
|
|
},
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
const body = await response.json();
|
|
expect(body.codes).toHaveLength(1);
|
|
return body.codes[0] as string;
|
|
}
|
|
|
|
async function listCode(request: APIRequestContext, type: RedeemType, code: string) {
|
|
const response = await request.get(
|
|
`${WORKER_URL}/admin/redeem_codes?redeem_type=${type}`
|
|
+ `&limit=100&offset=0&query=${encodeURIComponent(code)}`,
|
|
{ headers: ADMIN_HEADERS },
|
|
);
|
|
expect(response.ok()).toBe(true);
|
|
const body = await response.json();
|
|
return body.results.find((row: { code: string }) => row.code === code);
|
|
}
|
|
|
|
async function deleteCode(request: APIRequestContext, type: RedeemType, code: string) {
|
|
const row = await listCode(request, type, code);
|
|
if (!row) return;
|
|
const response = await request.delete(`${WORKER_URL}/admin/redeem_codes/${row.id}`, {
|
|
headers: ADMIN_HEADERS,
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
}
|
|
|
|
async function queryCode(request: APIRequestContext, code: unknown) {
|
|
return await request.post(`${WORKER_URL}/redeem_api/query`, { data: { code } });
|
|
}
|
|
|
|
async function queryResult(request: APIRequestContext, code: unknown) {
|
|
return await request.post(`${WORKER_URL}/redeem_api/result`, { data: { code } });
|
|
}
|
|
|
|
async function createUser(request: APIRequestContext) {
|
|
const email = `${uniqueValue('redeem-user')}@test.example.com`;
|
|
const createResponse = await request.post(`${WORKER_URL}/admin/users`, {
|
|
headers: ADMIN_HEADERS,
|
|
data: { email, password: hashPassword('redeem-user-password') },
|
|
});
|
|
expect(createResponse.ok()).toBe(true);
|
|
const listResponse = await request.get(
|
|
`${WORKER_URL}/admin/users?limit=10&offset=0&query=${encodeURIComponent(email)}`,
|
|
{ headers: ADMIN_HEADERS },
|
|
);
|
|
const user = (await listResponse.json()).results.find(
|
|
(row: { user_email: string }) => row.user_email === email,
|
|
);
|
|
expect(user).toBeTruthy();
|
|
await setUserRole(request, user.id, null);
|
|
return { id: user.id as number, email };
|
|
}
|
|
|
|
async function setUserRole(request: APIRequestContext, userId: number, role: string | null) {
|
|
const response = await request.post(`${WORKER_URL}/admin/user_roles`, {
|
|
headers: ADMIN_HEADERS,
|
|
data: { user_id: userId, role_text: role },
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
}
|
|
|
|
async function deleteUser(request: APIRequestContext, userId: number) {
|
|
await setUserRole(request, userId, null);
|
|
const response = await request.delete(`${WORKER_URL}/admin/users/${userId}`, {
|
|
headers: ADMIN_HEADERS,
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
}
|
|
|
|
test.describe('Redemption code query and guards', () => {
|
|
test('previews each supported type without consuming it', async ({ request }) => {
|
|
const roleCode = await createCode(request, 'role', 'case-role');
|
|
const balanceCode = await createCode(request, 'send_balance', '12');
|
|
const addressCode = await createCode(request, 'address_prefix_once', 'vip');
|
|
try {
|
|
const roleResponse = await queryCode(request, roleCode);
|
|
expect(roleResponse.ok()).toBe(true);
|
|
expect(await roleResponse.json()).toEqual({
|
|
redeem_type: 'role', value: 'case-role', status: 'unused',
|
|
});
|
|
|
|
const balanceResponse = await queryCode(request, balanceCode);
|
|
expect(balanceResponse.ok()).toBe(true);
|
|
expect(await balanceResponse.json()).toEqual({
|
|
redeem_type: 'send_balance', value: '12', status: 'unused',
|
|
});
|
|
|
|
const addressResponse = await queryCode(request, addressCode);
|
|
expect(addressResponse.ok()).toBe(true);
|
|
expect(await addressResponse.json()).toEqual({
|
|
redeem_type: 'address_prefix_once',
|
|
value: 'vip',
|
|
status: 'unused',
|
|
});
|
|
|
|
expect((await listCode(request, 'role', roleCode)).redeemed_at).toBeNull();
|
|
expect((await listCode(request, 'send_balance', balanceCode)).redeemed_at).toBeNull();
|
|
expect((await listCode(request, 'address_prefix_once', addressCode)).redeemed_at).toBeNull();
|
|
expect((await listCode(request, 'role', roleCode)).redeemed).toBe(false);
|
|
expect((await listCode(request, 'send_balance', balanceCode)).redeemed).toBe(false);
|
|
expect((await listCode(request, 'address_prefix_once', addressCode)).redeemed).toBe(false);
|
|
expect((await queryResult(request, addressCode)).status()).toBe(400);
|
|
} finally {
|
|
await deleteCode(request, 'role', roleCode);
|
|
await deleteCode(request, 'send_balance', balanceCode);
|
|
await deleteCode(request, 'address_prefix_once', addressCode);
|
|
}
|
|
});
|
|
|
|
test('rejects missing, malformed, unknown, and disabled codes, but reports expired codes', async ({ request }) => {
|
|
const disabledCode = await createCode(request, 'role', 'case-role', { enabled: false });
|
|
const expiredCode = await createCode(request, 'role', 'case-role', {
|
|
expiresAt: new Date(Date.now() + 5_000).toISOString(),
|
|
});
|
|
try {
|
|
await new Promise((resolve) => setTimeout(resolve, 5_500));
|
|
for (const code of [undefined, null, '', 'x'.repeat(257), uniqueValue('missing')]) {
|
|
expect((await queryCode(request, code)).status()).toBe(400);
|
|
}
|
|
expect((await queryCode(request, disabledCode)).status()).toBe(400);
|
|
const expiredResponse = await queryCode(request, expiredCode);
|
|
expect(expiredResponse.ok()).toBe(true);
|
|
expect(await expiredResponse.json()).toEqual({
|
|
redeem_type: 'role', value: 'case-role', status: 'expired',
|
|
});
|
|
} finally {
|
|
await deleteCode(request, 'role', disabledCode);
|
|
await deleteCode(request, 'role', expiredCode);
|
|
}
|
|
});
|
|
|
|
test('rejects malformed JSON request bodies', async ({ request }) => {
|
|
const options = {
|
|
headers: { 'content-type': 'application/json' },
|
|
data: '{',
|
|
};
|
|
for (const path of ['/query', '/result', '/redeem']) {
|
|
const response = await request.post(`${WORKER_URL}/redeem_api${path}`, options);
|
|
expect(response.status()).toBe(400);
|
|
}
|
|
});
|
|
|
|
test('uses the code type and does not consume when its required target is missing', async ({ request }) => {
|
|
const code = await createCode(request, 'send_balance', '9');
|
|
try {
|
|
const mismatchResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, user_email: 'nobody@test.example.com' },
|
|
});
|
|
expect(mismatchResponse.status()).toBe(400);
|
|
expect((await queryCode(request, code)).ok()).toBe(true);
|
|
expect((await listCode(request, 'send_balance', code)).redeemed_at).toBeNull();
|
|
} finally {
|
|
await deleteCode(request, 'send_balance', code);
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Role redemption', () => {
|
|
test('concurrent different roles consume only the winning code', async ({ request }) => {
|
|
const user = await createUser(request);
|
|
const roles = ['case-role', 'empty-role'];
|
|
const codes = await Promise.all(roles.map((role) => createCode(request, 'role', role)));
|
|
try {
|
|
const responses = await Promise.all(codes.map((code) => (
|
|
request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, user_email: user.email },
|
|
})
|
|
)));
|
|
expect(responses.filter((response) => response.ok())).toHaveLength(1);
|
|
expect(responses.filter((response) => response.status() === 409)).toHaveLength(1);
|
|
const winner = responses.findIndex((response) => response.ok());
|
|
const loser = 1 - winner;
|
|
const rows = await Promise.all(codes.map((code) => listCode(request, 'role', code)));
|
|
expect(rows[winner].redeemed).toBe(true);
|
|
expect(rows[loser]).toMatchObject({ redeemed: false, result: null, redeemed_at: null });
|
|
const users = await request.get(
|
|
`${WORKER_URL}/admin/users?limit=10&offset=0&query=${encodeURIComponent(user.email)}`,
|
|
{ headers: ADMIN_HEADERS },
|
|
);
|
|
expect((await users.json()).results[0].role_text).toBe(roles[winner]);
|
|
} finally {
|
|
for (const code of codes) await deleteCode(request, 'role', code);
|
|
await deleteUser(request, user.id);
|
|
}
|
|
});
|
|
|
|
test('applies a configured role by case-insensitive account email and consumes once', async ({ request }) => {
|
|
const user = await createUser(request);
|
|
const code = await createCode(request, 'role', 'case-role');
|
|
try {
|
|
const response = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, user_email: user.email.toUpperCase() },
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
expect(await response.json()).toEqual({
|
|
success: true,
|
|
type: 'role',
|
|
role: 'case-role',
|
|
user_email: user.email,
|
|
});
|
|
|
|
const userResponse = await request.get(
|
|
`${WORKER_URL}/admin/users?limit=10&offset=0&query=${encodeURIComponent(user.email)}`,
|
|
{ headers: ADMIN_HEADERS },
|
|
);
|
|
expect((await userResponse.json()).results[0].role_text).toBe('case-role');
|
|
|
|
const row = await listCode(request, 'role', code);
|
|
expect(row.redeemed).toBe(true);
|
|
expect(row.redeemed_at).toEqual(expect.any(String));
|
|
expect(JSON.parse(row.result)).toMatchObject({
|
|
type: 'role',
|
|
user_id: user.id,
|
|
user_email: user.email,
|
|
role: 'case-role',
|
|
});
|
|
const publicResult = await queryResult(request, code);
|
|
expect(publicResult.ok()).toBe(true);
|
|
expect(await publicResult.json()).toEqual({
|
|
type: 'role', role: 'case-role', user_email: user.email,
|
|
});
|
|
|
|
const repeatedResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, user_email: user.email },
|
|
});
|
|
expect(repeatedResponse.status()).toBe(400);
|
|
expect(await (await queryCode(request, code)).json()).toEqual({
|
|
redeem_type: 'role', value: 'case-role', status: 'redeemed',
|
|
});
|
|
} finally {
|
|
await deleteCode(request, 'role', code);
|
|
await deleteUser(request, user.id);
|
|
}
|
|
});
|
|
|
|
test('accepts the same existing role', async ({ request }) => {
|
|
const user = await createUser(request);
|
|
const code = await createCode(request, 'role', 'case-role');
|
|
try {
|
|
await setUserRole(request, user.id, 'case-role');
|
|
const response = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, user_email: user.email },
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
} finally {
|
|
await deleteCode(request, 'role', code);
|
|
await deleteUser(request, user.id);
|
|
}
|
|
});
|
|
|
|
test('keeps the code unused when the account is missing or has a conflicting role', async ({ request }) => {
|
|
const missingCode = await createCode(request, 'role', 'case-role');
|
|
const conflictCode = await createCode(request, 'role', 'case-role');
|
|
const user = await createUser(request);
|
|
try {
|
|
const missingResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: missingCode, user_email: `${uniqueValue('missing')}@test.example.com` },
|
|
});
|
|
expect(missingResponse.status()).toBe(400);
|
|
expect((await queryCode(request, missingCode)).ok()).toBe(true);
|
|
|
|
await setUserRole(request, user.id, 'empty-role');
|
|
const conflictResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: conflictCode, user_email: user.email },
|
|
});
|
|
expect(conflictResponse.status()).toBe(409);
|
|
expect((await queryCode(request, conflictCode)).ok()).toBe(true);
|
|
expect((await listCode(request, 'role', conflictCode)).result).toBeNull();
|
|
} finally {
|
|
await deleteCode(request, 'role', missingCode);
|
|
await deleteCode(request, 'role', conflictCode);
|
|
await deleteUser(request, user.id);
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Sending-credit redemption', () => {
|
|
test('preserves default credits before the mailbox is first opened', async ({ request }) => {
|
|
const address = await createTestAddress(request, 'rcfresh');
|
|
const code = await createCode(request, 'send_balance', '7');
|
|
try {
|
|
const before = await request.get(
|
|
`${WORKER_URL}/admin/address_sender?limit=1&offset=0&address=${encodeURIComponent(address.address)}`,
|
|
{ headers: ADMIN_HEADERS },
|
|
);
|
|
expect((await before.json()).results).toHaveLength(0);
|
|
const response = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, address: address.address },
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
expect(await response.json()).toMatchObject({ amount: 7, balance: 17 });
|
|
for (let index = 0; index < 2; index++) {
|
|
const settings = await request.get(`${WORKER_URL}/api/settings`, {
|
|
headers: { Authorization: `Bearer ${address.jwt}` },
|
|
});
|
|
expect(settings.ok()).toBe(true);
|
|
}
|
|
expect((await getAddressSender(request, address.address)).balance).toBe(17);
|
|
} finally {
|
|
await deleteCode(request, 'send_balance', code);
|
|
await deleteAddress(request, address.jwt);
|
|
}
|
|
});
|
|
|
|
test('coalesces NULL balance, preserves disabled state, records result, and consumes once', async ({ request }) => {
|
|
const address = await createTestAddress(request, 'rcnull');
|
|
const code = await createCode(request, 'send_balance', '7');
|
|
try {
|
|
const settingsResponse = await request.get(`${WORKER_URL}/api/settings`, {
|
|
headers: { Authorization: `Bearer ${address.jwt}` },
|
|
});
|
|
expect(settingsResponse.ok()).toBe(true);
|
|
const sender = await getAddressSender(request, address.address);
|
|
const nullResponse = await request.post(`${WORKER_URL}/admin/address_sender`, {
|
|
headers: ADMIN_HEADERS,
|
|
data: {
|
|
address: address.address,
|
|
address_id: sender.id,
|
|
balance: null,
|
|
enabled: false,
|
|
},
|
|
});
|
|
expect(nullResponse.ok()).toBe(true);
|
|
|
|
const redeemResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, address: address.address.toUpperCase() },
|
|
});
|
|
expect(redeemResponse.ok()).toBe(true);
|
|
expect(await redeemResponse.json()).toEqual({
|
|
success: true,
|
|
type: 'send_balance',
|
|
address: address.address,
|
|
amount: 7,
|
|
balance: 7,
|
|
});
|
|
|
|
const updatedSender = await getAddressSender(request, address.address);
|
|
expect(updatedSender.balance).toBe(7);
|
|
expect(updatedSender.enabled).toBe(0);
|
|
expect(JSON.parse((await listCode(request, 'send_balance', code)).result)).toMatchObject({
|
|
type: 'send_balance',
|
|
address: address.address,
|
|
amount: 7,
|
|
});
|
|
const publicResult = await queryResult(request, code);
|
|
expect(publicResult.ok()).toBe(true);
|
|
expect(await publicResult.json()).toEqual({
|
|
type: 'send_balance', address: address.address, amount: 7,
|
|
});
|
|
|
|
const repeatedResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, address: address.address },
|
|
});
|
|
expect(repeatedResponse.status()).toBe(400);
|
|
expect(await (await queryCode(request, code)).json()).toEqual({
|
|
redeem_type: 'send_balance', value: '7', status: 'redeemed',
|
|
});
|
|
} finally {
|
|
await deleteCode(request, 'send_balance', code);
|
|
await deleteAddress(request, address.jwt);
|
|
}
|
|
});
|
|
|
|
test('does not consume when the target address is missing', async ({ request }) => {
|
|
const code = await createCode(request, 'send_balance', '5');
|
|
try {
|
|
const response = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, address: `${uniqueValue('missing')}@test.example.com` },
|
|
});
|
|
expect(response.status()).toBe(400);
|
|
expect((await queryCode(request, code)).ok()).toBe(true);
|
|
expect((await listCode(request, 'send_balance', code)).result).toBeNull();
|
|
} finally {
|
|
await deleteCode(request, 'send_balance', code);
|
|
}
|
|
});
|
|
|
|
test('concurrent requests add the balance exactly once', async ({ request }) => {
|
|
const address = await createTestAddress(request, 'rcrace');
|
|
const code = await createCode(request, 'send_balance', '5');
|
|
try {
|
|
await request.get(`${WORKER_URL}/api/settings`, {
|
|
headers: { Authorization: `Bearer ${address.jwt}` },
|
|
});
|
|
const before = await getAddressSender(request, address.address);
|
|
const responses = await Promise.all([
|
|
request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, address: address.address },
|
|
}),
|
|
request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, address: address.address },
|
|
}),
|
|
]);
|
|
expect(responses.filter((response) => response.ok())).toHaveLength(1);
|
|
expect(responses.filter((response) => [400, 409].includes(response.status()))).toHaveLength(1);
|
|
expect((await getAddressSender(request, address.address)).balance).toBe(before.balance + 5);
|
|
} finally {
|
|
await deleteCode(request, 'send_balance', code);
|
|
await deleteAddress(request, address.jwt);
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe('Special-address redemption', () => {
|
|
test('uses the redemption prefix without the system prefix and repeats the complete original result', async ({ request }) => {
|
|
const code = await createCode(request, 'address_prefix_once', 'vip');
|
|
const name = `a${Math.random().toString(36).slice(2, 8)}`;
|
|
let result: AddressRedeemResult | undefined;
|
|
try {
|
|
const firstResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, name, domain: 'TEST.EXAMPLE.COM' },
|
|
});
|
|
expect(firstResponse.ok()).toBe(true);
|
|
result = await firstResponse.json();
|
|
expect(result).toMatchObject({
|
|
type: 'address_prefix_once',
|
|
address: `vip${name}@test.example.com`,
|
|
address_id: expect.any(Number),
|
|
jwt: expect.any(String),
|
|
password: expect.any(String),
|
|
});
|
|
expect(result.address).not.toContain('tmpvip');
|
|
|
|
const repeatedResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, name: 'ignored', domain: 'manual.example.com' },
|
|
});
|
|
expect(repeatedResponse.ok()).toBe(true);
|
|
expect(await repeatedResponse.json()).toEqual(result);
|
|
|
|
const previewResponse = await queryCode(request, code);
|
|
expect(previewResponse.ok()).toBe(true);
|
|
const preview = await previewResponse.json();
|
|
expect(preview).toEqual({
|
|
redeem_type: 'address_prefix_once', value: 'vip', status: 'redeemed',
|
|
});
|
|
const publicResult = await queryResult(request, code);
|
|
expect(publicResult.ok()).toBe(true);
|
|
expect(await publicResult.json()).toEqual({
|
|
type: 'address_prefix_once',
|
|
address: result.address,
|
|
jwt: result.jwt,
|
|
password: result.password,
|
|
});
|
|
const row = await listCode(request, 'address_prefix_once', code);
|
|
expect(JSON.parse(row.result)).toEqual(result);
|
|
} finally {
|
|
if (result?.jwt) await deleteAddress(request, result.jwt);
|
|
await deleteCode(request, 'address_prefix_once', code);
|
|
}
|
|
});
|
|
|
|
test('supports an empty prefix and the longest usable prefix', async ({ request }) => {
|
|
const emptyCode = await createCode(request, 'address_prefix_once', '');
|
|
const maxCode = await createCode(request, 'address_prefix_once', 'a'.repeat(29));
|
|
const emptyName = `n${Math.random().toString(36).slice(2, 8)}`;
|
|
let emptyResult: AddressRedeemResult | undefined;
|
|
let maxResult: AddressRedeemResult | undefined;
|
|
try {
|
|
const emptyResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: emptyCode, name: emptyName, domain: 'test.example.com' },
|
|
});
|
|
expect(emptyResponse.ok()).toBe(true);
|
|
emptyResult = await emptyResponse.json();
|
|
expect(emptyResult.address).toBe(`${emptyName}@test.example.com`);
|
|
|
|
const maxResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: maxCode, name: 'z', domain: 'test.example.com' },
|
|
});
|
|
expect(maxResponse.ok()).toBe(true);
|
|
maxResult = await maxResponse.json();
|
|
expect(maxResult.address.split('@')[0]).toBe(`${'a'.repeat(29)}z`);
|
|
} finally {
|
|
if (emptyResult?.jwt) await deleteAddress(request, emptyResult.jwt);
|
|
if (maxResult?.jwt) await deleteAddress(request, maxResult.jwt);
|
|
await deleteCode(request, 'address_prefix_once', emptyCode);
|
|
await deleteCode(request, 'address_prefix_once', maxCode);
|
|
}
|
|
});
|
|
|
|
test('keeps length and domain validation without consuming failed codes', async ({ request }) => {
|
|
const lengthCode = await createCode(request, 'address_prefix_once', 'a'.repeat(29));
|
|
const domainCode = await createCode(request, 'address_prefix_once', 'vip');
|
|
try {
|
|
const lengthResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: lengthCode, name: 'zz', domain: 'test.example.com' },
|
|
});
|
|
expect(lengthResponse.status()).toBe(400);
|
|
|
|
const domainResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: domainCode, name: 'valid', domain: 'invalid.example.com' },
|
|
});
|
|
expect(domainResponse.status()).toBe(400);
|
|
|
|
expect((await queryCode(request, lengthCode)).ok()).toBe(true);
|
|
expect((await queryCode(request, domainCode)).ok()).toBe(true);
|
|
} finally {
|
|
await deleteCode(request, 'address_prefix_once', lengthCode);
|
|
await deleteCode(request, 'address_prefix_once', domainCode);
|
|
}
|
|
});
|
|
|
|
test('generates a valid random suffix when the user omits the name', async ({ request }) => {
|
|
const code = await createCode(request, 'address_prefix_once', 'rnd');
|
|
let result: AddressRedeemResult | undefined;
|
|
try {
|
|
const response = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, domain: 'test.example.com' },
|
|
});
|
|
expect(response.ok()).toBe(true);
|
|
result = await response.json();
|
|
const localPart = result.address.split('@')[0];
|
|
expect(localPart.startsWith('rnd')).toBe(true);
|
|
expect(localPart.length).toBeLessThanOrEqual(30);
|
|
expect(localPart.length).toBeGreaterThan(3);
|
|
} finally {
|
|
if (result?.jwt) await deleteAddress(request, result.jwt);
|
|
await deleteCode(request, 'address_prefix_once', code);
|
|
}
|
|
});
|
|
|
|
test('does not return an address result after its original address is removed', async ({ request }) => {
|
|
const code = await createCode(request, 'address_prefix_once', 'removed');
|
|
try {
|
|
const firstResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, name: 'mail', domain: 'test.example.com' },
|
|
});
|
|
const first = await firstResponse.json();
|
|
await deleteAddress(request, first.jwt);
|
|
|
|
const repeatResponse = await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, name: 'ignored', domain: 'test.example.com' },
|
|
});
|
|
expect(repeatResponse.status()).toBe(400);
|
|
const resultResponse = await request.post(`${WORKER_URL}/redeem_api/result`, {
|
|
data: { code },
|
|
});
|
|
expect(resultResponse.status()).toBe(400);
|
|
} finally {
|
|
await deleteCode(request, 'address_prefix_once', code);
|
|
}
|
|
});
|
|
|
|
test('concurrent creations converge on one complete result', async ({ request }) => {
|
|
const code = await createCode(request, 'address_prefix_once', 'race');
|
|
let result: AddressRedeemResult | undefined;
|
|
try {
|
|
const responses = await Promise.all([
|
|
request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, name: `a${Math.random().toString(36).slice(2, 7)}`, domain: 'test.example.com' },
|
|
}),
|
|
request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code, name: `b${Math.random().toString(36).slice(2, 7)}`, domain: 'test.example.com' },
|
|
}),
|
|
]);
|
|
expect(responses.every((response) => response.ok())).toBe(true);
|
|
const results = await Promise.all(responses.map((response) => response.json()));
|
|
expect(results[1]).toEqual(results[0]);
|
|
result = results[0];
|
|
expect(result.password).toEqual(expect.any(String));
|
|
expect(JSON.parse((await listCode(request, 'address_prefix_once', code)).result)).toEqual(result);
|
|
} finally {
|
|
if (result?.jwt) await deleteAddress(request, result.jwt);
|
|
await deleteCode(request, 'address_prefix_once', code);
|
|
}
|
|
});
|
|
|
|
test('expiry stops retrieval of an existing result', async ({ request }) => {
|
|
const expiredCode = await createCode(request, 'address_prefix_once', 'exp', {
|
|
expiresAt: new Date(Date.now() + 5_000).toISOString(),
|
|
});
|
|
let expiredResult: AddressRedeemResult | undefined;
|
|
try {
|
|
expiredResult = await (await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: expiredCode, name: 'mail', domain: 'test.example.com' },
|
|
})).json();
|
|
await new Promise((resolve) => setTimeout(resolve, 5_500));
|
|
|
|
expect((await request.post(`${WORKER_URL}/redeem_api/redeem`, {
|
|
data: { code: expiredCode },
|
|
})).status()).toBe(400);
|
|
expect(await (await queryCode(request, expiredCode)).json()).toMatchObject({ status: 'expired' });
|
|
expect((await queryResult(request, expiredCode)).status()).toBe(400);
|
|
} finally {
|
|
if (expiredResult?.jwt) await deleteAddress(request, expiredResult.jwt);
|
|
await deleteCode(request, 'address_prefix_once', expiredCode);
|
|
}
|
|
});
|
|
});
|