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>
This commit is contained in:
1 parent
cb6291410d
commit
d4b9e6a5b9
5 files changed
+55
-4
No files matched your search
@@ -25,6 +25,7 @@ after(async () => {
|
||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
||||
|
||||
const { config } = await import('../server/config.js')
|
||||
const { withLocks } = await import('../server/services/keyedLock.js')
|
||||
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
|
||||
const K = await import('../server/services/shop/kontext.js')
|
||||
const B = await import('../server/services/shop/bestellungen.js')
|
||||
@@ -138,6 +139,24 @@ test('pruefeRueckUrl: eigene Hosts und Portal ja, fremde, http, Zugangsdaten nei
|
||||
assert.equal(B.pruefeRueckUrl('http://www.brot.example/', ctx), 'http://127.0.0.1:4000/shop/' + SITE_KEY)
|
||||
})
|
||||
|
||||
test('M9: Wartezeit auf die Bestandssperre verkuerzt die Sitzungsdauer nicht - expires_at und ablaufAt werden vor dem Stripe-Aufruf nachgefuehrt', async () => {
|
||||
const ctx = await aufbau()
|
||||
let los
|
||||
const gehalten = new Promise((r) => (los = r))
|
||||
const sperre = withLocks([`shop-bestand:${FEATURE}`], () => gehalten)
|
||||
const lauf = B.legeBestellungAn(ctx, eingabe(), { now: T0 })
|
||||
await new Promise((r) => setTimeout(r, 2100))
|
||||
los()
|
||||
await sperre
|
||||
await lauf
|
||||
const [call] = sessionCalls()
|
||||
const ende = Number(call.body.expires_at)
|
||||
const grundwert = Math.ceil(T0.getTime() / 1000) + 30 * 60 + 30
|
||||
assert.ok(ende >= grundwert + 2, `expires_at ${ende} soll mindestens 2 s nach ${grundwert} liegen`)
|
||||
assert.ok(ende <= grundwert + 6)
|
||||
assert.equal(new Date(orders()[0].ablaufAt).getTime(), ende * 1000, 'ablaufAt = Ende der Stripe-Sitzung')
|
||||
})
|
||||
|
||||
test('Normalfall: Bestellung offen, Stripe-Kasse auf dem Haendlerkonto mit allen Parametern', async () => {
|
||||
const ctx = await aufbau()
|
||||
const r = await B.legeBestellungAn(ctx, eingabe(), { now: T0 })
|
||||
|
||||
+19
-2
@@ -67,14 +67,16 @@ test('gemischte Steuersaetze: Text mit absteigenden Saetzen, 0 % liefert keinen
|
||||
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), positionen: eine(a, 3), lieferart: 'abholung', einstellungen: e })
|
||||
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 abh = (p, n = 1) => berechne({ produkte: karte(p), positionen: eine(p, n), lieferart: 'abholung', einstellungen: einstellungen() })
|
||||
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, [])
|
||||
@@ -348,6 +350,21 @@ test('Fehler mindestbestellwert: Grenze gleich ist erlaubt, Versandkosten zaehle
|
||||
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')
|
||||
|
||||
Reference in new issue
Block a user