import { test, after } from 'node:test' import assert from 'node:assert/strict' import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs' import path from 'node:path' import { Router } from 'express' import { startAppwriteMock } from './support/appwriteMock.js' import { startFakeStripe } from './support/fakeStripe.js' import { useTestEnv } from './support/testEnv.js' // Etappe 4: Admin-Panel, Rechnungsanalyse und alte Admin-Schnittstellen sind weg; was das Ticketsystem nutzt, bleibt const aw = await startAppwriteMock() const st = await startFakeStripe() useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) const { config } = await import('../server/config.js') const { startTestApp } = await import('./support/testApp.js') const { viewAsCookie } = await import('./support/fixtures.js') const { kundenansichtGuard } = await import('../server/middleware/kundenansichtGuard.js') // Hinter dem Waechter: eine Lese- und eine Schreib-Route, die selbst immer 200 liefern const probe = Router() probe.get('/', (_req, res) => res.json({ ok: true })) probe.post('/', (_req, res) => res.json({ ok: true })) const app = await startTestApp({ '/': kundenansichtGuard, '/api/projects': probe }) after(() => Promise.all([app.close(), aw.close(), st.close()])) const root = new URL('..', import.meta.url).pathname const clears = (res, name) => res.headers.getSetCookie().some((c) => c.startsWith(`${name}=`) && /Expires=Thu, 01 Jan 1970/.test(c)) function filesUnder(dir) { const out = [] for (const name of readdirSync(dir)) { const full = path.join(dir, name) if (statSync(full).isDirectory()) out.push(...filesUnder(full)) else if (/\.(js|mjs|html|css)$/.test(name)) out.push(full) } return out } test('Admin-Panel und alte Admin-Schnittstellen sind weg', () => { for (const f of [ 'public/admin.html', 'public/admin.js', 'public/kalkulation.html', 'public/kalkulation.js', 'public/kalkulation.css', 'server/routes/portalAdmin.js', 'server/routes/admin/switchLink.js', 'server/routes/admin/previewOpen.js', 'server/services/appointments.js', ]) { assert.equal(existsSync(path.join(root, f)), false, f) } const index = readFileSync(path.join(root, 'server/index.js'), 'utf8') for (const mount of ['/api/portal-admin', '/api/admin/switch-link', '/api/admin/preview-open']) assert.ok(!index.includes(`'${mount}'`), mount) for (const f of ['public/chat-composer.js', 'public/chat-composer.css', 'public/kundenansicht.js', 'server/services/staffPreviewLink.js']) { assert.ok(existsSync(path.join(root, f)), `${f} muss bleiben`) } }) test('keine Verweise mehr auf admin.html, kalkulation.html oder /api/portal-admin in server/ und public/', () => { // Mail-Vorlagen und ihre Links stellt Task 3 um (eigener Test in mailTemplates.test.js) const skip = [path.join(root, 'server/mail-templates/'), path.join(root, 'server/services/takeoverCredits.js')] const hits = [] for (const file of [...filesUnder(path.join(root, 'server')), ...filesUnder(path.join(root, 'public'))]) { if (skip.some((s) => file.startsWith(s))) continue readFileSync(file, 'utf8').split('\n').forEach((line, i) => { if (/admin\.html|kalkulation\.html|\/api\/portal-admin/.test(line)) hits.push(`${path.relative(root, file)}:${i + 1}`) }) } assert.deepEqual(hits, []) }) test('Alt-Ansicht ohne Kennung: Lesen und Schreiben 401 view_expired, Cookies geloescht', async () => { for (const method of ['GET', 'POST']) { const res = await fetch(`${app.url}/api/projects`, { method, headers: { cookie: viewAsCookie('c1'), ...(method === 'POST' ? { 'content-type': 'application/json' } : {}) }, body: method === 'POST' ? '{}' : undefined, }) assert.equal(res.status, 401, method) assert.equal((await res.json()).code, 'view_expired', method) assert.ok(clears(res, config.cookieName), `${method}: Portal-Cookie geloescht`) assert.ok(clears(res, 'preview_session'), `${method}: Preview-Cookie geloescht`) } })