Files
netris-nestri/apps/auth/test/email.test.ts
Wanjohi 96b0cf8111 feat(auth): sign in with an email address
Wires the pin-code provider, which existed and was never reachable, and makes
it the only branch that can create an account. Steam now resolves an existing
connection instead of minting a user from a persona, and refuses when there is
no account behind it — which is an answer the interface renders rather than an
implicit signup.

Delivery is a small provider-neutral POST rather than a vendor SDK: configure
an endpoint, a key and a from address. With none of them set it logs the code
outside production so a local sign-in works, and throws in production, because
a screen that says "check your email" when nothing was sent leaves someone
waiting instead of telling anybody.

A person who has only ever signed in by email has no connected account, and
the token says so with an empty value — the same one a server-to-server caller
has always carried.
2026-09-05 00:02:16 +03:00

82 lines
2.5 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { sendVerificationCode } from '../src/email.js';
describe('sending a sign-in code', () => {
test('with nothing configured outside production, it does not block a sign-in', async () => {
await sendVerificationCode({ NODE_ENV: 'development' }, 'ada@example.com', '123456');
});
test('with nothing configured in production, it says so instead of pretending', async () => {
await expect(
sendVerificationCode({ NODE_ENV: 'production' }, 'ada@example.com', '123456')
).rejects.toThrow(/not configured/);
});
test('a configured mailer is called with the address and the code', async () => {
let seen: { url: string; body: any; auth: string | null } | null = null;
const original = globalThis.fetch;
globalThis.fetch = (async (url: any, init: any) => {
seen = {
url: String(url),
body: JSON.parse(init.body),
auth: new Headers(init.headers).get('authorization')
};
return new Response('{}', { status: 200 });
}) as unknown as typeof fetch;
try {
await sendVerificationCode(
{
NODE_ENV: 'production',
EMAIL_SEND_URL: 'https://mail.example.com/send',
EMAIL_API_KEY: 'key',
EMAIL_FROM: 'hello@nestri.io'
},
'ada@example.com',
'123456'
);
} finally {
globalThis.fetch = original;
}
expect(seen!.url).toBe('https://mail.example.com/send');
expect(seen!.auth).toBe('Bearer key');
expect(seen!.body.to).toEqual(['ada@example.com']);
expect(seen!.body.from).toBe('hello@nestri.io');
expect(seen!.body.text).toContain('123456');
});
test('a refusal from the mailer is not swallowed', async () => {
const original = globalThis.fetch;
globalThis.fetch = (async () =>
new Response('over quota', { status: 429 })) as unknown as typeof fetch;
try {
await expect(
sendVerificationCode(
{
EMAIL_SEND_URL: 'https://mail.example.com/send',
EMAIL_API_KEY: 'key',
EMAIL_FROM: 'hello@nestri.io'
},
'ada@example.com',
'123456'
)
).rejects.toThrow(/over quota/);
} finally {
globalThis.fetch = original;
}
});
});
describe('the worker itself', () => {
// Cheap, and it catches the thing a type check cannot: the sign-in screen
// lives in a `.tsx` file, and whether that file can be imported across a
// package boundary at run time is decided by the package's export map
// rather than by the compiler.
test('loads, with every provider it wires resolvable', async () => {
const worker = await import('../src/index.js');
expect(typeof worker.default.fetch).toBe('function');
});
});