diff --git a/src/data/repositories/settings.ts b/src/data/repositories/settings.ts index 7543eeb..387718a 100644 --- a/src/data/repositories/settings.ts +++ b/src/data/repositories/settings.ts @@ -1,4 +1,4 @@ -import { desc, eq, inArray } from 'drizzle-orm' +import { and, desc, eq, inArray, isNull } from 'drizzle-orm' import { db } from '../db' import { appSettings, reportRuns, type ReportRun } from '../schema' @@ -54,6 +54,17 @@ export async function markReportRunDeleted(id: string): Promise { await db.update(reportRuns).set({ deletedAt: new Date() }).where(eq(reportRuns.id, id)) } +export async function findLatestSentRun(): Promise { + const rows = await db + .select() + .from(reportRuns) + .where(and(eq(reportRuns.sent, true), isNull(reportRuns.deletedAt))) + .orderBy(desc(reportRuns.toAt)) + .limit(1) + + return rows[0] +} + export async function listReportRuns(limit: number): Promise { return db.select().from(reportRuns).orderBy(desc(reportRuns.createdAt)).limit(limit) } diff --git a/src/lib/admin-settings.ts b/src/lib/admin-settings.ts index e940396..1af926d 100644 --- a/src/lib/admin-settings.ts +++ b/src/lib/admin-settings.ts @@ -1,6 +1,7 @@ import { recordAudit } from '~/data/repositories/audit' import { clearSetting, + findLatestSentRun, findReportRun, markReportRunDeleted, readSettings, @@ -167,6 +168,14 @@ export async function performDeleteMessage(viewer: Viewer, formData: FormData): await markReportRunDeleted(run.id) + const previous = await findLatestSentRun() + + if (previous) { + await writeSetting('slack.lastSentAt', previous.toAt.toISOString()) + } else { + await clearSetting('slack.lastSentAt') + } + await recordAudit({ actorId: viewer.id, actorLabel: viewer.email, diff --git a/tests/lib/report-settings.test.ts b/tests/lib/report-settings.test.ts index 94db0bb..18746d1 100644 --- a/tests/lib/report-settings.test.ts +++ b/tests/lib/report-settings.test.ts @@ -3,6 +3,7 @@ import { db } from '~/data/db' import { appSettings } from '~/data/schema' import { listReportRuns, writeSetting } from '~/data/repositories/settings' import { readSlackConfig, reportScope, runWeeklyReport } from '~/lib/report-settings' +import { performDeleteMessage } from '~/lib/admin-settings' import { makeBrand, makeProject } from '../support/admin' import { makePost } from '../support/entries' @@ -129,10 +130,10 @@ describe('Doppelter Versand', () => { await makePost(project.id, { number: 1, slug: 'eins', status: 'published', publishAt: new Date() }) await writeSetting('slack.botToken', 'xoxb-1234') - await writeSetting('slack.channel', 'C123') + await writeSetting('slack.channel', 'C0123456') vi.stubGlobal('fetch', vi.fn(async () => new Response( - JSON.stringify({ ok: true, channel: 'C123', ts: '1785600000.000100' }), + JSON.stringify({ ok: true, channel: 'C0123456', ts: '1785600000.000100' }), { headers: { 'content-type': 'application/json' } }, ))) @@ -143,3 +144,37 @@ describe('Doppelter Versand', () => { expect(second.reason).toBe('nothing_published') }) }) + +describe('Löschen nimmt den Versand zurück', () => { + it('macht den Bericht nach dem Löschen wieder sendbar', async () => { + const brand = await makeBrand() + const project = await makeProject(brand.id) + + await makePost(project.id, { number: 1, slug: 'eins', status: 'published', publishAt: new Date() }) + await writeSetting('slack.botToken', 'xoxb-1234') + await writeSetting('slack.channel', 'C0123456') + + vi.stubGlobal('fetch', vi.fn(async () => new Response( + JSON.stringify({ ok: true, channel: 'C0123456', ts: '1785600000.000100' }), + { headers: { 'content-type': 'application/json' } }, + ))) + + const first = await runWeeklyReport({ origin, trigger: 'test' }) + + expect(first.reason).toBe('sent') + expect((await runWeeklyReport({ origin, trigger: 'test' })).reason).toBe('nothing_published') + + const form = new FormData() + + form.set('runId', first.run.id) + + const deleted = await performDeleteMessage( + { id: 'u1', name: 'Admin', email: 'admin@nyo.de', isAdmin: true, assignments: [] }, + form, + ) + + expect(deleted.message).toBe('messageDeleted') + expect(deleted.status).toBe('ok') + expect((await runWeeklyReport({ origin, trigger: 'test' })).reason).toBe('sent') + }) +})