Files
Webklar-Kundenbereich/test/shopPreise.test.js
T
ClaudeandClaude Opus 5.5 d4b9e6a5b9 Shop Review A M9: Mindestbetrag 0,50 EUR (Stripe) und Sitzungsende direkt vor dem Stripe-Aufruf nachfuehren
berechne lehnt Gesamtbetraege unter 0,50 EUR mit dem Code mindestbetrag und einem Kaeufertext ab. legeBestellungAn schiebt expires_at um die seit Beginn vergangenen ganzen Sekunden (Warten auf Sperren) weiter und fuehrt ablaufAt auf denselben Wert nach, damit Stripes 30-Minuten-Minimum sicher bleibt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:35:17 +00:00

397 lines
21 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 } from 'node:test'
import assert from 'node:assert/strict'
import { berechne } from '../server/services/shop/preise.js'
import { euro } from '../server/services/shop/geld.js'
const basis = {
aktiv: true, kurz: '', beschreibung: '', steuersatz: 19, grundpreis: null, bestand: null, varianten: null,
bilder: [], lieferart: 'beides', widerruf: 'gesetzlich',
}
const produkt = (id, extra = {}) => ({ ...basis, id, name: `Artikel ${id}`, preisCent: 1000, ...extra })
const karte = (...liste) => new Map(liste.map((p) => [p.id, p]))
const shirt = produkt('shirt', {
name: 'T-Shirt',
preisCent: null,
varianten: {
name: 'Größe',
werte: [
{ id: 'sizeaaaa', name: 'M', preisCent: 2000, bestand: 5 },
{ id: 'sizebbbb', name: 'L', preisCent: 2200, bestand: null },
],
},
})
const einstellungen = (extra = {}) => ({
kleinunternehmer: false,
versand: { aktiv: true, laender: ['DE', 'AT'], kostenCent: { DE: 490, AT: 990 }, freiAbCent: null },
abholung: { aktiv: true, adresse: 'Weg 1', hinweis: '' },
mindestbestellCent: null,
...extra,
})
const rechne = (args) => berechne({ lieferart: 'versand', land: 'DE', einstellungen: einstellungen(), ...args })
const eine = (p, menge = 1, varianteId) => [{ produktId: p.id, menge, ...(varianteId ? { varianteId } : {}) }]
test('einfacher Warenkorb: Summen, Texte, Versand und Steuer', () => {
const a = produkt('a', { preisCent: 1190 })
const r = rechne({ produkte: karte(a), positionen: eine(a, 2) })
assert.equal(r.ok, true)
assert.equal(r.zwischensummeCent, 2380)
assert.equal(r.versandCent, 490)
assert.equal(r.gesamtCent, 2870)
assert.deepEqual(r.positionen[0], {
produktId: 'a', varianteId: null, name: 'Artikel a', varianteText: '', menge: 2, einzelCent: 1190, summeCent: 2380,
steuersatz: 19, widerruf: 'gesetzlich', einzelText: euro(1190), summeText: euro(2380),
})
assert.equal(r.texte.zwischensumme, euro(2380))
assert.equal(r.texte.versand, euro(490))
assert.equal(r.texte.gesamt, euro(2870))
// 2870 * 19 / 119 = 458,23 => 458
assert.deepEqual(r.steuer, [{ satz: 19, betragCent: 458, text: `inkl. 19 % USt: ${euro(458)}` }])
assert.equal(r.texte.steuerHinweis, `inkl. 19 % USt: ${euro(458)}`)
assert.equal(r.lieferart, 'versand')
assert.equal(r.land, 'DE')
})
test('gemischte Steuersaetze: Text mit absteigenden Saetzen, 0 % liefert keinen Eintrag', () => {
const a = produkt('a', { preisCent: 2000, steuersatz: 19 })
const b = produkt('b', { preisCent: 700, steuersatz: 7 })
const c = produkt('c', { preisCent: 500, steuersatz: 0 })
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: 1 } })
const r = berechne({ produkte: karte(a, b, c), positionen: [...eine(b), ...eine(c), ...eine(a)], lieferart: 'versand', land: 'DE', einstellungen: e })
assert.equal(r.versandCent, 0)
assert.equal(r.texte.versand, 'kostenlos')
assert.deepEqual(r.steuer.map((s) => [s.satz, s.betragCent]), [[19, 319], [7, 46]])
assert.equal(r.texte.steuerHinweis, `inkl. 19 % USt: ${euro(319)}; inkl. 7 % USt: ${euro(46)}`)
assert.deepEqual(r.positionen.map((p) => p.produktId), ['b', 'c', 'a'], 'Reihenfolge der Eingabe bleibt')
})
test('Steuer wird je Satz ueber die Summe gerundet, nicht je Position', () => {
// Einzeln: 3 * round(0,119 * ... ) waere 3 * 0 ; ueber die Summe: 3 * 10 = 30 Cent brutto => 30*19/119 = 4,79 => 5
const a = produkt('a', { preisCent: 10 })
const fueller = produkt('fueller', { preisCent: 100, steuersatz: 0 }) // hebt den Gesamtbetrag ueber 0,50 EUR, ohne Steuer
const e = einstellungen({ abholung: { aktiv: true, adresse: 'x', hinweis: '' } })
const r = berechne({ produkte: karte(a, fueller), positionen: [...eine(a, 3), ...eine(fueller)], lieferart: 'abholung', einstellungen: e })
assert.equal(r.steuer[0].betragCent, 5)
})
test('Rundung halb auf an der Grenze', () => {
// brutto 107 bei 7 % => 107*7/107 = 7 exakt; brutto 50 bei 7 % => 3,271 => 3; brutto 1 bei 19 % => 0,159 => 0 (kein Eintrag)
const fueller = produkt('fueller', { preisCent: 100, steuersatz: 0 }) // Gesamtbetrag mindestens 0,50 EUR, selbst ohne Steuer
const abh = (p, n = 1) => berechne({ produkte: karte(p, fueller), positionen: [...eine(p, n), ...eine(fueller)], lieferart: 'abholung', einstellungen: einstellungen() })
assert.equal(abh(produkt('a', { preisCent: 107, steuersatz: 7 })).steuer[0].betragCent, 7)
assert.equal(abh(produkt('a', { preisCent: 50, steuersatz: 7 })).steuer[0].betragCent, 3)
assert.deepEqual(abh(produkt('a', { preisCent: 1, steuersatz: 19 })).steuer, [])
assert.equal(abh(produkt('a', { preisCent: 1, steuersatz: 19 })).texte.steuerHinweis, '')
// brutto 5 bei 19 % => 0,798 => 1
assert.equal(abh(produkt('a', { preisCent: 5, steuersatz: 19 })).steuer[0].betragCent, 1)
// brutto 30 bei 7 % => 1,962 => 2
assert.equal(abh(produkt('a', { preisCent: 30, steuersatz: 7 })).steuer[0].betragCent, 2)
})
test('Versand wird anteilig nach Bruttowert auf die Saetze verteilt, Rest-Cent auf den hoechsten Satz', () => {
const a = produkt('a', { preisCent: 1000, steuersatz: 19 })
const b = produkt('b', { preisCent: 1000, steuersatz: 7 })
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 501 }, freiAbCent: null } })
const r = berechne({ produkte: karte(b, a), positionen: [...eine(b), ...eine(a)], lieferart: 'versand', land: 'DE', einstellungen: e })
// Anteile: floor(501*1000/2000) = 250 je Satz, Rest 1 => 19 %: 1251, 7 %: 1250
assert.equal(r.gesamtCent, 2501)
assert.deepEqual(r.steuer.map((s) => [s.satz, s.betragCent]), [
[19, Math.floor((2 * 1251 * 19 + 119) / (2 * 119))],
[7, Math.floor((2 * 1250 * 7 + 107) / (2 * 107))],
])
assert.equal(r.steuer[0].betragCent, 200)
assert.equal(r.steuer[1].betragCent, 82)
})
test('Versand bei nur 0-%-Artikeln erzeugt keine Steuer', () => {
const a = produkt('a', { preisCent: 1000, steuersatz: 0 })
const r = rechne({ produkte: karte(a), positionen: eine(a) })
assert.deepEqual(r.steuer, [])
assert.equal(r.gesamtCent, 1490)
})
test('Kleinunternehmer: keine Steuer, Hinweis nach Paragraph 19', () => {
const a = produkt('a', { preisCent: 1000, steuersatz: 0 })
const r = rechne({ produkte: karte(a), positionen: eine(a), einstellungen: einstellungen({ kleinunternehmer: true }) })
assert.deepEqual(r.steuer, [])
assert.equal(r.texte.steuerHinweis, 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.')
// auch wenn ein Artikel (fehlerhaft) einen Satz traegt
const b = produkt('b', { preisCent: 1000, steuersatz: 19 })
const r2 = rechne({ produkte: karte(b), positionen: eine(b), einstellungen: einstellungen({ kleinunternehmer: true }) })
assert.deepEqual(r2.steuer, [])
})
test('Versandkosten: Oesterreich, Schwelle gleich ist kostenlos, knapp darunter nicht', () => {
const e = einstellungen({ versand: { aktiv: true, laender: ['DE', 'AT'], kostenCent: { DE: 490, AT: 990 }, freiAbCent: 5000 } })
const p = (cent) => produkt('a', { preisCent: cent })
const lauf = (cent, land) => {
const a = p(cent)
return berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'versand', land, einstellungen: e })
}
assert.equal(lauf(2000, 'AT').versandCent, 990)
assert.equal(lauf(2000, 'AT').texte.versand, euro(990))
assert.equal(lauf(4999, 'DE').versandCent, 490)
assert.equal(lauf(5000, 'DE').versandCent, 0)
assert.equal(lauf(5000, 'AT').versandCent, 0)
assert.equal(lauf(5001, 'DE').texte.versand, 'kostenlos')
assert.equal(lauf(5000, 'DE').gesamtCent, 5000)
})
test('freiAbCent 0 heisst immer kostenlos', () => {
const a = produkt('a')
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: 0 } })
assert.equal(berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'versand', land: 'DE', einstellungen: e }).versandCent, 0)
})
test('Abholung: keine Versandkosten, eigener Text, Land ignoriert', () => {
const a = produkt('a', { preisCent: 1190 })
const r = berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: einstellungen() })
assert.equal(r.ok, true)
assert.equal(r.versandCent, 0)
assert.equal(r.gesamtCent, 1190)
assert.equal(r.texte.versand, 'Abholung – kostenlos')
assert.equal(r.lieferart, 'abholung')
})
test('Abholung nur-Abholung-Artikel sind bei Abholung erlaubt', () => {
const a = produkt('a', { lieferart: 'abholung' })
const r = berechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: einstellungen() })
assert.equal(r.ok, true)
})
test('Varianten: Preis und Text der Variante', () => {
const r = rechne({ produkte: karte(shirt), positionen: eine(shirt, 2, 'sizebbbb') })
assert.equal(r.ok, true)
assert.equal(r.positionen[0].varianteId, 'sizebbbb')
assert.equal(r.positionen[0].varianteText, 'Größe: L')
assert.equal(r.positionen[0].einzelCent, 2200)
assert.equal(r.positionen[0].summeCent, 4400)
})
test('Fehler leer: leere, fehlende oder keine Liste', () => {
const a = produkt('a')
for (const positionen of [[], undefined, null, 'x', {}]) {
const r = rechne({ produkte: karte(a), positionen })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'leer')
assert.match(r.fehler.text, /Warenkorb/)
}
})
test('Fehler zu_viele: 51 verschiedene Positionen, aber 50 sind erlaubt und Duplikate zaehlen einmal', () => {
const liste = Array.from({ length: 51 }, (_, i) => produkt(`p${i}`))
const positionen = (n) => liste.slice(0, n).map((p) => ({ produktId: p.id, menge: 1 }))
const r51 = rechne({ produkte: karte(...liste), positionen: positionen(51) })
assert.equal(r51.ok, false)
assert.equal(r51.fehler.code, 'zu_viele')
assert.equal(rechne({ produkte: karte(...liste), positionen: positionen(50) }).ok, true)
// 51 Eintraege, aber nur 50 verschiedene
const dup = [...positionen(50), { produktId: 'p0', menge: 1 }]
const r = rechne({ produkte: karte(...liste), positionen: dup })
assert.equal(r.ok, true)
assert.equal(r.positionen.length, 50)
assert.equal(r.positionen[0].menge, 2)
})
test('Fehler menge: 0, 100, negativ, Dezimal, Text, NaN, fehlend', () => {
const a = produkt('a')
for (const menge of [0, 100, -1, 1.5, '2', NaN, Infinity, undefined, null]) {
const r = rechne({ produkte: karte(a), positionen: [{ produktId: 'a', menge }] })
assert.equal(r.ok, false, String(menge))
assert.equal(r.fehler.code, 'menge', String(menge))
assert.equal(r.fehler.produktId, 'a')
}
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 99) }).ok, true)
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 1) }).ok, true)
})
test('Zusammengefasste Menge ueber 99 ist ein Mengenfehler', () => {
const a = produkt('a')
const r = rechne({ produkte: karte(a), positionen: [{ produktId: 'a', menge: 60 }, { produktId: 'a', menge: 40 }] })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'menge')
assert.equal(r.fehler.produktId, 'a')
})
test('Duplikate gleicher Artikel werden zusammengefasst, Varianten bleiben getrennt', () => {
const a = produkt('a', { preisCent: 300 })
const r = rechne({
produkte: karte(a, shirt),
positionen: [
{ produktId: 'a', menge: 1 },
{ produktId: 'shirt', varianteId: 'sizeaaaa', menge: 1 },
{ produktId: 'a', menge: 2 },
{ produktId: 'shirt', varianteId: 'sizebbbb', menge: 1 },
{ produktId: 'shirt', varianteId: 'sizeaaaa', menge: 1 },
],
})
assert.equal(r.ok, true)
assert.deepEqual(r.positionen.map((p) => [p.produktId, p.varianteId, p.menge]), [
['a', null, 3], ['shirt', 'sizeaaaa', 2], ['shirt', 'sizebbbb', 1],
])
assert.equal(r.zwischensummeCent, 900 + 4000 + 2200)
})
test('Fehler nicht_verfuegbar: unbekannt, inaktiv, ungueltige Position', () => {
const a = produkt('a')
const aus = produkt('aus', { aktiv: false })
for (const pos of [{ produktId: 'x', menge: 1 }, { produktId: 'aus', menge: 1 }, { produktId: '__proto__', menge: 1 }, { produktId: 'constructor', menge: 1 }]) {
const r = rechne({ produkte: karte(a, aus), positionen: [pos] })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'nicht_verfuegbar')
}
assert.equal(rechne({ produkte: karte(a), positionen: [null] }).fehler.code, 'nicht_verfuegbar')
assert.equal(rechne({ produkte: karte(a), positionen: [{ menge: 1 }] }).fehler.code, 'nicht_verfuegbar')
assert.equal(rechne({ produkte: karte(a), positionen: [{ produktId: 5, menge: 1 }] }).fehler.code, 'nicht_verfuegbar')
assert.equal(rechne({ produkte: karte(aus), positionen: eine(aus) }).fehler.produktId, 'aus')
})
test('Fehler variante: fehlt, unbekannt, bei Artikel ohne Varianten', () => {
const a = produkt('a')
const ohne = rechne({ produkte: karte(shirt), positionen: eine(shirt) })
assert.equal(ohne.fehler.code, 'variante')
assert.match(ohne.fehler.text, /Größe/)
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 1, 'gibtsnich') }).fehler.code, 'variante')
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 1, 'sizeaaaa') }).fehler.code, 'variante')
assert.equal(ohne.fehler.produktId, 'shirt')
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 1, 'sizeaaaa') }).ok, true)
})
test('Bestand: unbegrenzt, genau ausreichend, zu wenig mit Restmenge, ausverkauft', () => {
const frei = produkt('frei', { bestand: null })
assert.equal(rechne({ produkte: karte(frei), positionen: eine(frei, 99) }).ok, true)
const a = produkt('a', { bestand: 3 })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 3) }).ok, true)
const zuwenig = rechne({ produkte: karte(a), positionen: eine(a, 4) })
assert.equal(zuwenig.ok, false)
assert.equal(zuwenig.fehler.code, 'ausverkauft')
assert.equal(zuwenig.fehler.produktId, 'a')
assert.match(zuwenig.fehler.text, /Artikel a/)
assert.match(zuwenig.fehler.text, /nur noch 3/)
const null0 = produkt('n', { bestand: 0 })
const aus = rechne({ produkte: karte(null0), positionen: eine(null0) })
assert.equal(aus.fehler.code, 'ausverkauft')
assert.match(aus.fehler.text, /Artikel n/)
assert.match(aus.fehler.text, /ausverkauft/)
})
test('Bestand: gilt fuer die zusammengefasste Menge', () => {
const a = produkt('a', { bestand: 3 })
const r = rechne({ produkte: karte(a), positionen: [{ produktId: 'a', menge: 2 }, { produktId: 'a', menge: 2 }] })
assert.equal(r.fehler.code, 'ausverkauft')
})
test('Reservierungen verringern den Bestand (Artikel und Variante)', () => {
const a = produkt('a', { bestand: 5 })
const res = new Map([['a', 3]])
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 2), reserviert: res }).ok, true)
const r = rechne({ produkte: karte(a), positionen: eine(a, 3), reserviert: res })
assert.equal(r.fehler.code, 'ausverkauft')
assert.match(r.fehler.text, /nur noch 2/)
assert.equal(rechne({ produkte: karte(a), positionen: eine(a, 1), reserviert: new Map([['a', 7]]) }).fehler.code, 'ausverkauft')
const vres = new Map([['shirt:sizeaaaa', 4]])
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 1, 'sizeaaaa'), reserviert: vres }).ok, true)
const rv = rechne({ produkte: karte(shirt), positionen: eine(shirt, 2, 'sizeaaaa'), reserviert: vres })
assert.equal(rv.fehler.code, 'ausverkauft')
assert.match(rv.fehler.text, /T-Shirt/)
assert.match(rv.fehler.text, /Größe: M/)
// unbegrenzte Variante bleibt unberuehrt, andere Schluessel stoeren nicht
assert.equal(rechne({ produkte: karte(shirt), positionen: eine(shirt, 50, 'sizebbbb'), reserviert: new Map([['shirt:sizebbbb', 99], ['shirt', 99]]) }).ok, true)
})
test('Variantenbestand wird je Variante geprueft', () => {
const r = rechne({ produkte: karte(shirt), positionen: eine(shirt, 6, 'sizeaaaa') })
assert.equal(r.fehler.code, 'ausverkauft')
assert.match(r.fehler.text, /nur noch 5/)
})
test('Fehler lieferart: unbekannt, Versand aus, Abholung aus', () => {
const a = produkt('a')
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'post' }).fehler.code, 'lieferart')
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: undefined }).fehler.code, 'lieferart')
const keinVersand = einstellungen({ versand: { aktiv: false, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), einstellungen: keinVersand }).fehler.code, 'lieferart')
const keineAbholung = einstellungen({ abholung: { aktiv: false, adresse: '', hinweis: '' } })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: keineAbholung }).fehler.code, 'lieferart')
// Abholung geht, obwohl Versand aus ist
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: einstellungen({ versand: { aktiv: false, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } }) }).ok, true)
})
test('Fehler land: nicht geliefert, fehlend, ungueltig', () => {
const a = produkt('a')
const nurDe = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } })
for (const land of ['AT', 'CH', '', undefined, null, 'de']) {
const r = rechne({ produkte: karte(a), positionen: eine(a), land, einstellungen: nurDe })
assert.equal(r.fehler.code, 'land', String(land))
}
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), land: 'AT' }).ok, true)
})
test('Fehler nur_abholung: Versand mit Nur-Abholung-Artikel', () => {
const a = produkt('a')
const abholer = produkt('abholer', { lieferart: 'abholung' })
const r = rechne({ produkte: karte(a, abholer), positionen: [...eine(a), ...eine(abholer)] })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'nur_abholung')
assert.equal(r.fehler.produktId, 'abholer')
assert.match(r.fehler.text, /Artikel abholer/)
})
test('Fehler mindestbestellwert: Grenze gleich ist erlaubt, Versandkosten zaehlen nicht mit', () => {
const a = produkt('a', { preisCent: 1000 })
const e = (cent) => einstellungen({ mindestbestellCent: cent })
const r = rechne({ produkte: karte(a), positionen: eine(a), einstellungen: e(1001) })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'mindestbestellwert')
assert.ok(r.fehler.text.includes(euro(1001)))
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), einstellungen: e(1000) }).ok, true)
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), einstellungen: e(null) }).ok, true)
// gilt auch bei Abholung
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'abholung', einstellungen: e(1001) }).fehler.code, 'mindestbestellwert')
})
test('Fehler mindestbetrag: Gesamtbetrag unter 0,50 EUR (Stripe-Minimum) wird abgelehnt, 0,50 EUR genau ist erlaubt; Versandkosten zaehlen mit', () => {
const klein = produkt('klein', { preisCent: 30 })
const r = rechne({ produkte: karte(klein), positionen: eine(klein), lieferart: 'abholung' })
assert.equal(r.ok, false)
assert.equal(r.fehler.code, 'mindestbetrag')
assert.equal(r.fehler.text, 'Der Bestellwert muss mindestens 0,50 € betragen.')
const genau = produkt('genau', { preisCent: 50 })
assert.equal(rechne({ produkte: karte(genau), positionen: eine(genau), lieferart: 'abholung' }).ok, true)
const knapp = produkt('knapp', { preisCent: 49 })
assert.equal(rechne({ produkte: karte(knapp), positionen: eine(knapp), lieferart: 'abholung' }).fehler.code, 'mindestbetrag')
// mit Versandkosten ueber der Grenze
const e = einstellungen({ versand: { aktiv: true, laender: ['DE'], kostenCent: { DE: 490 }, freiAbCent: null } })
assert.equal(rechne({ produkte: karte(klein), positionen: eine(klein), lieferart: 'versand', land: 'DE', einstellungen: e }).ok, true)
})
test('Reihenfolge der Pruefungen: Artikelfehler vor Lieferart, Lieferart vor Mindestbestellwert', () => {
const a = produkt('a', { bestand: 0 })
assert.equal(rechne({ produkte: karte(a), positionen: eine(a), lieferart: 'post' }).fehler.code, 'ausverkauft')
const b = produkt('b')
assert.equal(rechne({ produkte: karte(b), positionen: eine(b), lieferart: 'post', einstellungen: einstellungen({ mindestbestellCent: 99999 }) }).fehler.code, 'lieferart')
})
test('Eingaben werden nicht veraendert und das Ergebnis teilt keine Referenzen', () => {
const a = produkt('a', { bestand: 10 })
const positionen = [{ produktId: 'a', menge: 2 }, { produktId: 'a', menge: 1 }]
const e = einstellungen()
const vorher = JSON.stringify({ a, positionen, e })
const r1 = rechne({ produkte: karte(a), positionen, einstellungen: e })
assert.equal(JSON.stringify({ a, positionen, e }), vorher)
const r2 = rechne({ produkte: karte(a), positionen, einstellungen: e })
assert.deepEqual(r1, r2)
assert.notEqual(r1.positionen, r2.positionen)
})
test('Gesamtsumme ist immer ganzzahlig und gleich Zwischensumme plus Versand', () => {
const a = produkt('a', { preisCent: 333, steuersatz: 7 })
const b = produkt('b', { preisCent: 777, steuersatz: 19 })
const r = rechne({ produkte: karte(a, b), positionen: [{ produktId: 'a', menge: 7 }, { produktId: 'b', menge: 3 }], land: 'AT' })
assert.ok(Number.isInteger(r.gesamtCent))
assert.equal(r.gesamtCent, r.zwischensummeCent + r.versandCent)
assert.equal(r.zwischensummeCent, 333 * 7 + 777 * 3)
const summe = r.steuer.reduce((s, x) => s + x.betragCent, 0)
assert.ok(summe < r.gesamtCent)
})