mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-29 10:40:59 +00:00
59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
import { readFileSync, statSync } from 'node:fs';
|
|
import { E2E } from '../../playwright.config';
|
|
|
|
/**
|
|
* Reading one-time codes back out of the API's own output.
|
|
*
|
|
* There is no local SMS gateway, and the code is not recoverable anywhere else:
|
|
* `iam.user_verifications.verification_code` holds an argon2 hash, and the
|
|
* notification rows it is delivered through store an empty body. The log line
|
|
* the message composer emits is the only plaintext.
|
|
*
|
|
* Codes are matched by *position in the log*, not by recipient — the line names
|
|
* no user. Callers take an offset before the action that triggers the send and
|
|
* only read what was written after it, which is sound because the suite runs
|
|
* single-worker and serially.
|
|
*/
|
|
|
|
const OTP_PATTERN = /is (\d{4,8})\./g;
|
|
|
|
/** Byte offset to read from later. Zero when the log does not exist yet. */
|
|
export function logOffset(): number {
|
|
try {
|
|
return statSync(E2E.apiLog).size;
|
|
} catch {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/** Waits for a code to appear after `offset`, and returns the last one seen. */
|
|
export async function waitForOtp(
|
|
offset: number,
|
|
timeoutMs = 20_000,
|
|
): Promise<string> {
|
|
const deadline = Date.now() + timeoutMs;
|
|
|
|
while (Date.now() < deadline) {
|
|
const found = otpSince(offset);
|
|
if (found) return found;
|
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
}
|
|
|
|
throw new Error(
|
|
`No one-time code appeared in ${E2E.apiLog} within ${timeoutMs}ms. ` +
|
|
`Is the API teeing its output there?`,
|
|
);
|
|
}
|
|
|
|
function otpSince(offset: number): string | null {
|
|
let text: string;
|
|
try {
|
|
text = readFileSync(E2E.apiLog, 'utf8').slice(offset);
|
|
} catch {
|
|
return null;
|
|
}
|
|
|
|
const codes = [...text.matchAll(OTP_PATTERN)].map((m) => m[1]);
|
|
return codes.length > 0 ? codes[codes.length - 1] : null;
|
|
}
|