import { test, after, beforeEach } from 'node:test' import assert from 'node:assert/strict' import { startAppwriteMock } from './support/appwriteMock.js' import { startFakeStripe } from './support/fakeStripe.js' import { mailSink } from './support/mailSink.js' import { schemaKeys } from './support/schema.js' import { useTestEnv } from './support/testEnv.js' // Anfragen im Abo-Modell (P5 Task 12): die Shop-Anfrage nimmt Unternehmer aus der Kundenart (nur Betriebe), der Buchungskalender bleibt offen const aw = await startAppwriteMock({ schema: { portalFeatures: schemaKeys('portalFeatures'), shopConfig: schemaKeys('shopConfig'), ukEinstellungen: schemaKeys('ukEinstellungen'), featureConsents: schemaKeys('featureConsents'), }, datetimes: { ukEinstellungen: ['rechnungAuftragAt', 'anfrageAt', 'anfrageEntschiedenAt', 'icalFehlerMailAt'] }, }) const st = await startFakeStripe() useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) const { config } = await import('../server/config.js') const { startTestApp, post } = await import('./support/testApp.js') const { customerCookie, customers, seedFixtures } = await import('./support/fixtures.js') const { deleteDocument } = await import('../server/services/appwriteAdmin.js') const { setMailTransportForTests } = await import('../server/services/mailer.js') const { NUR_BETRIEB_TEXT } = await import('../server/services/kundenart.js') const { default: shopRoutes, resetShopRoutesForTests } = await import('../server/routes/shop.js') const { default: ukRoutes, resetUnterkunftRoutesForTests } = await import('../server/routes/unterkunft.js') const app = await startTestApp({ '/api/shop': shopRoutes, '/api/unterkunft': ukRoutes }) const sink = mailSink() setMailTransportForTests(sink) const vorher = { neu: { mode: config.kundenansichtNeu.mode, pilotEmails: [...config.kundenansichtNeu.pilotEmails] }, shop: { mode: config.shop.mode, pilotEmails: [...config.shop.pilotEmails] }, unterkunft: { mode: config.unterkunft.mode, pilotEmails: [...config.unterkunft.pilotEmails] }, } after(() => { Object.assign(config.kundenansichtNeu, vorher.neu) Object.assign(config.shop, vorher.shop) Object.assign(config.unterkunft, vorher.unterkunft) setMailTransportForTests(null) return Promise.all([app.close(), aw.close(), st.close()]) }) const c1 = customerCookie('c1') const anfrage = (extra = {}) => ({ warenart: 'waren', beschreibung: 'Backwaren aus eigener Herstellung', artikelzahl: 'bis20', versand: false, abholung: true, keineAusgeschlossenen: true, ...extra }) const senden = (a, cookie = c1) => post('/api/shop/anfrage', { projectId: 'p1', anfrage: a }, { cookie }) // aw.docs() liefert eine Kopie: Dokumente werden ueber die API geloescht const leere = async (...collections) => { for (const col of collections) for (const id of aw.docs(col).keys()) await deleteDocument(col, id) } beforeEach(async () => { await leere('portalFeatures', 'shopConfig', 'ukEinstellungen', 'featureConsents', 'websiteProjects') seedFixtures(aw) aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Bäckerei', archived: false }) config.kundenansichtNeu.mode = 'pilot' config.kundenansichtNeu.pilotEmails = ['k@example.invalid', 'test@example.invalid'] config.shop.mode = config.unterkunft.mode = 'on' resetShopRoutesForTests() resetUnterkunftRoutesForTests() sink.sent.length = 0 }) test('privat (Kundenart leer): 409 nur_betrieb, auch mit gefälschtem unternehmer: true; nichts geschrieben, keine Mail', async () => { const r = await senden(anfrage({ unternehmer: true })) assert.deepEqual([r.status, r.body.code, r.body.error], [409, 'nur_betrieb', NUR_BETRIEB_TEXT]) assert.deepEqual([aw.docs('shopConfig').size, aw.docs('portalFeatures').size, sink.sent.length], [0, 0, 0]) for (const kundenart of ['', 'kaputt', 'Unternehmen', 'privat']) { aw.seed('customers', { ...customers.c1, kundenart }) const x = await senden(anfrage({ unternehmer: true, kundenart: 'unternehmen' })) assert.deepEqual([x.status, x.body.code], [409, 'nur_betrieb'], kundenart) } }) test('Betrieb: unternehmer kommt vom Server, Anfrage speichert die Kundenart', async () => { aw.seed('customers', { ...customers.c1, kundenart: 'unternehmen' }) const r = await senden(anfrage()) assert.deepEqual([r.status, r.body.anfrage.status], [200, 'offen']) assert.deepEqual([r.body.anfrage.anfrage.unternehmer, r.body.anfrage.anfrage.kundenart], [true, 'unternehmen']) assert.equal(sink.sent.length, 1, 'T21 ans Team') await leere('shopConfig', 'portalFeatures') const falsch = await senden(anfrage({ unternehmer: false })) // der Client-Wert zählt nicht assert.deepEqual([falsch.status, falsch.body.anfrage.anfrage.unternehmer], [200, true]) await leere('shopConfig', 'portalFeatures') const ohne = await senden(anfrage({ keineAusgeschlossenen: false })) assert.deepEqual([ohne.status, Object.keys(ohne.body.fehler)], [400, ['keineAusgeschlossenen']]) }) test('alte Ansicht unverändert: unternehmer bleibt Pflicht, privat darf anfragen', async () => { config.kundenansichtNeu.mode = 'off' const r = await senden(anfrage()) assert.deepEqual([r.status, Object.keys(r.body.fehler)], [400, ['unternehmer']]) const ok = await senden(anfrage({ unternehmer: true })) assert.equal(ok.status, 200) assert.equal(Object.hasOwn(ok.body.anfrage.anfrage, 'kundenart'), false, 'ohne neue Ansicht keine Kundenart in der Anfrage') }) test('Reihenfolge: Projekt und Schalter vor der Kundenart, die Kundenart vor der Eingabe', async () => { assert.equal((await post('/api/shop/anfrage', { projectId: 'pfremd', anfrage: anfrage() }, { cookie: c1 })).status, 403) config.shop.mode = 'off' assert.equal((await senden(anfrage())).body.code, 'nicht_verfuegbar') config.shop.mode = 'on' const leer = await senden({}) // ungültige Eingabe, aber privat: nur_betrieb kommt zuerst assert.deepEqual([leer.status, leer.body.code], [409, 'nur_betrieb']) aw.seed('customers', { ...customers.c1, kundenart: 'unternehmen' }) assert.equal((await senden({})).status, 400) }) test('Buchungskalender: privat darf in der neuen Ansicht anfragen (nurBetrieb false)', async () => { const r = await post('/api/unterkunft/anfrage-funktion', { projectId: 'p1', art: 'ferienwohnung', einheiten: 2, portale: ['booking'] }, { cookie: c1 }) assert.equal(r.status, 200, JSON.stringify(r.body)) assert.equal(r.body.anfrage.status, 'offen') })