Files
przeprogramowani__10x-cli/tests/e2e/support/resend-magic-links.ts
psmyrdek 95a8d4ec5e test(e2e): stop logging third-party recipients in resend poller
The first-poll diagnostic dumped the 5 most recent emails from the
shared Resend account, including their `to` field, leaking unrelated
customer addresses into CI logs. Replace with a counts-only summary
and drop the recipient from the timeout error message.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:56:54 +02:00

140 lines
3.9 KiB
TypeScript

interface EmailListItem {
id: string;
to: string[];
created_at: string;
subject: string;
[key: string]: unknown;
}
interface EmailDetail {
html: string;
[key: string]: unknown;
}
interface FindCallbackUrlOptions {
recipientEmail: string;
sentAfter: Date;
timeoutMs?: number;
pollIntervalMs?: number;
}
const DEFAULT_TIMEOUT_MS = 30_000;
const DEFAULT_POLL_INTERVAL_MS = 500;
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function normalizeEmail(email: string): string {
return email.trim().toLowerCase();
}
function extractCallbackUrl(html: string): string | null {
const pattern =
/https?:\/\/[^"'<>\s]+\/auth\/callback\?[^"'<>\s]+/i;
const match = html.match(pattern);
return match ? match[0].replace(/&amp;/g, "&") : null;
}
export class ResendMagicLinks {
private readonly apiKey: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async findCallbackUrl(options: FindCallbackUrlOptions): Promise<string> {
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
const deadline = Date.now() + timeoutMs;
const normalizedRecipient = normalizeEmail(options.recipientEmail);
let lastError: unknown;
let pollCount = 0;
while (Date.now() < deadline) {
try {
const listResponse = await fetch(
"https://api.resend.com/emails?limit=50",
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
},
},
);
if (!listResponse.ok) {
throw new Error(
`Resend list failed: ${listResponse.status} ${listResponse.statusText}`,
);
}
const listData = (await listResponse.json()) as {
data: EmailListItem[];
};
const allEmails = listData.data ?? [];
if (pollCount === 0) {
const matchCount = allEmails.filter((e) =>
(e.to ?? []).map(normalizeEmail).includes(normalizedRecipient),
).length;
console.log(
`[resend-poll] polling after=${options.sentAfter.toISOString()}, account inbox=${allEmails.length}, matches=${matchCount}`,
);
}
const candidates = allEmails
.filter((email) => {
const createdAt = new Date(email.created_at);
if (Number.isNaN(createdAt.getTime())) return false;
if (createdAt < options.sentAfter) return false;
const recipients = (email.to ?? []).map(normalizeEmail);
return recipients.includes(normalizedRecipient);
})
.sort((a, b) => {
return (
new Date(b.created_at).getTime() -
new Date(a.created_at).getTime()
);
});
for (const candidate of candidates) {
const getResponse = await fetch(
`https://api.resend.com/emails/${candidate.id}`,
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
},
},
);
if (!getResponse.ok) {
throw new Error(
`Resend get failed for ${candidate.id}: ${getResponse.status}`,
);
}
const emailDetail = (await getResponse.json()) as EmailDetail;
const html = typeof emailDetail.html === "string" ? emailDetail.html : "";
const callbackUrl = extractCallbackUrl(html);
if (callbackUrl) {
return callbackUrl;
}
}
} catch (error) {
lastError = error;
}
pollCount++;
await sleep(pollIntervalMs);
}
const suffix =
lastError instanceof Error ? ` Last error: ${lastError.message}` : "";
throw new Error(
`Timed out after ${timeoutMs}ms waiting for auth callback email. ` +
`Polled ${pollCount} times.${suffix}`,
);
}
}