import { test, after } from 'node:test' import assert from 'node:assert/strict' import os from 'node:os' import path from 'node:path' import { mkdtempSync } from 'node:fs' import sharp from 'sharp' import { startAppwriteMock } from './support/appwriteMock.js' import { startFakeStripe } from './support/fakeStripe.js' import { useTestEnv } from './support/testEnv.js' import { schemaKeys } from './support/schema.js' // Kundenansicht: News-Aenderungen eines Mitarbeiters landen mit Titel im Protokoll, die des Kunden nicht process.env.NEWS_UPLOAD_DIR = mkdtempSync(path.join(os.tmpdir(), 'kundenansicht-news-')) const aw = await startAppwriteMock({ schema: { customerViewLog: schemaKeys('customerViewLog'), portalFeatures: schemaKeys('portalFeatures'), newsPosts: schemaKeys('newsPosts'), }, }) const st = await startFakeStripe() useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) const { startTestApp } = await import('./support/testApp.js') const { seedFixtures, customerCookie, kundenansichtCookie } = await import('./support/fixtures.js') const { kundenansichtGuard } = await import('../server/middleware/kundenansichtGuard.js') const { default: newsRoutes } = await import('../server/routes/news.js') const app = await startTestApp({ '/': kundenansichtGuard, '/api/news': newsRoutes }) after(() => Promise.all([app.close(), aw.close(), st.close()])) seedFixtures(aw) aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Bäckerei' }) aw.seed('portalFeatures', { $id: 'f_p1_news', customerId: 'c1', projectId: 'p1', featureKey: 'news', state: 'active', enabled: true, siteKey: 'abcdefghijklmnop' }) const VIEW = 'a'.repeat(32) const staff = { cookie: kundenansichtCookie(), headers: { 'x-kundenansicht': VIEW } } const note = { projectId: 'p1', type: 'note', title: 'Hallo', text: 'Wir haben offen', paper: 'sun', duration: '1m' } const call = async (method, url, { cookie, headers = {}, body } = {}) => { const raw = Buffer.isBuffer(body) const res = await fetch(app.url + url, { method, headers: { ...(cookie ? { cookie } : {}), ...(body !== undefined && !raw ? { 'content-type': 'application/json' } : {}), ...headers }, body: body === undefined ? undefined : raw ? body : JSON.stringify(body), }) const text = await res.text() return { status: res.status, body: text ? JSON.parse(text) : null } } const logOf = (customerId) => [...aw.docs('customerViewLog').values()].filter((d) => d.customerId === customerId) const png = () => sharp({ create: { width: 40, height: 30, channels: 3, background: '#c33' } }).png().toBuffer() test('anlegen, aendern, Foto, loeschen in der Kundenansicht -> vier Protokolleintraege', async () => { const before = logOf('c1').length const created = await call('POST', '/api/news', { ...staff, body: note }) assert.equal(created.status, 201) const id = created.body.$id const patched = await call('PATCH', `/api/news/${id}`, { ...staff, body: { ...note, title: 'Neu' } }) assert.equal(patched.status, 200) const image = await call('POST', '/api/news/image?projectId=p1', { ...staff, headers: { ...staff.headers, 'content-type': 'application/octet-stream' }, body: await png(), }) assert.equal(image.status, 201) const removed = await call('DELETE', `/api/news/${id}?projectId=p1`, staff) assert.equal(removed.status, 204) const entries = logOf('c1').slice(before) assert.deepEqual(entries.map((e) => [e.action, e.detail]), [ ['news_angelegt', 'Hallo'], ['news_geaendert', 'Neu'], ['news_bild', ''], ['news_geloescht', 'Neu'], ]) for (const e of entries) assert.deepEqual([e.staffName, e.staffUserId], ['Andrej', 'u_staff2']) }) test('ohne Titel: Detail sind die ersten 60 Zeichen des Textes', async () => { const before = logOf('c1').length const text = 'Heute frische Brötchen bis 18 Uhr und morgen geschlossen wegen Inventur, danke!' const res = await call('POST', '/api/news', { ...staff, body: { ...note, title: '', text } }) assert.equal(res.status, 201) assert.equal(logOf('c1').slice(before)[0].detail, text.slice(0, 60)) }) test('Kunde selbst schreibt -> kein Protokolleintrag', async () => { const before = logOf('c1').length const res = await call('POST', '/api/news', { cookie: customerCookie('c1'), body: note }) assert.equal(res.status, 201) assert.equal(logOf('c1').length, before) }) test('Protokoll scheitert -> News-Aktion trotzdem 201', async (t) => { t.mock.method(console, 'warn', () => {}) aw.failNext({ collection: 'customerViewLog', method: 'POST', status: 500, message: 'kaputt' }) const res = await call('POST', '/api/news', { ...staff, body: note }) assert.equal(res.status, 201) })