From c6a9a2c4a6887f165be01f45bc590d752694b5c1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 13:01:40 +0000 Subject: [PATCH] Kundenansicht neu: /api/auth/me meldet ansicht neu oder alt Co-Authored-By: Claude Sonnet 5.5 --- server/routes/auth.js | 3 +++ test/kundenansichtMe.test.js | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 test/kundenansichtMe.test.js diff --git a/server/routes/auth.js b/server/routes/auth.js index 6ab0708..c264d06 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -41,6 +41,7 @@ import { } from '../services/kundenansicht.js' import { logViewAction } from '../services/customerViewLog.js' import { isStaffAccount } from '../services/staffLabels.js' +import { neueAnsichtFuerSession } from '../services/kundenansicht/schalter.js' const router = Router() @@ -407,6 +408,7 @@ router.get('/me', async (req, res) => { viewAs: true, viewId: session.viewId, viewExpiresAt: session.viewExpiresAt, + ansicht: neueAnsichtFuerSession(session) ? 'neu' : 'alt', admin: { name: session.adminName || 'Mitarbeiter', email: session.adminEmail || '', isAdmin: Boolean(session.isAdmin) }, customer: sanitizeCustomer(doc), }) @@ -463,6 +465,7 @@ router.get('/me', async (req, res) => { return res.json({ authenticated: true, role: 'customer', + ansicht: neueAnsichtFuerSession(session) ? 'neu' : 'alt', customer: sanitizeCustomer(customer), }) } catch (err) { diff --git a/test/kundenansichtMe.test.js b/test/kundenansichtMe.test.js new file mode 100644 index 0000000..3e85246 --- /dev/null +++ b/test/kundenansichtMe.test.js @@ -0,0 +1,48 @@ +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' + +// /api/auth/me meldet in beiden Antworten mit authenticated: true das Feld ansicht ('neu' | 'alt') nach dem Pilot-Schalter +const aw = await startAppwriteMock({ schema: { customerViewLog: schemaKeys('customerViewLog') } }) +const st = await startFakeStripe() +useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) +const { config } = await import('../server/config.js') +const { startTestApp, rawGet } = await import('./support/testApp.js') +const { customers, customerCookie, kundenansichtCookie, adminCookie, seedFixtures } = await import('./support/fixtures.js') +const { kundenansichtGuard } = await import('../server/middleware/kundenansichtGuard.js') +const { default: authRoutes } = await import('../server/routes/auth.js') +const app = await startTestApp({ '/': kundenansichtGuard, '/api/auth': authRoutes }) +after(() => Promise.all([app.close(), aw.close(), st.close()])) + +seedFixtures(aw) +// /me prueft bei Kunden Status und Portalzugang +for (const id of ['c1', 'c2']) { + aw.seed('customers', { ...customers[id], customerStatus: 'active' }) + aw.seed('customerPortalAccess', { $id: `pa_${id}`, customerId: id, enabled: true }) +} + +const stelle = (mode, pilotEmails = []) => Object.assign(config.kundenansichtNeu, { mode, pilotEmails }) +const me = async (cookie) => (await rawGet('/api/auth/me', { cookie })).body +const ansichtVon = async (cookie) => (await me(cookie)).ansicht + +test('Kunde: Pilot-Liste, off mit Stern, on', async () => { + const f = [['pilot', ['k@example.invalid'], customerCookie('c1'), 'neu'], ['pilot', ['k@example.invalid'], customerCookie('c2'), 'alt'], + ['off', ['*'], customerCookie('c1'), 'alt'], ['on', [], customerCookie('c2'), 'neu']] + for (const [modus, liste, cookie, soll] of f) { stelle(modus, liste); assert.equal(await ansichtVon(cookie), soll, `${modus} ${liste}`) } +}) +test('Mitarbeiter-Kundenansicht: im Pilot neu, mit off alt; viewAs und viewId bleiben', async () => { + stelle('pilot', []); const b = await me(kundenansichtCookie({ viewId: 'c'.repeat(32) })) + assert.deepEqual([b.authenticated, b.viewAs, b.viewId, b.ansicht], [true, true, 'c'.repeat(32), 'neu']) + stelle('off', ['*']); assert.equal(await ansichtVon(kundenansichtCookie()), 'alt') +}) +test('Spitzenschlüssel bleiben; unauthentifizierte Antworten tragen kein ansicht', async () => { + stelle('on') + assert.deepEqual(Object.keys(await me(customerCookie('c1'))).sort(), ['ansicht', 'authenticated', 'customer', 'role']) // customer darf wachsen (P5) + assert.deepEqual(await me(), { authenticated: false }) + const abgelaufen = await me(kundenansichtCookie({ viewExpiresAt: Date.now() - 1000 })) + const alt = await me(adminCookie()) + assert.deepEqual([abgelaufen.reason, 'ansicht' in abgelaufen, alt.reason, 'ansicht' in alt], ['view_expired', false, 'staff', false]) +})