Terminplaner TP2: Fragebogen im Kontext und Speicher

kontext.js: ctx.fragebogen (normalisiert; fehlt er oder ist er kaputt, der leere,
kaputt mit einer Logzeile und ohne Wurf), ladeEinstellung liefert zusaetzlich
fragebogen, neuer kontextFuerPortal(feature) fuer das Portal in jedem Zustand.
speichereFragebogen schreibt unter termine-cfg:<id> nur fragebogen, updatedAt und
updatedBy (ohne version; fehlt das Dokument: 409 keine_einstellung). Neue Option
fragebogen bei speichereEinstellung schreibt Einstellung und Fragebogen in
derselben Sperre. kontextPerSiteKey bleibt unveraendert (Entwurf nie oeffentlich),
Tests dazu fuer off, ordered, setup, disabled und nach activate. Der Test
ladeEntwurf in funktionenCheckout.test.js schaltet die neue Logzeile stumm.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 19:14:22 +00:00
1 parent 008ad4fb1e
commit 31aa80affd
3 files changed
+481 -31

No files matched your search

+20 -19
View File
@@ -1155,28 +1155,29 @@ test('ladeEntwurf: Einstellung und Fragebogen aus termineConfig, fehlend oder ka
const doc = (extra) => aw.seed('termineConfig', { $id: 'f_p1_termine', customerId: 'c1', projectId: 'p1', einstellung: JSON.stringify(einstellung), version: 3, updatedAt: new Date().toISOString(), updatedBy: 'x', ...extra })
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung: null, fragebogen: null }, 'kein Dokument')
doc({ fragebogen: JSON.stringify(fragebogen) })
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung, fragebogen })
doc({})
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung, fragebogen: null }, 'Attribut fehlt')
for (const kaputt of ['{kaputt', '', '[1,2]', '42', '"text"', 'null']) {
doc({ fragebogen: kaputt })
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung, fragebogen: null }, `fragebogen ${JSON.stringify(kaputt)}`)
}
// Einstellung unlesbar: null (der Fehler wird im Kontext-Modul geloggt), der Fragebogen bleibt
const log = stumm()
// ladeEinstellung (Kontext-Modul) meldet einen Fragebogen, den die Normalisierung ablehnt, mit einer Logzeile: hier stumm
const leise = stumm()
try {
doc({ fragebogen: JSON.stringify(fragebogen) })
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung, fragebogen })
doc({})
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung, fragebogen: null }, 'Attribut fehlt')
for (const kaputt of ['{kaputt', '', '[1,2]', '42', '"text"', 'null']) {
doc({ fragebogen: kaputt })
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung, fragebogen: null }, `fragebogen ${JSON.stringify(kaputt)}`)
}
// Einstellung unlesbar: null (der Fehler wird im Kontext-Modul geloggt), der Fragebogen bleibt
doc({ einstellung: '{kaputt', fragebogen: JSON.stringify(fragebogen) })
assert.deepEqual(await order.ladeEntwurf('f_p1_termine'), { einstellung: null, fragebogen })
// Lesefehler werden nicht verschluckt
doc({ fragebogen: JSON.stringify(fragebogen) })
aw.failNext({ collection: 'termineConfig', status: 500 })
await assert.rejects(order.ladeEntwurf('f_p1_termine'))
aw.failNext({ collection: 'termineConfig', method: 'GET', status: 500, after: 1 })
await assert.rejects(order.ladeEntwurf('f_p1_termine'))
await assert.doesNotReject(order.ladeEntwurf('f_p1_termine'))
await assert.rejects(order.ladeEntwurf(''), TypeError)
} finally {
log.mock.restore()
leise.mock.restore()
}
// Lesefehler werden nicht verschluckt
doc({ fragebogen: JSON.stringify(fragebogen) })
aw.failNext({ collection: 'termineConfig', status: 500 })
await assert.rejects(order.ladeEntwurf('f_p1_termine'))
aw.failNext({ collection: 'termineConfig', method: 'GET', status: 500, after: 1 })
await assert.rejects(order.ladeEntwurf('f_p1_termine'))
await assert.doesNotReject(order.ladeEntwurf('f_p1_termine'))
await assert.rejects(order.ladeEntwurf(''), TypeError)
})
+341 -3
View File
@@ -24,12 +24,14 @@ const { config } = await import('../server/config.js')
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
const { setMailTransportForTests } = await import('../server/services/mailer.js')
const { renderMail, loadTemplate } = await import('../server/services/mailTemplates.js')
const { normalisiereEinstellung } = await import('../server/services/termine/einstellungen.js')
const { normalisiereEinstellung, fehlendePflichtangaben } = await import('../server/services/termine/einstellungen.js')
const { resetOutboxForTests, warteAufVersand } = await import('../server/services/termine/outbox.js')
const { buchungsNummer, verwaltungsToken } = await import('../server/services/termine/token.js')
const { leerFragebogen, normalisiereFragebogen, FRAGEBOGEN_MAX } = await import('../server/services/termine/fragebogen.js')
const { activate } = await import('../server/services/siteFeatures.js')
const K = await import('../server/services/termine/kontext.js')
const M = await import('../server/services/termine/mails.js')
const { ladeEinstellung, speichereEinstellung, kontextPerSiteKey, kontextPerFeature, websiteUrl, resetKontextForTests } = K
const { ladeEinstellung, speichereEinstellung, speichereFragebogen, kontextPerSiteKey, kontextPerFeature, kontextFuerPortal, websiteUrl, resetKontextForTests } = K
const { BETREFFE, werteFuer, anGast, anBetrieb } = M
after(() => setMailTransportForTests(null))
@@ -205,7 +207,7 @@ test('speichereEinstellung: erstes Speichern legt das Dokument unter der Funktio
assert.equal(typeof doc.einstellung, 'string', 'JSON-Text im Attribut einstellung')
assert.deepEqual(JSON.parse(doc.einstellung), e)
assert.equal(Object.hasOwn(doc, 'data'), false, 'nie ein Attribut data')
assert.deepEqual(await ladeEinstellung(FEATURE), { einstellung: e, version: 1 })
assert.deepEqual(await ladeEinstellung(FEATURE), { einstellung: e, version: 1, fragebogen: leerFragebogen() }, 'ohne gespeicherten Fragebogen der leere')
})
test('speichereEinstellung: zweites Speichern mit der gelesenen Version zaehlt hoch und ersetzt die Einstellung', async () => {
@@ -552,6 +554,342 @@ test('websiteUrl: betrieb.websiteUrl, sonst Live-Domain, sonst Vorschau-Subdomai
assert.equal(websiteUrl(null), config.publicBaseUrl)
})
// ---------------------------------------------------------------- Fragebogen im Speicher und im Kontext (TP2)
/** Normalisierter Fragebogen aus einer (teilweisen) Eingabe; die Eingabe muss gueltig sein. */
const frage = (over = {}) => {
const erg = normalisiereFragebogen({ ...leerFragebogen(), ...over })
assert.equal(erg.fehler, undefined, JSON.stringify(erg.fehler))
return erg.fragebogen
}
test('speichereFragebogen: aendert nur fragebogen, updatedAt und updatedBy; einstellung und version bleiben bytegleich, ladeEinstellung liefert den neuen Stand', async () => {
const e = einstellung()
await speichereEinstellung(feature(), e, { version: 0, by: 'kunde:c_kunde01', now: JETZT })
const vorher = { ...gespeichert() }
const gesehen = []
const erg = await speichereFragebogen(feature(), (bisher) => {
gesehen.push(structuredClone(bisher))
return { ...bisher, branche: 'salon', bestaetigt: ['wer', 'branche'], einbau: { ...bisher.einbau, modus: 'knopf', knopfText: 'Termin buchen' } }
}, { by: 'kunde:zweiter', now: SEKUNDEN(5) })
const erwartet = frage({ branche: 'salon', bestaetigt: ['branche', 'wer'], einbau: { modus: 'knopf', knopfText: 'Termin buchen' } })
assert.deepEqual(gesehen, [leerFragebogen()], 'ohne gespeicherten Fragebogen bekommt aendere den leeren')
assert.deepEqual(erg, { fragebogen: erwartet }, 'zurueck kommt der normalisierte Fragebogen')
const doc = gespeichert()
assert.equal(doc.einstellung, vorher.einstellung, 'einstellung bytegleich')
assert.equal(doc.version, vorher.version, 'version bleibt')
assert.equal(doc.version, 1)
assert.equal(doc.customerId, KUNDE)
assert.equal(doc.projectId, PROJEKT)
assert.equal(typeof doc.fragebogen, 'string', 'JSON-Text')
assert.deepEqual(JSON.parse(doc.fragebogen), erwartet)
assert.equal(doc.updatedBy, 'kunde:zweiter')
assert.equal(new Date(doc.updatedAt).getTime(), SEKUNDEN(5).getTime())
assert.deepEqual(await ladeEinstellung(FEATURE), { einstellung: e, version: 1, fragebogen: erwartet })
// zweites Mal: aendere sieht den gespeicherten Stand; updatedBy wird auf 100 Zeichen gekuerzt
await speichereFragebogen(feature(), (bisher) => {
gesehen.push(structuredClone(bisher))
return { ...bisher, preiseGeprueft: true }
}, { by: 'x'.repeat(300) })
assert.deepEqual(gesehen[1], erwartet)
assert.equal(gespeichert().updatedBy.length, 100)
assert.equal(gespeichert().einstellung, vorher.einstellung)
assert.equal((await ladeEinstellung(FEATURE)).fragebogen.preiseGeprueft, true)
assert.equal((await ladeEinstellung(FEATURE)).version, 1)
})
test('speichereFragebogen: auch eine asynchrone aendere-Funktion; unbekannte Schluessel und __proto__ aus der Eingabe landen nie im Speicher', async () => {
await speichereEinstellung(feature(), einstellung(), { version: 0 })
const boese = JSON.parse('{"branche":"salon","__proto__":{"admin":true},"fremd":1,"einbau":{"stelle":"Startseite","__proto__":{"x":1},"zusatz":"y"}}')
const erg = await speichereFragebogen(feature(), async () => boese, {})
assert.deepEqual(erg.fragebogen, frage({ branche: 'salon', einbau: { stelle: 'Startseite' } }))
assert.deepEqual(JSON.parse(gespeichert().fragebogen), erg.fragebogen)
assert.equal(Object.hasOwn(JSON.parse(gespeichert().fragebogen), 'fremd'), false)
})
test('speichereFragebogen: ungueltige Eingabe => 400 mit err.fehler, eigene Fehler gehen unveraendert durch, nichts wird geschrieben; falsche Argumente => TypeError', async () => {
await speichereEinstellung(feature(), einstellung(), { version: 0 })
await speichereFragebogen(feature(), () => frage({ branche: 'salon' }), {})
const vorher = { ...gespeichert() }
await assert.rejects(
() => speichereFragebogen(feature(), (bisher) => ({ ...bisher, einbau: { ...bisher.einbau, modus: 'popup' } }), {}),
(err) => err.status === 400 && typeof err.fehler['einbau.modus'] === 'string',
)
for (const unsinn of [null, undefined, 'x', 5, []]) {
await assert.rejects(
() => speichereFragebogen(feature(), () => unsinn, {}),
(err) => err.status === 400 && typeof err.fehler._ === 'string',
String(unsinn),
)
}
const eigener = Object.assign(new Error('Eingabe'), { status: 400, fehler: { branche: 'x' } })
await assert.rejects(() => speichereFragebogen(feature(), async () => { throw eigener }, {}), (err) => err === eigener)
assert.deepEqual(gespeichert(), vorher, 'kein Schreibzugriff')
for (const unsinn of [undefined, null, 'x', {}]) await assert.rejects(() => speichereFragebogen(feature(), unsinn, {}), TypeError)
await assert.rejects(() => speichereFragebogen({}, () => frage(), {}), TypeError)
await assert.rejects(() => speichereFragebogen(feature(), () => frage(), { now: 'gestern' }), TypeError)
assert.deepEqual(gespeichert(), vorher)
// das Sperrschloss ist wieder frei
assert.equal((await speichereFragebogen(feature(), () => frage({ branche: 'kosmetik' }), {})).fragebogen.branche, 'kosmetik')
})
test('speichereFragebogen ohne termineConfig-Dokument: 409 keine_einstellung, aendere laeuft nie, nichts wird angelegt', async () => {
seedFeature()
let aufrufe = 0
await assert.rejects(
() => speichereFragebogen(feature(), () => { aufrufe += 1; return frage() }, {}),
(err) => err.status === 409 && err.code === 'keine_einstellung' && typeof err.message === 'string' && err.message !== '',
)
assert.equal(aufrufe, 0)
assert.equal(aw.docs('termineConfig').size, 0)
assert.equal(await ladeEinstellung(FEATURE), null)
})
test('speichereFragebogen: der groesste zulaessige Fragebogen bleibt im Speicher unter FRAGEBOGEN_MAX', async () => {
await speichereEinstellung(feature(), einstellung(), { version: 0 })
const alle = ['branche', 'wer', 'leistungen', 'zeiten', 'regeln', 'gastfelder', 'benachrichtigung', 'website']
await speichereFragebogen(feature(), () => frage({
branche: 'sonstiges',
bestaetigt: alle,
bundeslandGewaehlt: true,
preiseGeprueft: true,
// Zeichen, die im JSON-Text verdoppelt werden
einbau: { modus: 'inline', knopfText: '"'.repeat(40), stelle: '\\'.repeat(300), gestaltung: '"'.repeat(1000), bemerkung: '\\'.repeat(1000) },
}), {})
assert.ok(gespeichert().fragebogen.length < FRAGEBOGEN_MAX, `${gespeichert().fragebogen.length} Zeichen`)
assert.equal(JSON.parse(gespeichert().fragebogen).einbau.bemerkung.length, 1000)
})
test('speichereEinstellung ohne Option fragebogen laesst den gespeicherten Fragebogen stehen (bytegleich), auch in der Funktionsform', async () => {
await speichereEinstellung(feature(), einstellung(), { version: 0 })
await speichereFragebogen(feature(), () => frage({ branche: 'salon', bestaetigt: ['branche'] }), {})
const roh = gespeichert().fragebogen
assert.ok(roh)
await speichereEinstellung(feature(), einstellung({ betrieb: betriebMit({ name: 'Neu' }) }), { version: 1 })
assert.equal(gespeichert().fragebogen, roh)
await speichereEinstellung(feature(), async (bisher) => ({ ...bisher, farbe: '#112233' }), { version: null })
assert.equal(gespeichert().fragebogen, roh)
const geladen = await ladeEinstellung(FEATURE)
assert.equal(geladen.version, 3)
assert.equal(geladen.einstellung.betrieb.name, 'Neu')
assert.deepEqual(geladen.fragebogen, frage({ branche: 'salon', bestaetigt: ['branche'] }))
})
test('speichereEinstellung mit Option fragebogen: Neuanlage auf leerer Collection schreibt Einstellung und Fragebogen in ein Dokument', async () => {
const e = einstellung()
const erg = await speichereEinstellung(feature(), e, { version: 0, by: 'kunde:c_kunde01', now: JETZT, fragebogen: { branche: 'salon' } })
assert.equal(erg.version, 1)
assert.deepEqual(erg.einstellung, e)
assert.equal(aw.docs('termineConfig').size, 1)
const doc = gespeichert()
assert.deepEqual(JSON.parse(doc.einstellung), e)
assert.deepEqual(JSON.parse(doc.fragebogen), frage({ branche: 'salon' }), 'normalisiert (alle Felder) gespeichert')
assert.equal(doc.version, 1)
assert.equal(doc.updatedBy, 'kunde:c_kunde01')
assert.deepEqual(await ladeEinstellung(FEATURE), { einstellung: e, version: 1, fragebogen: frage({ branche: 'salon' }) })
})
test('speichereEinstellung mit Option fragebogen: ersetzt den Fragebogen in derselben Sperre (Vorlage ersetzen), Objekt- und Funktionsform, Version zaehlt einmal', async () => {
await speichereEinstellung(feature(), einstellung(), { version: 0 })
const einbau = { modus: 'knopf', knopfText: 'Termin buchen', stelle: 'Startseite' }
await speichereFragebogen(feature(), () => frage({ branche: 'salon', bestaetigt: ['branche', 'wer'], preiseGeprueft: true, bundeslandGewaehlt: true, einbau }), {})
// Funktionsform: sieht den gespeicherten Fragebogen und setzt Bestaetigungen zurueck, der Einbau bleibt
const gesehen = []
const ersatz = einstellung({ betrieb: betriebMit({ name: 'Ersatz' }) })
const erg = await speichereEinstellung(feature(), ersatz, {
version: 1,
fragebogen: (bisher) => {
gesehen.push(structuredClone(bisher))
return { ...bisher, branche: 'kosmetik', bestaetigt: [], preiseGeprueft: false, bundeslandGewaehlt: false }
},
})
assert.equal(erg.version, 2)
assert.equal(gesehen.length, 1)
assert.equal(gesehen[0].branche, 'salon')
const geladen = await ladeEinstellung(FEATURE)
assert.equal(geladen.version, 2)
assert.equal(geladen.einstellung.betrieb.name, 'Ersatz')
assert.deepEqual(geladen.fragebogen, frage({ branche: 'kosmetik', einbau }))
// Objektform ersetzt vollstaendig
await speichereEinstellung(feature(), ersatz, { version: 2, fragebogen: { branche: 'beratung' } })
assert.deepEqual((await ladeEinstellung(FEATURE)).fragebogen, frage({ branche: 'beratung' }))
assert.equal((await ladeEinstellung(FEATURE)).version, 3)
})
test('speichereEinstellung mit ungueltigem fragebogen: 400 mit err.fehler, weder Einstellung noch Version noch Dokument werden geschrieben', async () => {
const falsch = (err) => err.status === 400 && typeof err.fehler === 'object' && Object.keys(err.fehler).length > 0
await assert.rejects(() => speichereEinstellung(feature(), einstellung(), { version: 0, fragebogen: { branche: 'gibtsnicht' } }), (err) => falsch(err) && typeof err.fehler.branche === 'string')
assert.equal(aw.docs('termineConfig').size, 0, 'Neuanlage scheitert ganz')
await speichereEinstellung(feature(), einstellung(), { version: 0, fragebogen: { branche: 'salon' } })
const vorher = { ...gespeichert() }
const neu = einstellung({ betrieb: betriebMit({ name: 'Anders' }) })
await assert.rejects(() => speichereEinstellung(feature(), neu, { version: 1, fragebogen: { einbau: { modus: 'popup' } } }), (err) => falsch(err) && typeof err.fehler['einbau.modus'] === 'string')
for (const unsinn of ['x', 5, [], null]) {
await assert.rejects(() => speichereEinstellung(feature(), neu, { version: 1, fragebogen: unsinn }), falsch, String(unsinn))
}
assert.deepEqual(gespeichert(), vorher)
assert.equal((await ladeEinstellung(FEATURE)).version, 1)
// eine veraltete Version meldet weiter zuerst 409
await assert.rejects(() => speichereEinstellung(feature(), neu, { version: 0, fragebogen: { branche: 'gibtsnicht' } }), (err) => err.status === 409 && err.code === 'EINSTELLUNG_VERALTET')
})
test('speichereEinstellung (Version 1) und speichereFragebogen gleichzeitig: nacheinander unter derselben Sperre, beide Aenderungen gelten, die Version steigt genau einmal', async () => {
for (const zuerst of ['einstellung', 'fragebogen']) {
await leere()
resetKontextForTests()
await speichereEinstellung(feature(), einstellung({ betrieb: betriebMit({ name: 'Start' }) }), { version: 0 })
const ablauf = []
const langsam = async (name) => {
ablauf.push(`${name} start`)
await new Promise((resolve) => setTimeout(resolve, 20)) // der Zweite haette Zeit, dazwischenzufunken
ablauf.push(`${name} ende`)
}
const alsEinstellung = () => speichereEinstellung(feature(), async (bisher) => {
await langsam('einstellung')
return { ...bisher, betrieb: { ...bisher.betrieb, name: 'Neu' } }
}, { version: 1 })
const alsFragebogen = () => speichereFragebogen(feature(), async (bisher) => {
await langsam('fragebogen')
return { ...bisher, branche: 'salon' }
}, {})
await Promise.all(zuerst === 'einstellung' ? [alsEinstellung(), alsFragebogen()] : [alsFragebogen(), alsEinstellung()])
const zweiter = zuerst === 'einstellung' ? 'fragebogen' : 'einstellung'
assert.deepEqual(ablauf, [`${zuerst} start`, `${zuerst} ende`, `${zweiter} start`, `${zweiter} ende`], zuerst)
const geladen = await ladeEinstellung(FEATURE)
assert.equal(geladen.version, 2, `Version steigt genau einmal (${zuerst} zuerst)`)
assert.equal(geladen.einstellung.betrieb.name, 'Neu', zuerst)
assert.equal(geladen.fragebogen.branche, 'salon', zuerst)
}
})
test('kaputter oder unbrauchbarer Fragebogen im Dokument: leerer Fragebogen mit einer Logzeile ohne Wurf, die Einstellung bleibt gueltig; fehlend oder leer ohne Logzeile', async () => {
seedFeature()
seedProjekt()
const e = einstellung()
const basis = { $id: FEATURE, customerId: KUNDE, projectId: PROJEKT, einstellung: JSON.stringify(e), version: 2 }
const kaputt = ['{kaputt', '[]', '"x"', 'null', '5', '{"branche":"gibtsnicht"}', '{"einbau":{"modus":"popup"}}']
for (const roh of kaputt) {
log.error.mock.resetCalls()
aw.seed('termineConfig', { ...basis, fragebogen: roh })
assert.deepEqual(await ladeEinstellung(FEATURE), { einstellung: e, version: 2, fragebogen: leerFragebogen() }, roh)
assert.equal(log.error.mock.callCount(), 1, `eine Logzeile: ${roh}`)
const zeile = String(log.error.mock.calls[0].arguments[0])
assert.match(zeile, /Fragebogen f_p_projekt01_termine/)
const ctx = await kontextFuerPortal(feature())
assert.deepEqual(ctx.fragebogen, leerFragebogen(), roh)
assert.deepEqual(ctx.einstellung, e, roh)
assert.equal(ctx.version, 2, roh)
}
for (const ohne of [{}, { fragebogen: '' }, { fragebogen: null }]) {
log.error.mock.resetCalls()
aw.seed('termineConfig', { ...basis, ...ohne })
assert.deepEqual((await ladeEinstellung(FEATURE)).fragebogen, leerFragebogen(), JSON.stringify(ohne))
assert.equal(log.error.mock.callCount(), 0, JSON.stringify(ohne))
}
// der Schaden laesst sich ueberschreiben: aendere bekommt den leeren Fragebogen, die Einstellung bleibt unberuehrt
aw.seed('termineConfig', { ...basis, fragebogen: '{kaputt' })
let gesehen
await speichereFragebogen(feature(), (bisher) => { gesehen = bisher; return frage({ branche: 'salon' }) }, {})
assert.deepEqual(gesehen, leerFragebogen())
assert.equal(gespeichert().einstellung, basis.einstellung)
assert.equal(gespeichert().version, 2)
assert.deepEqual((await ladeEinstellung(FEATURE)).fragebogen, frage({ branche: 'salon' }))
})
test('ctx.fragebogen: Kontext per siteKey, per Feature und fuers Portal tragen ihn (ohne Dokument den leeren); speichereFragebogen leert den Speicher', async () => {
seedFeature()
seedProjekt()
assert.deepEqual((await kontextPerFeature(FEATURE)).fragebogen, leerFragebogen(), 'kein termineConfig-Dokument')
await speichereEinstellung(feature(), einstellung(), { version: 0 })
assert.deepEqual((await kontextPerSiteKey(SITE_KEY, T0)).fragebogen, leerFragebogen())
await speichereFragebogen(feature(), () => frage({ branche: 'salon', bestaetigt: ['branche'] }), {})
const neu = await kontextPerSiteKey(SITE_KEY, SEKUNDEN(1))
assert.deepEqual(neu.fragebogen, frage({ branche: 'salon', bestaetigt: ['branche'] }), 'sofort sichtbar: Speicher geleert')
assert.equal(neu.version, 1, 'die Version der Einstellung zaehlt nicht hoch')
assert.equal((await kontextPerFeature(FEATURE)).fragebogen.branche, 'salon')
assert.equal((await kontextFuerPortal(feature())).fragebogen.branche, 'salon')
})
test('kontextFuerPortal: das uebergebene Dokument in jedem Zustand und bei jedem Modus, ohne Nachschlagen und ohne Speicher', async () => {
seedProjekt()
const e = einstellung()
// bewusst kein portalFeatures-Dokument in der Datenbank: der Kontext nimmt das uebergebene, Modus und Zustand prueft der Aufrufer
await speichereEinstellung(feature(), e, { version: 0 })
await speichereFragebogen(feature(), () => frage({ branche: 'salon' }), {})
assert.equal(aw.docs('portalFeatures').size, 0)
for (const modus of ['pilot', 'off']) {
config.termine.mode = modus
for (const state of ['off', 'ordered', 'setup', 'active', 'disabled']) {
const doc = feature({ state })
const ctx = await kontextFuerPortal(doc)
const was = `${modus}/${state}`
assert.equal(ctx.feature, doc, was)
assert.equal(ctx.feature.state, state, was)
assert.equal(ctx.project.$id, PROJEKT, was)
assert.deepEqual(ctx.einstellung, e, was)
assert.equal(ctx.version, 1, was)
assert.equal(ctx.fragebogen.branche, 'salon', was)
}
}
// kein Speicher: eine Aenderung ist sofort sichtbar, auch ohne dass jemand den Speicher leert
await speichereFragebogen(feature(), (b) => ({ ...b, branche: 'kosmetik' }), {})
assert.equal((await kontextFuerPortal(feature())).fragebogen.branche, 'kosmetik')
// ohne termineConfig-Dokument und ohne Projekt
await deleteDocument('termineConfig', FEATURE)
const leer = await kontextFuerPortal(feature({ projectId: 'p_gibt_es_nicht', state: 'off' }))
assert.equal(leer.einstellung, null)
assert.equal(leer.version, 0)
assert.equal(leer.project, null)
assert.deepEqual(leer.fragebogen, leerFragebogen())
for (const unsinn of [undefined, null, {}, { $id: '' }, 'f_x']) await assert.rejects(() => kontextFuerPortal(unsinn), TypeError, String(unsinn))
})
test('Entwurf nie oeffentlich: kontextPerSiteKey ist fuer off, ordered, setup und disabled trotz vollstaendiger Einstellung null, kontextFuerPortal liefert sie; nach activate gibt es den Kontext', async () => {
const e = einstellung()
assert.deepEqual(fehlendePflichtangaben(e), [], 'die Einstellung ist fuer Gaeste vollstaendig')
await speichereEinstellung(feature(), e, { version: 0 })
await speichereFragebogen(feature(), () => frage({ branche: 'salon', bestaetigt: ['branche', 'wer', 'leistungen'] }), {})
seedProjekt()
for (const state of ['off', 'ordered', 'setup', 'disabled']) {
resetKontextForTests()
seedFeature({ state, ...(state === 'disabled' ? { disabledAt: JETZT.toISOString(), disabledReason: 'abo_ende' } : {}) })
assert.equal(await kontextPerSiteKey(SITE_KEY, T0), null, `oeffentlich: ${state}`)
const portal = await kontextFuerPortal(feature({ state }))
assert.deepEqual(portal.einstellung, e, `Portal: ${state}`)
assert.equal(portal.fragebogen.branche, 'salon', `Portal: ${state}`)
await deleteDocument('portalFeatures', FEATURE)
}
// Freischaltung: aus setup wird active, der Kontext folgt (hoechstens 30 Sekunden spaeter, so lange haelt der Speicher das null)
resetKontextForTests()
seedFeature({ state: 'setup' })
assert.equal(await kontextPerSiteKey(SITE_KEY, T0), null)
await activate(FEATURE, { by: 'team' })
const ctx = await kontextPerSiteKey(SITE_KEY, SEKUNDEN(31))
assert.equal(ctx.feature.state, 'active')
assert.deepEqual(ctx.einstellung, e)
assert.equal(ctx.fragebogen.branche, 'salon')
})
// ---------------------------------------------------------------- Betreffzeilen
const FAELLE = [