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

124 lines
6.0 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
const { config } = await import('../server/config.js')
const { getProduct, productKeyForLookupKey, describeItems, priceIdsOfInvoice, creditMinutesForInvoice, termsDocFor, productView } =
await import('../server/services/products.js')
const line = (price, amount, proration = false) => ({
amount,
pricing: { price_details: { price } },
parent: { type: 'subscription_item_details', subscription_item_details: { proration } },
})
const keys = new Map([['price_w', 'wartung'], ['price_h', 'hosting'], ['price_s', 'mailStorage']])
test('Katalog: Preise, lookup_keys, Minuten, Mindestlaufzeit', () => {
assert.equal(getProduct('wartung').amountCents, 5000)
assert.equal(getProduct('hosting').amountCents, 1500)
assert.equal(getProduct('hosting').lookupKey, 'webklar_hosting_monatlich_1500')
assert.equal(getProduct('wartung').minutesPerMonth, 60)
assert.equal(getProduct('hosting').minutesPerMonth, 0)
assert.equal(getProduct('hosting').minTermMonths, 12)
assert.equal(config.mailStorage.lookupKey, 'webklar_mailspeicher_5gb_monatlich_100')
assert.equal(config.mailStorage.maxUnits, 10)
assert.throws(() => getProduct('gold'), (e) => e.status === 400)
})
test('Bisherige Werte bleiben gleich', () => {
assert.equal(config.stripe.priceLookupKey, 'webklar_wartung_monatlich_5000')
assert.equal(config.stripe.priceAmountCents, 5000)
assert.equal(config.stripe.productName, 'WEBklar Wartungs-Abo')
assert.equal(config.stripe.statementDescriptor, 'WEBKLAR WARTUNG')
assert.equal(config.abo.minutesPerMonth, 60)
assert.equal(config.abo.minTermMonths, 12)
})
test('lookup_key → Produkt', () => {
assert.equal(productKeyForLookupKey('webklar_wartung_monatlich_5000'), 'wartung')
assert.equal(productKeyForLookupKey('webklar_hosting_monatlich_1500'), 'hosting')
assert.equal(productKeyForLookupKey('webklar_mailspeicher_5gb_monatlich_100'), 'mailStorage')
assert.equal(productKeyForLookupKey('irgendwas'), null)
assert.equal(productKeyForLookupKey(undefined), null)
})
test('describeItems: Abo-Posten und Zusatzspeicher, Reihenfolge egal', () => {
const items = [
{ id: 'si_s', quantity: 2, price: { id: 'price_s', lookup_key: 'webklar_mailspeicher_5gb_monatlich_100', unit_amount: 100 } },
{ id: 'si_a', quantity: 1, price: { id: 'price_h', lookup_key: 'webklar_hosting_monatlich_1500', unit_amount: 1500 } },
]
assert.deepEqual(describeItems(items), {
productKey: 'hosting',
aboItemId: 'si_a',
aboPriceId: 'price_h',
lookupKey: 'webklar_hosting_monatlich_1500',
priceAmount: 1500,
storageItemId: 'si_s',
storageUnits: 2,
})
})
test('describeItems: ohne bekannten Abo-Preis', () => {
const d = describeItems([{ id: 'si_x', quantity: 1, price: { id: 'price_x', lookup_key: null, unit_amount: 4900 } }])
assert.deepEqual(d, {
productKey: null,
aboItemId: null,
aboPriceId: null,
lookupKey: null,
priceAmount: null,
storageItemId: null,
storageUnits: 0,
})
assert.deepEqual(describeItems(undefined).storageUnits, 0)
})
test('Stunden nur für eine reguläre Wartungs-Zeile über 0 €', () => {
const inv = (...lines) => ({ lines: { data: lines } })
assert.equal(creditMinutesForInvoice(inv(line('price_w', 5000)), keys), 60)
assert.equal(creditMinutesForInvoice(inv(line('price_h', 1500), line('price_s', 200)), keys), 0)
assert.equal(creditMinutesForInvoice(inv(line('price_w', 2300, true)), keys), 0) // anteilige Verrechnung
assert.equal(creditMinutesForInvoice(inv(line('price_w', 0)), keys), 0) // Probezeit
// unbekannter Preis neben einem bekannten Abo-Preis: keine Stunde
assert.equal(creditMinutesForInvoice(inv(line('price_h', 1500), line('price_x', 800)), keys), 0)
const invoiceItem = {
amount: 1200,
pricing: { price_details: { price: 'price_w' } },
parent: { type: 'invoice_item_details', invoice_item_details: { proration: true } },
}
assert.equal(creditMinutesForInvoice(inv(invoiceItem), keys), 0)
// Wechsel-Rechnung: Gutschrift Hosting (anteilig, negativ) und voller Wartungs-Monat
assert.equal(creditMinutesForInvoice(inv(line('price_h', -900, true), line('price_w', 5000)), keys), 60)
assert.equal(creditMinutesForInvoice({}, keys), 0)
})
test('Altbestand: Rechnung ohne bekannten Abo-Preis zählt als Wartung (wie im Abo-Spiegel)', () => {
const inv = (...lines) => ({ lines: { data: lines } })
assert.equal(creditMinutesForInvoice(inv(line('price_x', 5000)), keys), 60)
assert.equal(creditMinutesForInvoice(inv(line('price_x', 0)), keys), 0) // Probezeit bleibt ohne Stunde
assert.equal(creditMinutesForInvoice(inv(line('price_x', 2300, true)), keys), 0) // anteilig bleibt ohne Stunde
})
test('priceIdsOfInvoice: eindeutige Preis-IDs, ohne Lücken', () => {
const inv = { lines: { data: [line('price_w', 5000), line('price_w', -100, true), { amount: 5 }] } }
assert.deepEqual(priceIdsOfInvoice(inv), ['price_w'])
})
test('Vertragsunterlagen je Version', () => {
assert.equal(termsDocFor('2026-10-05-v4').pdfFile, 'WEBklar-Abo-Vertragsunterlagen.pdf')
assert.equal(termsDocFor('2026-09-25-v3').url, '/vertragsunterlagen/v3/abo-bedingungen.html')
assert.equal(termsDocFor(null).url, '/vertragsunterlagen/v3/abo-bedingungen.html')
})
test('Produktansicht enthält Leistungen ohne 1-Postfach-Regel bei Wartung', () => {
const w = productView('wartung').features.flat().join(' ')
assert.ok(w.includes('beliebig viele Postfächer') && !w.includes('1 E-Mail-Postfach'))
assert.ok(productView('hosting').features.flat().join(' ').includes('1 E-Mail-Postfach'))
})
// F07: die Wartungs-Beschreibung darf die alte 1-Postfach-Regel nicht mehr enthalten (Version 4:
// beliebig viele Postfächer, zusammen 5 GB). Der Stripe-Produkttext selbst wird erst beim
// Livegang ueber scripts/stripe-products.mjs gesetzt.
test('Wartung-Beschreibung ohne 1-Postfach-Regel (F07)', () => {
assert.ok(!getProduct('wartung').description.includes('1 E-Mail-Postfach'))
assert.ok(getProduct('wartung').description.includes('beliebig viele Postfächer'))
})