Files
Webklar-Kundenbereich/test/featureCheckout.test.js
T

114 lines
6.6 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 } 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'
// createFeatureCheckout (Stripe-Checkout je Funktion aus dem Register config.features), direkter Aufruf gegen den Stripe-Nachbau
const aw = await startAppwriteMock({ schema: { subscriptions: schemaKeys('subscriptions') } })
const st = await startFakeStripe()
after(() => Promise.all([aw.close(), st.close()]))
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
const { config } = await import('../server/config.js')
const { createFeatureCheckout } = await import('../server/services/stripeBilling.js')
const customer = { $id: 'c1', name: 'Test Kunde', email: 'kunde@example.invalid', code: 'K-1' }
const CONSENT_AT = '2026-10-04T10:00:00.000Z'
const sessionCalls = () => st.calls.filter((c) => c.method === 'POST' && c.path === '/v1/checkout/sessions')
const lastCall = () => sessionCalls().at(-1)
const priceOf = (call) => st.prices.get(call.body['line_items[0][price]'])
test('News: Preis 3000, URLs und Rechnungsdaten wie bisher', async () => {
const created = await createFeatureCheckout(customer, { projectId: 'p1', key: 'news', consentAt: CONSENT_AT })
assert.match(created.id, /^cs_test_/)
assert.equal(created.url, `https://checkout.stripe.test/c/pay/${created.id}`)
const call = lastCall()
assert.equal(priceOf(call).unit_amount, 3000)
assert.equal(call.body['mode'], 'payment')
assert.equal(call.body['client_reference_id'], 'c1')
assert.equal(call.body['success_url'], `${config.publicBaseUrl}/dashboard.html#news?bestellt=1`)
assert.equal(call.body['cancel_url'], `${config.publicBaseUrl}/dashboard.html#news`)
assert.equal(call.body['invoice_creation[enabled]'], 'true')
assert.equal(call.body['invoice_creation[invoice_data][description]'], 'News-Pinnwand – Einrichtung')
assert.equal(call.body['invoice_creation[invoice_data][footer]'], config.stripe.invoiceFooter)
assert.equal(call.body['invoice_creation[invoice_data][metadata][featureKey]'], 'news')
assert.equal(call.body['invoice_creation[invoice_data][metadata][projectId]'], 'p1')
// Metadaten der Sitzung unveraendert
assert.equal(call.body['metadata[customerId]'], 'c1')
assert.equal(call.body['metadata[projectId]'], 'p1')
assert.equal(call.body['metadata[featureKey]'], 'news')
assert.equal(call.body['metadata[consentAt]'], CONSENT_AT)
// Ablauf in hoechstens 24 Stunden, Karte + SEPA
const now = Math.floor(Date.now() / 1000)
assert.ok(Number(call.body['expires_at']) > now)
assert.ok(Number(call.body['expires_at']) < now + 24 * 60 * 60)
assert.equal(call.body['payment_method_types[0]'], 'card')
assert.equal(call.body['payment_method_types[1]'], 'sepa_debit')
})
test('News: ohne consentAt steht kein consentAt in den Metadaten', async () => {
await createFeatureCheckout(customer, { projectId: 'p1' })
const call = lastCall()
assert.equal(call.body['metadata[featureKey]'], 'news', 'Standardschluessel news')
assert.ok(!('metadata[consentAt]' in call.body))
})
test('News: SEPA nicht verfuegbar -> zweiter Versuch nur mit Karte', async () => {
const before = sessionCalls().length
st.failNext({ method: 'POST', path: '/v1/checkout/sessions', status: 400, message: 'The payment method type "sepa_debit" is invalid.' })
const created = await createFeatureCheckout(customer, { projectId: 'p1', key: 'news', consentAt: CONSENT_AT })
const calls = sessionCalls().slice(before)
assert.equal(calls.length, 2)
assert.equal(calls[0].body['payment_method_types[1]'], 'sepa_debit')
assert.equal(calls[1].body['payment_method_types[0]'], 'card')
assert.ok(!('payment_method_types[1]' in calls[1].body), 'nur Karte')
assert.ok(created.id && created.url)
})
test('Termine: Preis 14900, Rechnungsdaten mit featureKey und projectId, URLs mit Projekt', async () => {
const created = await createFeatureCheckout(customer, { projectId: 'p/1', key: 'termine', consentAt: CONSENT_AT })
assert.equal(created.url, `https://checkout.stripe.test/c/pay/${created.id}`)
const call = lastCall()
assert.equal(priceOf(call).unit_amount, 14900)
assert.equal(priceOf(call).lookup_key, 'webklar_termine_einrichtung_14900')
assert.equal(call.body['invoice_creation[invoice_data][description]'], 'Terminplaner – Einrichtung')
assert.equal(call.body['invoice_creation[invoice_data][footer]'], config.stripe.invoiceFooter)
assert.equal(call.body['invoice_creation[invoice_data][metadata][featureKey]'], 'termine')
assert.equal(call.body['invoice_creation[invoice_data][metadata][projectId]'], 'p/1')
assert.equal(call.body['success_url'], `${config.publicBaseUrl}/dashboard.html#termine?bestellt=1&projekt=p%2F1`)
assert.equal(call.body['cancel_url'], `${config.publicBaseUrl}/dashboard.html#termine?projekt=p%2F1`)
assert.equal(call.body['metadata[customerId]'], 'c1')
assert.equal(call.body['metadata[projectId]'], 'p/1')
assert.equal(call.body['metadata[featureKey]'], 'termine')
assert.equal(call.body['metadata[consentAt]'], CONSENT_AT)
const now = Math.floor(Date.now() / 1000)
assert.ok(Number(call.body['expires_at']) > now)
assert.ok(Number(call.body['expires_at']) < now + 24 * 60 * 60)
assert.equal(call.body['payment_method_types[1]'], 'sepa_debit')
const sessionObj = st.checkoutSessions.get(created.id)
assert.equal(sessionObj.metadata.featureKey, 'termine')
})
test('Termine: SEPA nicht verfuegbar -> zweiter Versuch nur mit Karte, gleiche URLs', async () => {
const before = sessionCalls().length
st.failNext({ method: 'POST', path: '/v1/checkout/sessions', status: 400, message: 'The payment method type "sepa_debit" is invalid.' })
await createFeatureCheckout(customer, { projectId: 'p2', key: 'termine', consentAt: CONSENT_AT })
const calls = sessionCalls().slice(before)
assert.equal(calls.length, 2)
assert.ok(!('payment_method_types[1]' in calls[1].body), 'nur Karte')
assert.equal(calls[1].body['success_url'], `${config.publicBaseUrl}/dashboard.html#termine?bestellt=1&projekt=p2`)
assert.equal(calls[1].body['cancel_url'], `${config.publicBaseUrl}/dashboard.html#termine?projekt=p2`)
})
test('unbekannter Schluessel: Fehler 400 vor jedem Stripe-Aufruf', async () => {
const before = st.calls.length
for (const key of ['analyse', '__proto__', 'toString', '']) {
await assert.rejects(
createFeatureCheckout(customer, { projectId: 'p1', key, consentAt: CONSENT_AT }),
(err) => err.status === 400
)
}
assert.equal(st.calls.length, before, 'kein einziger Stripe-Aufruf')
})