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

113 lines
5.1 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'
// websiteProjects, workorders und customers legt das Setup-Skript nicht an: ohne Schema im Nachbau
const aw = await startAppwriteMock({ schema: { portalFeatures: schemaKeys('portalFeatures') } })
const st = await startFakeStripe()
after(() => Promise.all([aw.close(), st.close()]))
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
const sf = await import('../server/services/siteFeatures.js')
aw.seed('customers', { $id: 'c1', name: 'Kunde Eins', location: 'Berlin' })
aw.seed('customers', { $id: 'c2', name: 'Kunde Zwei' })
aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Bäckerei', liveDomain: 'baeckerei.de' })
aw.seed('websiteProjects', { $id: 'p2', customerId: 'c1', projectName: 'Café', subdomain: 'cafe' })
aw.seed('websiteProjects', { $id: 'p3', customerId: 'c2', projectName: 'Fremd' })
aw.seed('websiteProjects', { $id: 'p_' + 'x'.repeat(40), customerId: 'c1', projectName: 'Lang' })
const consent = { text: 'Ich stimme zu', at: '2026-09-30T10:00:00.000Z', ip: '1.2.3.4', userAgent: 'UA' }
const tickets = () => [...aw.docs('workorders').values()].filter((t) => String(t.topic).startsWith('News-Pinnwand einbauen'))
test('ohne_dokument_ist_off', async () => {
const list = await sf.listCustomerFeatures('c1')
assert.equal(list.length, 3)
for (const p of list) assert.equal(p.features.news.state, 'off')
assert.equal(list.find((p) => p.projectId === 'p1').liveUrl, 'https://baeckerei.de')
})
test('bezahlt_legt_ticket_an', async () => {
const ordered = await sf.markOrdered({ customerId: 'c1', projectId: 'p1', key: 'news', sessionId: 'cs_1', consent })
assert.equal(ordered.state, 'ordered')
assert.equal(ordered.enabled, false)
assert.equal(ordered.consentIp, '1.2.3.4')
assert.match(ordered.siteKey, /^[a-z2-7]{16}$/)
const { doc, created } = await sf.markPaid({ sessionId: 'cs_1', invoiceId: 'in_1', invoiceUrl: 'https://x/inv' })
assert.equal(created, true)
assert.equal(doc.state, 'setup')
assert.equal(doc.enabled, false)
assert.equal(doc.unlockedByPurchase, true)
assert.ok(doc.ticketId)
assert.equal(tickets().length, 1)
assert.match(tickets()[0].topic, /^News-Pinnwand einbauen – Bäckerei/)
assert.ok(tickets()[0].details.includes(`data-webklar-news="${doc.siteKey}"`))
const view = (await sf.listCustomerFeatures('c1')).find((p) => p.projectId === 'p1').features.news
assert.equal(view.state, 'setup')
assert.ok(view.ticketWoid)
})
test('webhook_idempotent', async () => {
const again = await sf.markPaid({ sessionId: 'cs_1', invoiceId: 'in_1', invoiceUrl: 'https://x/inv' })
assert.equal(again.created, false)
assert.equal(tickets().length, 1)
})
test('ticket_geschlossen_aktiviert', async () => {
const doc = await sf.getFeatureDoc('c1', 'p1', 'news')
assert.deepEqual(await sf.activateClosedTickets({ customerId: 'c1' }), [])
assert.equal((await sf.getFeatureDoc('c1', 'p1', 'news')).state, 'setup')
aw.seed('workorders', { ...aw.docs('workorders').get(doc.ticketId), status: 'Closed' })
assert.deepEqual(await sf.activateClosedTickets({ customerId: 'c2' }), [])
const now = new Date('2026-10-01T08:00:00Z')
const done = await sf.activateClosedTickets({ now })
assert.equal(done.length, 1)
assert.equal(done[0].state, 'active')
assert.equal(done[0].enabled, true)
assert.equal(done[0].activatedAt, now.toISOString())
assert.equal((await sf.findActiveBySiteKey(doc.siteKey)).$id, doc.$id)
})
test('fehlgeschlagene_zahlung_setzt_off', async () => {
await sf.markOrdered({ customerId: 'c1', projectId: 'p2', key: 'news', sessionId: 'cs_2', consent })
const doc = await sf.markCheckoutFailed('cs_2')
assert.equal(doc.state, 'off')
assert.equal(doc.enabled, false)
assert.equal(await sf.markCheckoutFailed('cs_unbekannt'), null)
})
test('fremdes_projekt_403', async () => {
await assert.rejects(() => sf.assertOwnProject('c1', 'p3'), { status: 403 })
await assert.rejects(() => sf.assertOwnProject('c1', 'gibts_nicht'), { status: 403 })
assert.equal((await sf.assertOwnProject('c1', 'p1')).$id, 'p1')
})
test('sitekey_format', () => {
const keys = new Set()
for (let i = 0; i < 100; i += 1) {
const k = sf.newSiteKey()
assert.match(k, /^[a-z2-7]{16}$/)
keys.add(k)
}
assert.equal(keys.size, 100)
})
test('admin_aktiviert_ohne_zahlung', async () => {
const before = tickets().length
const doc = await sf.adminActivate('c1', 'p2', 'news', { by: 'kenso' })
assert.equal(doc.state, 'active')
assert.equal(doc.enabled, true)
assert.equal(doc.unlockedByPurchase, false)
assert.equal(tickets().length, before)
const off = await sf.disable(doc.$id, { by: 'kenso' })
assert.equal(off.state, 'disabled')
assert.equal(off.enabled, false)
})
test('lange_projekt_id_bekommt_gueltige_dokument_id', async () => {
const doc = await sf.adminActivate('c1', 'p_' + 'x'.repeat(40), 'news', {})
assert.ok(doc.$id.length <= 36)
})