Files
Webklar-Kundenbereich/test/shopAnfrage.test.js
T
ClaudeandClaude Opus 5.5 9722f0a938 Shop C7: Kundenrouten der Verwaltung (Status, Einstellungen, Artikel, Rechtstexte, Oeffnen, Bilder)
- routes/shop.js: gemeinsame Pruefung mitShop (Schalter, eigenes Projekt, Funktion setup|active, lesend auch disabled, shopConfig
  bei Bedarf anlegen); GET /status erweitert (Bereitschaft, geoeffnet, Snippet, Vorschau, Stripe ohne Konto-ID, Zahlen);
  Einstellungen, Artikel (mit Bild-Pruefung, Beanspruchen und Aufraeumen), Sortierung, Rechtstexte (Entwurf, Bestaetigen mit Hash),
  Oeffnen/Schliessen, Bild-Upload (octet-stream, 8 MB) und Vorschau; Schreib-Limit 300 je Stunde und Kunde
- services/shop/bilder.js: Artikelbilder nach dem Muster von newsImages.js (1600/480 px WebP, Sidecar, 24-h-Aufraeumen, 40 Uploads/h)
- Kundenansicht: sieben Verwaltungs-Pfade erlaubt und als shop_aenderung protokolliert; Anfrage, Oeffnen, Bestaetigen, Stripe bleiben gesperrt
- Tests: shopRoutes, shopBilder; Erwartungen von kundenansichtGuard, customerViewLog und shopAnfrage angepasst

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:52:53 +00:00

404 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 { useTestEnv } from './support/testEnv.js'
import { schemaKeys } from './support/schema.js'
// Webshop B1: Anfrage stellen, zurueckziehen, Stand lesen (services/shop/anfrage.js, routes/shop.js), T21 ans Team,
// Sperre in der Kundenansicht (kundenansichtGuard, Router)
const aw = await startAppwriteMock({
schema: { portalFeatures: schemaKeys('portalFeatures'), shopConfig: schemaKeys('shopConfig'), featureConsents: schemaKeys('featureConsents') },
})
const st = await startFakeStripe()
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
const { config } = await import('../server/config.js')
const { startTestApp, send, get, post } = await import('./support/testApp.js')
const { customerCookie, kundenansichtCookie, viewAsCookie, seedFixtures, customers } = await import('./support/fixtures.js')
const { default: shopRoutes, resetShopRoutesForTests } = await import('../server/routes/shop.js')
const { kundenansichtGuard } = await import('../server/middleware/kundenansichtGuard.js')
const anfrage = await import('../server/services/shop/anfrage.js')
const sf = await import('../server/services/siteFeatures.js')
const { setMailTransportForTests } = await import('../server/services/mailer.js')
const { mailSink } = await import('./support/mailSink.js')
const { deleteDocument, updateDocument } = await import('../server/services/appwriteAdmin.js')
const { featureHaken } = await import('../server/services/featureRegistry.js')
const app = await startTestApp({ '/': kundenansichtGuard, '/api/shop': shopRoutes })
const mails = mailSink()
mails.versandFehler = null
const sinkMail = mails.sendMail.bind(mails)
mails.sendMail = async (mail) => {
if (mails.versandFehler) throw new Error('SMTP nicht erreichbar')
return sinkMail(mail)
}
setMailTransportForTests(mails)
after(() => {
setMailTransportForTests(null)
return Promise.all([app.close(), aw.close(), st.close()])
})
seedFixtures(aw)
aw.seed('websiteProjects', { $id: 'pt1', customerId: 'c1', projectName: 'Töpferei', archived: false })
aw.seed('websiteProjects', { $id: 'pt2', customerId: 'c1', projectName: 'Zweiter Shop' })
aw.seed('websiteProjects', { $id: 'pt3', customerId: 'c2', projectName: 'Fremdes Projekt' })
aw.seed('websiteProjects', { $id: 'pt4', customerId: 'c1', projectName: 'Altprojekt', archived: true })
const cookie = customerCookie('c1')
const GUELTIG = {
warenart: 'kunst',
beschreibung: 'Handgemachte Keramik und Tassen aus eigener Werkstatt.',
artikelzahl: 'bis20',
versand: true,
abholung: false,
unternehmer: true,
keineAusgeschlossenen: true,
nachricht: 'Wir würden gern vor Weihnachten starten.',
}
const stelle = (body = {}, opts = { cookie }) => post('/api/shop/anfrage', { projectId: 'pt1', anfrage: GUELTIG, ...body }, opts)
const zurueck = (projectId = 'pt1', opts = { cookie }) => post('/api/shop/anfrage/zurueckziehen', { projectId }, opts)
const status = (projectId = 'pt1', opts = { cookie }) => send('GET', `/api/shop/status?projectId=${projectId}`, opts)
const t21 = () => mails.sent.filter((m) => /^\[Webshop\]/.test(m.subject))
const shopDoc = (projectId = 'pt1') => aw.docs('shopConfig').get(`${projectId}`) ?? null
let origin
beforeEach(() => {
origin ??= { mode: config.shop.mode, pilotEmails: config.shop.pilotEmails }
config.shop.mode = 'pilot'
config.shop.pilotEmails = [customers.c1.email]
mails.versandFehler = null
resetShopRoutesForTests()
})
async function leereAlles() {
for (const col of ['portalFeatures', 'shopConfig']) for (const id of aw.docs(col).keys()) await deleteDocument(config.collections[col], id)
mails.sent.length = 0
}
test('normalisiereAnfrage: gueltige Eingabe, bereinigt; Fehler je Feld', () => {
const { anfrage: ok, fehler } = anfrage.normalisiereAnfrage({ ...GUELTIG, beschreibung: ' Handgemachte Tassen und mehr ', extra: 'x' })
assert.equal(fehler, undefined)
assert.deepEqual(Object.keys(ok).sort(), ['abholung', 'artikelzahl', 'beschreibung', 'keineAusgeschlossenen', 'nachricht', 'unternehmer', 'versand', 'warenart'])
assert.equal(ok.beschreibung, 'Handgemachte Tassen und mehr')
const f = (aenderung) => anfrage.normalisiereAnfrage({ ...GUELTIG, ...aenderung }).fehler
assert.ok(f({ warenart: 'drogen' }).warenart)
assert.ok(f({ warenart: 'constructor' }).warenart, 'kein Treffer ueber die Prototypkette')
assert.ok(f({ beschreibung: 'kurz' }).beschreibung)
assert.ok(f({ beschreibung: 'x'.repeat(1501) }).beschreibung)
assert.equal(f({ beschreibung: 'x'.repeat(1500) }), undefined)
assert.ok(f({ artikelzahl: 'viele' }).artikelzahl)
assert.ok(f({ versand: false, abholung: false }).versand, 'mindestens eins von Versand und Abholung')
assert.equal(f({ versand: false, abholung: true }), undefined)
assert.ok(f({ versand: 'ja' }).versand)
assert.ok(f({ unternehmer: 'true' }).unternehmer)
assert.ok(f({ unternehmer: false }).unternehmer)
assert.ok(f({ keineAusgeschlossenen: undefined }).keineAusgeschlossenen)
assert.ok(f({ nachricht: 'n'.repeat(1001) }).nachricht)
assert.equal(f({ nachricht: undefined }), undefined)
assert.equal(anfrage.normalisiereAnfrage({ ...GUELTIG, nachricht: undefined }).anfrage.nachricht, '')
assert.ok(anfrage.normalisiereAnfrage(null).fehler.warenart)
assert.ok(anfrage.normalisiereAnfrage([]).fehler.beschreibung)
})
test('GET /status: ohne Dokumente off und leer, legt nichts an; Schalter, fremdes Projekt, ohne Projekt', async () => {
await leereAlles()
const res = await status()
assert.equal(res.status, 200)
assert.deepEqual(res.body, {
verfuegbar: true, state: 'off', ticketWoid: null, anfrage: null, bereitschaft: null, geoeffnet: false, schalter: false, siteKey: null,
snippet: null, vorschauUrl: null, stripe: { verbunden: false, status: null }, zahlen: null,
})
assert.equal(res.headers.get('cache-control'), 'no-store')
assert.equal(aw.docs('portalFeatures').size + aw.docs('shopConfig').size, 0, 'GET legt nichts an')
config.shop.mode = 'off'
assert.equal((await status()).body.verfuegbar, false)
config.shop.mode = 'pilot'
config.shop.pilotEmails = ['jemand-anderes@example.invalid']
assert.equal((await status()).body.verfuegbar, false, 'Pilot: nur gelistete Adressen')
assert.equal((await status('pt3')).status, 403)
assert.equal((await status('gibtsnicht')).status, 403)
assert.equal((await send('GET', '/api/shop/status', { cookie })).status, 400)
assert.equal((await send('GET', '/api/shop/status?projectId=pt1')).status, 401)
})
test('POST /anfrage: Schalter aus => 409, nichts angelegt', async () => {
await leereAlles()
config.shop.mode = 'off'
const res = await stelle()
assert.equal(res.status, 409)
assert.equal(res.body.error, 'Der Webshop ist noch nicht verfügbar.')
assert.equal(res.body.code, 'nicht_verfuegbar')
config.shop.mode = 'pilot'
config.shop.pilotEmails = []
assert.equal((await stelle()).status, 409)
assert.equal(aw.docs('portalFeatures').size + aw.docs('shopConfig').size, 0)
assert.equal(t21().length, 0)
})
test('POST /anfrage: fremdes oder archiviertes Projekt, ohne Projekt, ungueltige Eingabe => nichts angelegt, keine Mail', async () => {
await leereAlles()
assert.equal((await stelle({ projectId: 'pt3' })).status, 403)
assert.equal((await stelle({ projectId: 'unbekannt' })).status, 403)
assert.equal((await stelle({ projectId: 'pt4' })).status, 409, 'archiviert')
const ohne = await post('/api/shop/anfrage', { anfrage: GUELTIG }, { cookie })
assert.equal(ohne.status, 400)
assert.equal(ohne.body.error, 'Bitte wähle ein Projekt.')
const schlecht = await stelle({ anfrage: { ...GUELTIG, beschreibung: 'kurz', unternehmer: false } })
assert.equal(schlecht.status, 400)
assert.ok(schlecht.body.fehler.beschreibung && schlecht.body.fehler.unternehmer)
assert.equal(schlecht.body.error, Object.values(schlecht.body.fehler)[0])
assert.equal((await stelle({ anfrage: 'text' })).status, 400)
assert.equal(aw.docs('portalFeatures').size + aw.docs('shopConfig').size, 0)
assert.equal(t21().length, 0)
assert.equal((await post('/api/shop/anfrage', { projectId: 'pt1', anfrage: GUELTIG })).status, 401)
})
test('POST /anfrage: speichert, Status offen, shopConfig-ID = portalFeatures-ID, T21 ans Team', async () => {
await leereAlles()
const res = await stelle()
assert.equal(res.status, 200)
assert.equal(res.body.anfrage.status, 'offen')
assert.deepEqual(res.body.anfrage.anfrage, { ...GUELTIG })
assert.match(res.body.anfrage.at, /^\d{4}-\d\d-\d\dT/)
assert.equal(res.body.anfrage.begruendung, '')
const feature = await sf.getFeatureDoc('c1', 'pt1', 'shop')
assert.equal(feature.state, 'off', 'Funktion bleibt off, die Anfrage ist keine Bestellung')
const shop = aw.docs('shopConfig').get(feature.$id)
assert.ok(shop, 'shopConfig mit der ID des portalFeatures-Dokuments')
assert.deepEqual([shop.customerId, shop.projectId, shop.siteKey, shop.anfrageStatus, shop.version], ['c1', 'pt1', feature.siteKey, 'offen', 1])
assert.deepEqual(JSON.parse(shop.anfrage), GUELTIG)
assert.equal(aw.docs('shopConfig').size, 1)
// GET zeigt dasselbe
const stand = await status()
assert.deepEqual([stand.body.verfuegbar, stand.body.state, stand.body.anfrage.status], [true, 'off', 'offen'])
assert.deepEqual(stand.body.anfrage.anfrage, GUELTIG)
// T21
assert.equal(t21().length, 1)
const mail = t21()[0]
assert.equal(mail.subject, '[Webshop] Neue Anfrage: Kunde Eins')
assert.ok(mail.to.length > 0 && mail.to.every((x) => config.chatNotify.emails.includes(x.address)), 'nur ans Team')
for (const teil of ['k@example.invalid', 'Töpferei', 'Kunst', 'bis 20', 'Versand', 'Handgemachte Keramik', 'vor Weihnachten']) assert.ok(mail.text.includes(teil), `T21 nennt ${teil}\n${mail.text}`)
assert.ok(!/Abholung/.test(mail.text.split('Lieferung')[1].split('\n')[0]), 'Lieferung: nur Versand')
assert.ok(mail.html.includes(`${config.womsBaseUrl}/customers/c1?tab=funktionen`), 'Link zum Kunden im Ticketsystem')
})
test('POST /anfrage: Lieferung beides; ohne Nachricht steht ein Strich; Rohtext wird maskiert', async () => {
await leereAlles()
const res = await stelle({ anfrage: { ...GUELTIG, versand: true, abholung: true, nachricht: undefined, beschreibung: 'Tassen <b>fett</b> & mehr' } })
assert.equal(res.status, 200)
const mail = t21()[0]
assert.ok(mail.text.includes('Versand und Abholung'))
assert.ok(mail.html.includes('Tassen &lt;b&gt;fett&lt;/b&gt; &amp; mehr'))
assert.ok(!mail.html.includes('<b>fett</b>'))
assert.match(mail.text, /Nachricht:\s*–/)
})
test('zweite Anfrage bei offener => 409, keine zweite Mail; zurueckziehen, dann wieder stellen (Version zaehlt hoch)', async () => {
await leereAlles()
assert.equal((await stelle()).status, 200)
const doppelt = await stelle()
assert.equal(doppelt.status, 409)
assert.equal(doppelt.body.code, 'anfrage_laeuft')
assert.equal(t21().length, 1)
const zu = await zurueck()
assert.equal(zu.status, 200)
assert.equal(zu.body.anfrage.status, 'zurueckgezogen')
assert.equal(shopDoc(), null, 'ID ist die des portalFeatures-Dokuments, nicht die Projekt-ID')
const feature = await sf.getFeatureDoc('c1', 'pt1', 'shop')
assert.equal(aw.docs('shopConfig').get(feature.$id).version, 2)
const nochmal = await zurueck()
assert.equal(nochmal.status, 409)
assert.equal(nochmal.body.code, 'nicht_offen')
assert.equal(nochmal.body.error, 'Die Anfrage kann nur zurückgezogen werden, solange sie offen ist.')
const neu = await stelle({ anfrage: { ...GUELTIG, warenart: 'kleidung' } })
assert.equal(neu.status, 200)
assert.equal(neu.body.anfrage.status, 'offen')
assert.equal(neu.body.anfrage.anfrage.warenart, 'kleidung')
assert.equal(aw.docs('shopConfig').get(feature.$id).version, 3)
assert.equal(aw.docs('shopConfig').size, 1, 'immer dasselbe Dokument')
assert.equal(t21().length, 2)
})
test('zurueckziehen ohne Anfrage, bei fremdem Projekt, bei ausgeschaltetem Schalter', async () => {
await leereAlles()
assert.equal((await zurueck()).status, 409, 'es gibt keine Anfrage')
assert.equal((await zurueck('pt3')).status, 403)
assert.equal((await post('/api/shop/anfrage/zurueckziehen', {}, { cookie })).status, 400)
assert.equal(aw.docs('portalFeatures').size, 0, 'zurueckziehen legt nichts an')
config.shop.mode = 'off'
const aus = await zurueck()
assert.equal(aus.status, 409)
assert.equal(aus.body.code, 'nicht_verfuegbar')
})
test('abgelehnt erlaubt eine neue Anfrage und leert die Entscheidung; angenommen und andere Zustaende nicht', async () => {
await leereAlles()
assert.equal((await stelle()).status, 200)
const feature = await sf.getFeatureDoc('c1', 'pt1', 'shop')
const id = feature.$id
const setze = (daten) => updateDocument(config.collections.shopConfig, id, daten)
await setze({ anfrageStatus: 'abgelehnt', anfrageEntschiedenAt: new Date().toISOString(), anfrageEntschiedenVon: 'Andrej <andrej@webklar.com>', anfrageBegruendung: 'Alkohol ist ausgeschlossen.' })
const abgelehnt = await status()
assert.equal(abgelehnt.body.anfrage.status, 'abgelehnt')
assert.equal(abgelehnt.body.anfrage.begruendung, 'Alkohol ist ausgeschlossen.')
assert.match(abgelehnt.body.anfrage.entschiedenAt, /^\d{4}-\d\d-\d\dT/)
assert.equal((await zurueck()).status, 409, 'abgelehnt laesst sich nicht zurueckziehen')
const neu = await stelle()
assert.equal(neu.status, 200)
assert.equal(neu.body.anfrage.begruendung, '')
assert.equal(neu.body.anfrage.entschiedenAt, null)
assert.equal(aw.docs('shopConfig').get(id).anfrageEntschiedenVon, '')
await setze({ anfrageStatus: 'angenommen' })
const angenommen = await stelle()
assert.equal(angenommen.status, 409)
assert.equal(angenommen.body.code, 'anfrage_laeuft')
assert.equal((await zurueck()).status, 409)
// Funktion nicht mehr off (bestellt): keine neue Anfrage, auch wenn der Status es erlauben wuerde
await setze({ anfrageStatus: 'abgelehnt' })
await updateDocument(config.collections.portalFeatures, id, { state: 'ordered' })
const bestellt = await stelle()
assert.equal(bestellt.status, 409)
assert.equal(bestellt.body.code, 'vorhanden')
assert.equal(bestellt.body.error, 'Für dieses Projekt gibt es schon einen Webshop.')
assert.equal((await status()).body.state, 'ordered')
})
test('Mailfehler: Anfrage ist trotzdem gespeichert (200), Fehler nur im Log', async () => {
await leereAlles()
mails.versandFehler = true
const warnungen = []
const warn = console.warn
console.warn = (...a) => warnungen.push(a.join(' '))
const error = console.error
console.error = () => {}
try {
const res = await stelle()
assert.equal(res.status, 200)
assert.equal(res.body.anfrage.status, 'offen')
} finally {
console.warn = warn
console.error = error
}
assert.equal(t21().length, 0)
assert.ok(warnungen.some((w) => w.includes('T21')), 'Log nennt T21')
assert.ok(!warnungen.some((w) => w.includes('k@example.invalid') || w.includes('Handgemachte')), 'keine Kundendaten im Log')
assert.equal([...aw.docs('shopConfig').values()][0].anfrageStatus, 'offen')
})
test('gleichzeitige Anfragen: genau eine gewinnt, eine Mail, ein shopConfig', async () => {
await leereAlles()
const antworten = await Promise.all([stelle(), stelle(), stelle()])
assert.deepEqual(antworten.map((a) => a.status).sort(), [200, 409, 409])
assert.equal(aw.docs('shopConfig').size, 1)
assert.equal(t21().length, 1)
})
test('ensureShopConfig: legt an, gleichzeitige Aufrufe teilen sich das Dokument (409 -> lesen)', async () => {
await leereAlles()
const feature = await sf.ensureFeatureDoc('c1', 'pt2', 'shop')
const [a, b, c] = await Promise.all([anfrage.ensureShopConfig(feature), anfrage.ensureShopConfig(feature), anfrage.ensureShopConfig(feature)])
assert.equal(a.$id, feature.$id)
assert.deepEqual([b.$id, c.$id], [feature.$id, feature.$id])
assert.equal(aw.docs('shopConfig').size, 1)
const doc = aw.docs('shopConfig').get(feature.$id)
assert.deepEqual([doc.customerId, doc.projectId, doc.siteKey, doc.anfrageStatus, doc.version], ['c1', 'pt2', feature.siteKey, '', 0])
assert.equal(anfrage.anfrageAnsicht(doc), null, 'ohne Anfrage keine Ansicht')
// vorhandenes Dokument bleibt unveraendert
await updateDocument(config.collections.shopConfig, feature.$id, { version: 7 })
assert.equal((await anfrage.ensureShopConfig(feature)).version, 7)
})
test('anfrageAnsicht: unlesbares JSON gibt anfrage null', () => {
assert.deepEqual(anfrage.anfrageAnsicht({ anfrageStatus: 'offen', anfrage: '{kaputt', anfrageAt: '2026-10-05T10:00:00.000+00:00' }), {
status: 'offen',
anfrage: null,
at: '2026-10-05T10:00:00.000+00:00',
entschiedenAt: null,
begruendung: '',
})
assert.equal(anfrage.anfrageAnsicht(null), null)
})
test('anfrageOffenePunkte (Haken bestellpruefung): nur angenommen gibt []', async () => {
await leereAlles()
const punkte = () => featureHaken('shop').bestellpruefung({ customerId: 'c1', projectId: 'pt1' })
assert.deepEqual(await punkte(), [anfrage.ANFRAGE_OFFEN_TEXT], 'ohne Dokumente')
assert.equal((await stelle()).status, 200)
assert.deepEqual(await punkte(), [anfrage.ANFRAGE_OFFEN_TEXT], 'offen')
const feature = await sf.getFeatureDoc('c1', 'pt1', 'shop')
await updateDocument(config.collections.shopConfig, feature.$id, { anfrageStatus: 'angenommen' })
assert.deepEqual(await punkte(), [])
assert.deepEqual(await anfrage.anfrageOffenePunkte({ customerId: 'c2', projectId: 'pt1' }), [anfrage.ANFRAGE_OFFEN_TEXT], 'fremder Kunde')
})
test('Rate-Limit: 20 Schreibaufrufe je Stunde und Kunde, dann 429; GET zaehlt nicht; anderer Kunde frei', async () => {
await leereAlles()
for (let i = 0; i < 20; i += 1) assert.equal((await stelle({ projectId: 'gibtsnicht' })).status, 403, `Aufruf ${i + 1}`)
const zuViele = await stelle({ projectId: 'gibtsnicht' })
assert.equal(zuViele.status, 429)
assert.match(zuViele.body.error, /Zu viele/)
assert.equal((await zurueck()).status, 429, 'zurueckziehen zaehlt mit')
assert.equal((await status()).status, 200)
assert.equal((await stelle({ projectId: 'pt3' }, { cookie: customerCookie('c2') })).status, 409, 'c2: nicht im Pilot, aber nicht begrenzt')
})
// Kundenansicht: die Schreib-Routen stehen nicht auf der Erlaubnisliste des Waechters; der Router sperrt zusaetzlich selbst
test('Kundenansicht: POSTs gesperrt (Waechter view_blocked), GET /status erlaubt', async () => {
await leereAlles()
const kv = { cookie: kundenansichtCookie({ customerId: 'c1' }), headers: { 'X-Kundenansicht': 'a'.repeat(32) } }
const a = await stelle({}, kv)
assert.equal(a.status, 403)
assert.equal(a.body.code, 'view_blocked')
const z = await zurueck('pt1', kv)
assert.equal(z.status, 403)
assert.equal(z.body.code, 'view_blocked')
const lesen = await status('pt1', kv)
assert.equal(lesen.status, 200)
assert.equal(aw.docs('portalFeatures').size + aw.docs('shopConfig').size, 0)
assert.equal(t21().length, 0)
})
test('Kundenansicht: der Router sperrt selbst, auch wenn der Waechter fehlt (403 mit eigenem Text)', async () => {
await leereAlles()
const express = (await import('express')).default
const { sessionMiddleware } = await import('../server/middleware/session.js')
const roh = express()
roh.use(sessionMiddleware())
roh.use(express.json())
roh.use('/api/shop', shopRoutes)
const server = await new Promise((resolve) => {
const s = roh.listen(0, '127.0.0.1', () => resolve(s))
})
try {
const url = `http://127.0.0.1:${server.address().port}`
for (const [pfad, body] of [['/api/shop/anfrage', { projectId: 'pt1', anfrage: GUELTIG }], ['/api/shop/anfrage/zurueckziehen', { projectId: 'pt1' }]]) {
for (const c of [kundenansichtCookie({ customerId: 'c1' }), viewAsCookie('c1')]) {
const res = await fetch(url + pfad, { method: 'POST', headers: { 'content-type': 'application/json', cookie: c }, body: JSON.stringify(body) })
assert.equal(res.status, 403, pfad)
assert.equal((await res.json()).error, 'In der Kundenansicht kann das nicht geändert werden.')
}
}
} finally {
await new Promise((resolve) => server.close(resolve))
}
assert.equal(aw.docs('portalFeatures').size + aw.docs('shopConfig').size, 0)
})
test('normaler Kunde im Pilot wird vom Waechter nicht beruehrt (Gegenprobe)', async () => {
await leereAlles()
const res = await stelle()
assert.equal(res.status, 200)
assert.equal((await get('/api/shop/status?projectId=pt1', { cookie })).anfrage.status, 'offen')
})