Files
Webklar-Kundenbereich/test/knBestaetigungsmails.test.js

121 lines
6.7 KiB
JavaScript
Raw Permalink 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 } 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'
import { mailSink } from './support/mailSink.js'
// K01 und K12 behaupten für Betriebe (Fassung 6 ohne Sofortbeginn-Zeitstempel) kein „ausdrücklich verlangt“
const aw = await startAppwriteMock({
schema: {
subscriptions: schemaKeys('subscriptions'),
contractEvents: schemaKeys('contractEvents'),
hourCredits: schemaKeys('hourCredits'),
featureConsents: schemaKeys('featureConsents'),
},
})
const st = await startFakeStripe()
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
Object.assign(process.env, { KUNDENANSICHT_NEU: 'pilot', KUNDENANSICHT_NEU_PILOT_EMAILS: 'K@example.invalid, k3@example.invalid' })
const { config } = await import('../server/config.js')
const { startTestApp, postWebhook } = await import('./support/testApp.js')
const { hostingDoc, consent, seedFixtures, stripeSubscription } = await import('./support/fixtures.js')
const { default: aboRoutes } = await import('../server/routes/abo.js')
const { switchToWartung } = await import('../server/services/planSwitch.js')
const { listContractEvents } = await import('../server/services/contractEvents.js')
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
const { setMailTransportForTests } = await import('../server/services/mailer.js')
const { loadTemplate, renderMail } = await import('../server/services/mailTemplates.js')
const app = await startTestApp({ '/api/abo': aboRoutes })
const mails = mailSink()
setMailTransportForTests(mails)
after(() => {
setMailTransportForTests(null)
return Promise.all([app.close(), aw.close(), st.close()])
})
seedFixtures(aw)
const price = (id, lookup_key, unit_amount) => ({ id, object: 'price', active: true, lookup_key, unit_amount, recurring: { interval: 'month' } })
st.prices.set('price_hosting', price('price_hosting', config.products.hosting.lookupKey, config.products.hosting.amountCents))
st.prices.set('price_wartung', price('price_wartung', config.products.wartung.lookupKey, config.products.wartung.amountCents))
st.prices.set('price_mailStorage', price('price_mailStorage', config.mailStorage.lookupKey, config.mailStorage.amountCents))
const nowUnix = () => Math.floor(Date.now() / 1000)
const K12_SUBJECT = 'Ihr Wechsel ins WEBklar Wartungs-Abo – Bestätigung'
const CARD = { id: 'pm_card_visa', object: 'payment_method', type: 'card' }
const sampleValues = (tpl) => Object.fromEntries(tpl.placeholders.map((p) => [p, `Wert-${p}`]))
const switchInvoice = (overrides = {}) => ({ id: 'in_wechsel', object: 'invoice', status: 'paid', amount_due: 4100, billing_reason: 'subscription_update', hosted_invoice_url: 'https://invoice.stripe.test/i/in_wechsel', ...overrides })
async function clearEvents(customerId) {
for (const e of await listContractEvents(customerId)) await deleteDocument(config.collections.contractEvents, e.$id)
}
async function resetHosting() {
await clearEvents('c1')
aw.seed('subscriptions', { ...hostingDoc })
st.subscriptions.set('sub_h', stripeSubscription({ id: 'sub_h', customerId: 'c1', productKey: 'hosting', default_payment_method: CARD }))
st.nextSubscriptionUpdate = null
mails.sent.length = 0
}
test('Vorlage K12: der Absatz „ausdrücklich verlangt“ hängt am Block sofortverlangen', () => {
const tpl = loadTemplate('K12')
const werte = sampleValues(tpl)
assert.ok(tpl.blocks.includes('sofortverlangen'))
assert.match(renderMail('K12', werte, { sofortverlangen: true }).text, /Sie haben ausdrücklich verlangt, dass wir vor Ablauf der Widerrufsfrist mit dem Wartungs-Abo beginnen/)
assert.doesNotMatch(renderMail('K12', werte, {}).text, /ausdrücklich verlangt|Wertersatz/)
assert.match(renderMail('K12', werte, {}).text, /haben wir am Wert-zustimmung_zeitpunkt Uhr erhalten\./, 'die Zustimmungszeile bleibt')
assert.deepEqual(renderMail('K12', werte, { sofortverlangen: true }).problems, [])
assert.deepEqual(renderMail('K12', werte, {}).problems, [])
})
test('K12 im Betrieb-Wechsel ohne den Satz, im Privat-Wechsel und in der alten Ansicht mit dem Satz', async () => {
const text = async (consentExtra) => {
await resetHosting()
st.nextSubscriptionUpdate = { latest_invoice: switchInvoice() }
const { earlyStartConsentAt: _x, ...ohne } = consent
await switchToWartung('c1', { ...ohne, ...consentExtra }, nowUnix())
return mails.sent.find((m) => m.subject === K12_SUBJECT).text
}
assert.doesNotMatch(await text({ kundenart: 'unternehmen' }), /ausdrücklich verlangt|Wertersatz/)
assert.match(await text({ kundenart: 'privat', earlyStartConsentAt: consent.consentAt }), /Sie haben ausdrücklich verlangt/)
assert.match(await text({ earlyStartConsentAt: consent.consentAt }), /Sie haben ausdrücklich verlangt/)
})
// Abo-IDs sind 28 Zeichen wie bei Stripe (sub_ + 24)
const SUB_NEW = 'sub_1SD6vPRxAbCdEfGhIjKlMnOp'
const checkoutCompleted = (subscriptionId, metadata = {}) => ({
id: `evt_${subscriptionId}`,
object: 'event',
type: 'checkout.session.completed',
created: Math.floor(Date.now() / 1000),
data: {
object: {
id: `cs_${subscriptionId}`, object: 'checkout.session', mode: 'subscription', subscription: subscriptionId, customer: 'cus_c3',
customer_details: { email: 'k3@example.invalid', name: 'Kunde Drei' },
metadata: { webklarCustomerId: 'c3', productKey: 'wartung', ...metadata },
},
},
})
async function cleanupWebhook(subscriptionId) {
await clearEvents('c3')
await deleteDocument(config.collections.subscriptions, subscriptionId).catch(() => {})
}
test('K01: Fassung 6 ohne Zeitstempel (Betrieb) ohne „ausdrücklich verlangt“, mit Zeitstempel und in Fassung 5 mit dem Satz', async () => {
const k01 = async (metadata) => {
st.subscriptions.set(SUB_NEW, stripeSubscription({ id: SUB_NEW, customerId: 'c3', productKey: 'wartung', metadata }))
mails.sent.length = 0
try {
assert.equal((await postWebhook(checkoutCompleted(SUB_NEW))).status, 200)
return mails.sent.find((m) => m.subject.includes('Vertragsbestätigung')).text
} finally {
await cleanupWebhook(SUB_NEW)
}
}
assert.doesNotMatch(await k01({ termsVersion: config.abo.termsVersionNeu, startChoice: 'sofort', earlyStartConsentAt: '' }), /ausdrücklich verlangt/)
assert.match(await k01({ termsVersion: config.abo.termsVersionNeu, startChoice: 'sofort', earlyStartConsentAt: new Date().toISOString() }), /ausdrücklich verlangt/)
assert.match(await k01({ termsVersion: config.abo.termsVersion, startChoice: 'sofort', earlyStartConsentAt: '' }), /ausdrücklich verlangt/)
})