141 lines
5.3 KiB
JavaScript
141 lines
5.3 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'
|
|
|
|
const aw = await startAppwriteMock({
|
|
schema: { contractEvents: schemaKeys('contractEvents'), subscriptions: schemaKeys('subscriptions') },
|
|
})
|
|
const st = await startFakeStripe()
|
|
after(() => Promise.all([aw.close(), st.close()]))
|
|
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
|
const {
|
|
recordContractEvent,
|
|
listContractEvents,
|
|
openWithdrawals,
|
|
listOpenStorageTasks,
|
|
completeContractEvent,
|
|
updateContractEvent,
|
|
} = await import('../server/services/contractEvents.js')
|
|
|
|
test('Ereignis speichern und lesen (details als JSON)', async () => {
|
|
await recordContractEvent({
|
|
customerId: 'c1',
|
|
subscriptionId: 'sub_1',
|
|
type: 'speicher',
|
|
productKey: 'hosting',
|
|
details: { fromUnits: 0, toUnits: 2 },
|
|
consent: {
|
|
termsVersion: '2026-10-05-v4',
|
|
consentAt: '2026-10-05T10:00:00.000Z',
|
|
consentIp: '1.2.3.4',
|
|
consentUserAgent: 'UA',
|
|
},
|
|
status: 'offen',
|
|
dueAt: '2026-10-05T10:00:00.000Z',
|
|
})
|
|
const [e] = await listContractEvents('c1')
|
|
assert.deepEqual(e.details, { fromUnits: 0, toUnits: 2 })
|
|
assert.equal((await listOpenStorageTasks()).length, 1)
|
|
})
|
|
|
|
test('Widerrufsfristen je Bestellung', () => {
|
|
const ev = (type, consentAt, details = {}, status = 'erledigt') => ({ $id: type, type, consentAt, details, status })
|
|
const now = new Date('2026-10-10T12:00:00Z')
|
|
const open = openWithdrawals(
|
|
[
|
|
ev('abschluss', '2026-09-01T10:00:00Z'),
|
|
ev('wechsel', '2026-10-05T10:00:00Z'),
|
|
ev('speicher', '2026-10-06T10:00:00Z', { fromUnits: 2, toUnits: 1 }),
|
|
ev('speicher', '2026-10-07T10:00:00Z', { fromUnits: 0, toUnits: 1 }),
|
|
],
|
|
now
|
|
)
|
|
assert.deepEqual(open.map((o) => o.type), ['wechsel', 'speicher'])
|
|
assert.equal(openWithdrawals([ev('wechsel', '2026-10-05T10:00:00Z', {}, 'widerrufen')], now).length, 0)
|
|
// F02: ein Wechsel ohne Zahlung (gescheitert) hat wie ein widerrufener kein Widerrufsrecht mehr
|
|
assert.equal(openWithdrawals([ev('wechsel', '2026-10-05T10:00:00Z', {}, 'gescheitert')], now).length, 0)
|
|
})
|
|
|
|
// F04: die Frist zaehlt ab dem Vertragsschluss bei Stripe (details.contractAt), nicht ab dem fruehereren
|
|
// Portal-Klick (consentAt). Waere hier consentAt massgeblich, waere die Frist laengst abgelaufen.
|
|
test('Widerrufsfrist zaehlt ab dem Vertragsschluss (details.contractAt), nicht ab dem Portal-Klick', () => {
|
|
const now = new Date('2026-10-10T12:00:00Z')
|
|
const ev = {
|
|
$id: 'e1',
|
|
type: 'abschluss',
|
|
status: 'erledigt',
|
|
consentAt: '2026-08-01T10:00:00Z',
|
|
details: { contractAt: '2026-10-08T10:00:00Z' },
|
|
}
|
|
assert.deepEqual(openWithdrawals([ev], now).map((o) => o.eventId), ['e1'])
|
|
})
|
|
|
|
test('Aufgabe erledigen', async () => {
|
|
const [t] = await listOpenStorageTasks()
|
|
const done = await completeContractEvent(t.$id, 'kenso@webklar.com')
|
|
assert.equal(done.status, 'erledigt')
|
|
assert.equal(done.doneBy, 'kenso@webklar.com')
|
|
})
|
|
|
|
// F01: Status setzen und details zusammenfuehren (nicht ersetzen) in einem Aufruf
|
|
test('Ereignis aktualisieren: Status setzen, details zusammenfuehren', async () => {
|
|
const created = await recordContractEvent({
|
|
customerId: 'c2',
|
|
subscriptionId: 'sub_2',
|
|
type: 'wechsel',
|
|
productKey: 'hosting',
|
|
details: { von: 'wartung', zu: 'hosting' },
|
|
status: 'offen',
|
|
})
|
|
const updated = await updateContractEvent(created.$id, {
|
|
status: 'gescheitert',
|
|
details: { grund: 'Zahlung fehlgeschlagen' },
|
|
})
|
|
assert.equal(updated.status, 'gescheitert')
|
|
assert.deepEqual(JSON.parse(updated.details), { von: 'wartung', zu: 'hosting', grund: 'Zahlung fehlgeschlagen' })
|
|
})
|
|
|
|
// F05: dieselbe id zweimal speichern (z. B. Webhook-Wiederholung) ist kein Fehler, sondern liefert
|
|
// das schon vorhandene Dokument zurueck, ohne ein zweites anzulegen
|
|
test('Ereignis mit vorhandener id (409): liefert das schon gespeicherte Dokument', async () => {
|
|
const first = await recordContractEvent({
|
|
customerId: 'c3',
|
|
subscriptionId: 'sub_3',
|
|
type: 'abschluss',
|
|
productKey: 'hosting',
|
|
details: { contractAt: '2026-10-01T09:00:00.000Z' },
|
|
id: 'abschluss-sub_3',
|
|
})
|
|
const again = await recordContractEvent({
|
|
customerId: 'c3',
|
|
subscriptionId: 'sub_3',
|
|
type: 'abschluss',
|
|
productKey: 'hosting',
|
|
details: { contractAt: '2026-10-01T09:00:00.000Z' },
|
|
id: 'abschluss-sub_3',
|
|
})
|
|
assert.equal(again.$id, 'abschluss-sub_3')
|
|
assert.equal(again.$id, first.$id)
|
|
assert.equal((await listContractEvents('c3')).length, 1)
|
|
})
|
|
|
|
// Rechtsrunde 28.09.2026: eine Speicher-Verringerung hat kein Pflicht-Haekchen und damit keine
|
|
// Zustimmungsdaten - recordContractEvent muss mit consent: null funktionieren und darf dann keine
|
|
// consentIp/consentUserAgent speichern
|
|
test('Speicher-Verringerung ohne Zustimmung (consent: null) speichert keine Zustimmungsdaten', async () => {
|
|
const doc = await recordContractEvent({
|
|
customerId: 'c4',
|
|
subscriptionId: 'sub_4',
|
|
type: 'speicher',
|
|
productKey: 'hosting',
|
|
details: { fromUnits: 2, toUnits: 1 },
|
|
consent: null,
|
|
})
|
|
assert.equal(doc.consentIp ?? null, null)
|
|
assert.equal(doc.consentUserAgent ?? null, null)
|
|
assert.equal(doc.termsVersion ?? null, null)
|
|
})
|