121 lines
6.7 KiB
JavaScript
121 lines
6.7 KiB
JavaScript
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/)
|
||
})
|