// Entscheidungen vom 04.10.2026: Projekt-Aktionen fuer alle Mitarbeiter, archivierte Projekte im // Kundenportal ausgeblendet. (Private Repos: siehe projektAblauf.test.js, generate-Aufruf.) 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' const aw = await startAppwriteMock() const st = await startFakeStripe() useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) const { startTestApp, rawGet, get, send, sessionCookie } = await import('./support/testApp.js') const { default: projectsRouter } = await import('../server/routes/projects.js') const { default: servicesRouter } = await import('../server/routes/admin/services.js') const { default: customersRouter } = await import('../server/routes/admin/customers.js') const { default: websiteProjectsRouter } = await import('../server/routes/admin/websiteProjects.js') const { default: giteaRouter } = await import('../server/routes/admin/gitea.js') const { default: funktionenRouter } = await import('../server/routes/funktionen.js') const app = await startTestApp({ '/api/projects': projectsRouter, '/api/admin/services': servicesRouter, '/api/admin/customers': customersRouter, '/api/admin/website-projects': websiteProjectsRouter, '/api/admin/gitea': giteaRouter, '/api/funktionen': funktionenRouter, }) after(() => Promise.all([aw.close(), st.close(), app.close()])) aw.seed('websiteProjects', { $id: 'p_aktiv', customerId: 'c1', projectName: 'Aktiv', subdomain: 'aktiv', status: 'deployed' }) aw.seed('websiteProjects', { $id: 'p_archiv', customerId: 'c1', projectName: 'Alt', subdomain: 'alt', status: 'archived', archived: true }) const kunde = { cookie: sessionCookie({ customerId: 'c1', appwriteUserId: 'u1' }) } const bearer = (jwt) => ({ headers: { Authorization: `Bearer ${jwt}` } }) test('Kundenportal: archivierte Projekte stehen nicht in der Liste', async () => { const { projects } = await get('/api/projects', kunde) assert.deepEqual(projects.map((p) => p.id), ['p_aktiv']) }) test('Kundenportal: archiviertes Projekt auch per Direktaufruf nicht erreichbar', async () => { assert.equal((await rawGet('/api/projects/p_archiv/work', kunde)).status, 404) assert.equal((await rawGet('/api/projects/p_aktiv/work', kunde)).status, 200) }) test('Projekt-Routen: Mitarbeiter ohne Admin-Label kommen rein, Kunden-Konto nicht', async () => { assert.equal((await rawGet('/api/admin/services', bearer('jwt-staff'))).status, 200) assert.equal((await rawGet('/api/admin/services', bearer('jwt-admin'))).status, 200) const kundeKonto = await rawGet('/api/admin/services', bearer('jwt-kunde')) assert.equal(kundeKonto.status, 403) assert.equal(kundeKonto.body.error, 'Nur für Mitarbeiter') assert.equal((await rawGet('/api/admin/services')).status, 401) }) test('Kunden-Verwaltung bleibt nur fuer Admins', async () => { const r = await rawGet('/api/admin/customers', bearer('jwt-staff')) assert.equal(r.status, 403) assert.equal(r.body.error, 'Admin-Berechtigung erforderlich') }) test('Funktionen: Kunde sieht archivierte Projekte nicht, die Mitarbeiter-Ansicht schon', async () => { const { listCustomerFeatures } = await import('../server/services/siteFeatures.js') assert.deepEqual((await listCustomerFeatures('c1', { docs: [] })).map((p) => p.projectId), ['p_aktiv']) assert.deepEqual( (await listCustomerFeatures('c1', { docs: [], includeArchived: true })).map((p) => p.projectId).sort(), ['p_aktiv', 'p_archiv'] ) }) test('Funktionen: archiviertes Projekt mit laufender Funktion bleibt fuer den Kunden sichtbar (Buchungen verwalten)', async () => { const { listCustomerFeatures } = await import('../server/services/siteFeatures.js') const docs = [{ customerId: 'c1', projectId: 'p_archiv', featureKey: 'termine', state: 'active' }] assert.deepEqual((await listCustomerFeatures('c1', { docs })).map((p) => p.projectId).sort(), ['p_aktiv', 'p_archiv']) const aus = [{ customerId: 'c1', projectId: 'p_archiv', featureKey: 'termine', state: 'off' }] assert.deepEqual((await listCustomerFeatures('c1', { docs: aus })).map((p) => p.projectId), ['p_aktiv']) }) test('Neu bestellen auf archiviertem Projekt geht nicht', async () => { const r = await send('POST', '/api/funktionen/news/checkout', { ...kunde, body: { projectId: 'p_archiv', consent: true } }) assert.equal(r.status, 409) assert.match(r.body.error, /archiviert/) }) test('Vorlage bleibt Admin-Sache; README nur fuer bekannte Projekte', async () => { const t = await send('PATCH', '/api/admin/website-projects/p_aktiv', { ...bearer('jwt-staff'), body: { projectType: 'template' } }) assert.equal(t.status, 403) const fremd = await rawGet('/api/admin/gitea/repo-readme?repoFullName=JUSN/tickte-system', bearer('jwt-staff')) assert.equal(fremd.status, 404) })