diff --git a/src/queues/email-send.worker.test.ts b/src/queues/email-send.worker.test.ts index c599936..f07cd65 100644 --- a/src/queues/email-send.worker.test.ts +++ b/src/queues/email-send.worker.test.ts @@ -65,10 +65,15 @@ describe('processEmailSendJob', () => { expect(clickhouse.insert).toHaveBeenCalledWith( expect.objectContaining({ values: expect.arrayContaining([ - expect.objectContaining({ event_type: 'suppressed' }), + expect.objectContaining({ + event_type: 'suppressed', + recipient_hash: 'abc123hash', + }), ]), }) ) + const insertCall = vi.mocked(clickhouse.insert).mock.calls[0][0] + expect(JSON.stringify(insertCall.values)).not.toContain('empfaenger@example.com') }) it('gibt err zurück wenn Kampagne nicht gefunden', async () => { @@ -117,4 +122,15 @@ describe('processEmailSendJob', () => { }) ) }) + + it('ClickHouse-Fehler nach erfolgreichem SMTP führt nicht zu err (verhindert Doppelversand)', async () => { + vi.mocked(getCampaign).mockResolvedValue({ ok: true, data: mockCampaign }) + vi.mocked(checkSuppression).mockResolvedValue(false) + vi.mocked(sendEmail).mockResolvedValue({ ok: true, data: undefined }) + vi.mocked(clickhouse.insert).mockRejectedValueOnce(new Error('ClickHouse down')) + + const result = await processEmailSendJob(jobData) + + expect(result.ok).toBe(true) + }) }) diff --git a/src/queues/email-send.worker.ts b/src/queues/email-send.worker.ts index 820c2b3..8b97232 100644 --- a/src/queues/email-send.worker.ts +++ b/src/queues/email-send.worker.ts @@ -14,7 +14,7 @@ export async function processEmailSendJob(data: EmailSendJobData): Promise { - await clickhouse.insert({ - table: 'email_events', - values: [ - { +async function safeInsertEvent( + eventType: 'sent' | 'suppressed', + data: EmailSendJobData +): Promise { + try { + await clickhouse.insert({ + table: 'email_events', + values: [{ event_type: eventType, tenant_id: data.tenantId, campaign_id: data.campaignId, - // Datenschutz: nur Hash wird gespeichert — keine Klartext-E-Mail-Adresse in ClickHouse recipient_hash: data.recipientHash, timestamp: new Date().toISOString(), metadata: {}, - }, - ], - format: 'JSONEachRow', - }) + }], + format: 'JSONEachRow', + }) + } catch (e) { + console.error(JSON.stringify({ level: 'warn', msg: 'ClickHouse insert fehlgeschlagen', error: String(e) })) + } } const connection = {