|
28 | 28 | * gets; the server-side phone reset call (no `Request`) still awaits its |
29 | 29 | * send; |
30 | 30 | * - `runAfterResponse` hands a task's synchronous throw to `onFailure` and |
31 | | - * survives an `onFailure` that throws. |
| 31 | + * survives an `onFailure` that throws; |
| 32 | + * - the public config says whether mail leaves the deployment |
| 33 | + * (`features.emailDelivery`): false with no email service and with a |
| 34 | + * transport that reports itself unconfigured (the stock `log` one), true |
| 35 | + * with a configured one. It describes the deployment, not an address: in |
| 36 | + * all three a known and an unknown address get the same reset answer, and |
| 37 | + * that answer is the same one whether mail leaves or not. |
32 | 38 | */ |
33 | 39 |
|
34 | 40 | import { describe, it, expect, afterAll, afterEach, vi } from 'vitest'; |
@@ -297,6 +303,45 @@ describe('the phone-number reset request answers without waiting on the reset te |
297 | 303 | }, 60_000); |
298 | 304 | }); |
299 | 305 |
|
| 306 | +describe('the public config says whether a reset link can be emailed from this deployment', () => { |
| 307 | + /** The deployment's one account, then the email service under test swapped in. */ |
| 308 | + async function withEmailService(service: Record<string, unknown> | undefined) { |
| 309 | + const d = await deployment(async () => ({ status: 'sent' })); |
| 310 | + d.manager.setEmailService(service as never); |
| 311 | + const config = d.manager.getPublicConfig() as { features: Record<string, unknown> }; |
| 312 | + const forAccount = await requestReset(d.manager, EMAIL); |
| 313 | + const forNobody = await requestReset(d.manager, NOBODY); |
| 314 | + return { emailDelivery: config.features.emailDelivery, forAccount, forNobody }; |
| 315 | + } |
| 316 | + |
| 317 | + const sendTemplate = async () => ({ status: 'sent' }); |
| 318 | + |
| 319 | + it('no email service: no delivery, and a known and an unknown address get the same answer', async () => { |
| 320 | + const r = await withEmailService(undefined); |
| 321 | + |
| 322 | + expect(r.emailDelivery).toBe(false); |
| 323 | + expect(r.forAccount.status).toBe(200); |
| 324 | + expect(r.forAccount).toEqual(r.forNobody); |
| 325 | + }, 60_000); |
| 326 | + |
| 327 | + it('a transport that reports itself unconfigured (the log one): no delivery, same answer for both addresses', async () => { |
| 328 | + const r = await withEmailService({ sendTemplate, isConfigured: () => false }); |
| 329 | + |
| 330 | + expect(r.emailDelivery).toBe(false); |
| 331 | + expect(r.forAccount.status).toBe(200); |
| 332 | + expect(r.forAccount).toEqual(r.forNobody); |
| 333 | + }, 60_000); |
| 334 | + |
| 335 | + it('a configured transport: delivery, and the reset answer is the one a deployment without mail gives', async () => { |
| 336 | + const live = await withEmailService({ sendTemplate, isConfigured: () => true }); |
| 337 | + const none = await withEmailService(undefined); |
| 338 | + |
| 339 | + expect(live.emailDelivery).toBe(true); |
| 340 | + expect(live.forAccount).toEqual(live.forNobody); |
| 341 | + expect(live.forAccount).toEqual(none.forAccount); |
| 342 | + }, 60_000); |
| 343 | +}); |
| 344 | + |
300 | 345 | describe('runAfterResponse', () => { |
301 | 346 | it("hands a task's synchronous throw to onFailure", async () => { |
302 | 347 | const failures: unknown[] = []; |
|
0 commit comments