import { test, after } from 'node:test' import assert from 'node:assert/strict' import { startAppwriteMock } from './support/appwriteMock.js' import { startFakeStripe } from './support/fakeStripe.js' import { useTestEnv } from './support/testEnv.js' import { schemaKeys } from './support/schema.js' // Protokoll der Kundenansicht (Collection customerViewLog): schreiben, lesen, nach 12 Monaten loeschen const aw = await startAppwriteMock({ schema: { customerViewLog: schemaKeys('customerViewLog') } }) const st = await startFakeStripe() useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) const { VIEW_ACTIONS, logViewAction, listViewLog, purgeViewLog } = await import('../server/services/customerViewLog.js') after(() => Promise.all([aw.close(), st.close()])) const DAY_MS = 24 * 60 * 60 * 1000 const entries = () => [...aw.docs('customerViewLog').values()] const seedLog = (id, customerId, createdAt, action = 'geoeffnet') => aw.seed('customerViewLog', { $id: id, customerId, staffUserId: 'u_staff2', staffName: 'Andrej', action, detail: '', createdAt }) test('VIEW_ACTIONS: genau die elf Aktionen', () => { assert.deepEqual(VIEW_ACTIONS, [ 'geoeffnet', 'beendet', 'abgelaufen', 'news_angelegt', 'news_bild', 'news_geaendert', 'news_geloescht', 'termin_einstellungen', 'termin_aktion', 'termin_manuell', 'shop_aenderung', ]) }) test('logViewAction schreibt alle Felder und kuerzt detail auf 300', async () => { const before = entries().length await logViewAction({ customerId: 'c9', staffUserId: 'u_staff2', staffName: 'Andrej', action: 'news_angelegt', detail: 'x'.repeat(400) }) const all = entries() assert.equal(all.length, before + 1) const doc = all.find((d) => d.customerId === 'c9') assert.equal(doc.detail.length, 300) assert.equal(doc.action, 'news_angelegt') assert.equal(doc.staffName, 'Andrej') assert.equal(doc.staffUserId, 'u_staff2') assert.ok(Math.abs(Date.parse(doc.createdAt) - Date.now()) < 5000) }) test('logViewAction wirft nicht, wenn Appwrite scheitert', async (t) => { const warn = t.mock.method(console, 'warn', () => {}) aw.failNext({ collection: 'customerViewLog', method: 'POST', status: 500, message: 'kaputt' }) await assert.doesNotReject(() => logViewAction({ customerId: 'c8', staffUserId: 'u', staffName: 'X', action: 'geoeffnet' })) assert.equal(entries().filter((d) => d.customerId === 'c8').length, 0) assert.equal(warn.mock.callCount(), 1) }) test('listViewLog: nur dieser Kunde, neueste zuerst, Form {id, at, staffName, action, detail}', async () => { seedLog('l1', 'c1', '2026-09-01T10:00:00.000Z') seedLog('l2', 'c1', '2026-09-03T10:00:00.000Z', 'news_angelegt') seedLog('l3', 'c1', '2026-09-02T10:00:00.000Z', 'beendet') seedLog('l4', 'c2', '2026-09-04T10:00:00.000Z') const list = await listViewLog('c1') assert.deepEqual(list.map((e) => e.id), ['l2', 'l3', 'l1']) assert.deepEqual(list[0], { id: 'l2', at: '2026-09-03T10:00:00.000Z', staffName: 'Andrej', action: 'news_angelegt', detail: '' }) }) test('purgeViewLog loescht nur Eintraege aelter als 365 Tage', async () => { const now = new Date('2026-10-01T12:00:00.000Z') seedLog('p1', 'c3', new Date(now - 366 * DAY_MS).toISOString()) seedLog('p2', 'c3', new Date(now - 364 * DAY_MS).toISOString()) seedLog('p3', 'c3', new Date(now - 1 * DAY_MS).toISOString()) const { deleted } = await purgeViewLog({ now }) assert.equal(deleted, 1) assert.deepEqual(entries().filter((d) => d.customerId === 'c3').map((d) => d.$id).sort(), ['p2', 'p3']) }) test('purgeViewLog arbeitet seitenweise (250 alte Eintraege -> deleted 250)', async () => { const now = new Date('2026-10-01T12:00:00.000Z') const old = new Date(now - 400 * DAY_MS).toISOString() for (let i = 0; i < 250; i += 1) seedLog(`old${i}`, 'c4', old) seedLog('keep', 'c4', now.toISOString()) const { deleted } = await purgeViewLog({ now }) assert.equal(deleted, 250) assert.deepEqual(entries().filter((d) => d.customerId === 'c4').map((d) => d.$id), ['keep']) })