diff --git a/server/src/nest/mail-ingest/mail-ingest-address.service.ts b/server/src/nest/mail-ingest/mail-ingest-address.service.ts index 9f751b229..974ab39a2 100644 --- a/server/src/nest/mail-ingest/mail-ingest-address.service.ts +++ b/server/src/nest/mail-ingest/mail-ingest-address.service.ts @@ -43,6 +43,18 @@ export class MailIngestAddressService { return this.view(await this.addresses.findLive(userId, null)); } + /** + * The person's own address as the settings card shows it: made on the first + * look once an address template is set and their import is on, so nobody has + * to ask for one before forwarding; read as it is otherwise. + * + * @txStandalone an idempotent lazy provision: one row, made once, then only read. + */ + async ownAddressForCard(userId: number, importOn: boolean): Promise { + if (importOn && (await this.settings.addressTemplate())) return this.view(await this.ensureOwn(userId)); + return this.ownAddress(userId); + } + /** A new own address for the person, the old one stops working. */ async renewOwn(userId: number): Promise { await this.requireTemplate(); diff --git a/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts b/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts index 146095d1c..0e0eaa681 100644 --- a/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts +++ b/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts @@ -69,7 +69,7 @@ export class MailIngestInboxService { } return { available: true, - address: await this.addresses.ownAddress(userId), + address: await this.addresses.ownAddressForCard(userId, prefs.enabled), prefs, // Whether AI reading can work here at all (the AI Parsing addon and a connection), not the person's own // switch: with it folded in, switching AI off greyed the switch out and it could never come back on. diff --git a/server/tests/e2e/mail-ingest.e2e.test.ts b/server/tests/e2e/mail-ingest.e2e.test.ts index 9432396d0..7bc6814d1 100644 --- a/server/tests/e2e/mail-ingest.e2e.test.ts +++ b/server/tests/e2e/mail-ingest.e2e.test.ts @@ -164,7 +164,12 @@ describe('/api/mail-ingest e2e (real guards + snapshot SQLite)', () => { it('MAIL-E2E-002: the settings card, its switches and a renewed own address', async () => { const card = await request(server).get('/api/mail-ingest/settings').set(as(USER)); expect(card.status).toBe(200); - expect(card.body).toMatchObject({ available: true, address: null, ai_available: false, pending: 0 }); + expect(card.body).toMatchObject({ available: true, ai_available: false, pending: 0 }); + // The first look makes the person's own address, so nobody has to ask for one before forwarding. + const first = card.body.address.address as string; + expect(first).toMatch(/^trek\+[a-z2-7]{24}@mail\.example\.com$/); + const again = await request(server).get('/api/mail-ingest/settings').set(as(USER)); + expect(again.body.address.address).toBe(first); expect((await request(server).patch('/api/mail-ingest/settings').set(as(USER)).send({})).status).toBe(400); const off = await request(server).patch('/api/mail-ingest/settings').set(as(USER)).send({ auto_add: false }); @@ -174,6 +179,7 @@ describe('/api/mail-ingest e2e (real guards + snapshot SQLite)', () => { const renewed = await request(server).post('/api/mail-ingest/address/renew').set(as(USER)); expect(renewed.status).toBe(200); expect(renewed.body.address.address).toMatch(/^trek\+[a-z2-7]{24}@mail\.example\.com$/); + expect(renewed.body.address.address).not.toBe(first); }); it('MAIL-E2E-003: an uploaded mail waits in the inbox, is added by hand and undone, for its person', async () => {