mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
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.
82 lines
2.5 KiB
TypeScript
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');
|
|
});
|
|
});
|