Funktionen: Zustände, Ticket beim Kauf, Freischaltung bei geschlossenem Ticket
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
16fbb03c13
commit
4b1df8fbfa
4 files changed
+416
No files matched your search
@@ -0,0 +1,112 @@
|
||||
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)
|
||||
})
|
||||
Reference in new issue
Block a user