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

89 lines
5.9 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'
// Einrichtungs-Ticket mit Abrechnung (V7) und unlockedBy in featureView fuer alle Funktionen.
// workorders, websiteProjects 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 { createFeatureTicket, ABRECHNUNGS_ZEILEN } = await import('../server/services/ticketAdmin.js')
const { featureView, ticketNachlegen } = await import('../server/services/siteFeatures.js')
aw.seed('customers', { $id: 'c1', name: 'Kunde Eins', location: 'Berlin', email: 'kunde1@example.test' })
const ALT = ['createdAt', 'customerId', 'customerLocation', 'customerName', 'details', 'priority', 'serviceType', 'status', 'systemType', 'topic', 'type', 'woid']
const base = { customerId: 'c1', project: { $id: 'p1', projectName: 'Bäckerei' }, featureKey: 'termine', siteKey: 'abcdefghijklmnop', topic: 'T', details: 'Zeile 1' }
const nutzer = (doc) => Object.keys(doc).filter((k) => !k.startsWith('$')).sort()
// aw.docs() liefert eine Kopie des Standes zum Aufruf: das Ticket erst anlegen, dann nachsehen
const ticketVon = async (args) => {
const { ticketId } = await createFeatureTicket(args)
return aw.docs('workorders').get(ticketId)
}
test('abrechnung abo und projekt: Felder und letzte Zeile', async () => {
assert.deepEqual(ABRECHNUNGS_ZEILEN, {
abo: 'Zeit im Arbeitsblatt erfassen und aufs Abo-Stundenkonto anrechnen (Einrichtung aus dem Abo, Vorschuss möglich).',
projekt: 'Zeit im Arbeitsblatt erfassen, nicht aufs Abo anrechnen (im Projekt enthalten).',
})
const t = await ticketVon({ ...base, abrechnung: 'abo' })
assert.deepEqual([t.einrichtungFuer, t.einrichtungAbrechnung], ['termine', 'abo'])
assert.equal(t.details, `Zeile 1\n${ABRECHNUNGS_ZEILEN.abo}`)
assert.ok((await ticketVon({ ...base, abrechnung: 'projekt' })).details.endsWith('\nZeit im Arbeitsblatt erfassen, nicht aufs Abo anrechnen (im Projekt enthalten).'))
})
test('ohne abrechnung: keine neuen Felder, Details unverändert (alte Ansicht)', async () => {
for (const abrechnung of [undefined, '']) {
const t = await ticketVon({ ...base, abrechnung })
assert.deepEqual(nutzer(t), ALT)
assert.equal(t.details, 'Zeile 1')
}
})
test('ungültiger Wert: TypeError, kein Ticket', async () => {
const n = aw.docs('workorders').size
for (const abrechnung of ['frei', 'Abo', null, 5]) await assert.rejects(() => createFeatureTicket({ ...base, abrechnung }), TypeError, String(abrechnung))
assert.equal(aw.docs('workorders').size, n)
})
test('lange Details: gekürzt auf 7000, die Schlusszeile bleibt; auch im Notfallweg nach 400', async () => {
const lang = Array.from({ length: 400 }, (_, i) => `Zeile ${i} ${'x'.repeat(30)}`).join('\n')
const t = await ticketVon({ ...base, details: lang, abrechnung: 'abo' })
assert.ok(t.details.length <= 7000 && t.details.endsWith(ABRECHNUNGS_ZEILEN.abo))
assert.ok(t.details.length > 6800, 'es wird nicht zu stark gekürzt')
aw.failNext({ collection: 'workorders', method: 'POST', status: 400, message: 'zu lang' })
const n = await ticketVon({ ...base, details: lang, fallbackDetails: 'Notfall', abrechnung: 'projekt' })
assert.ok(n.details.startsWith('Notfall') && n.details.endsWith(ABRECHNUNGS_ZEILEN.projekt))
})
test('featureView: unlockedBy für alle Funktionen, aboEndeAt/einbau nur bei Zeiten', async () => {
assert.equal((await featureView({ state: 'setup', unlockedBy: 'abo' }, 'news')).unlockedBy, 'abo')
assert.equal((await featureView({ state: 'setup', unlockedBy: 'projekt' }, 'shop')).unlockedBy, 'projekt')
assert.equal((await featureView({ state: 'active', unlockedByPurchase: true }, 'termine')).unlockedBy, 'kauf')
assert.equal((await featureView(undefined, 'news')).unlockedBy, null)
assert.equal((await featureView({ state: 'off', unlockedBy: 'abo' }, 'news')).unlockedBy, null)
assert.equal(Object.hasOwn(await featureView({ state: 'setup', unlockedBy: 'abo' }, 'news'), 'einbau'), false)
assert.equal(Object.hasOwn(await featureView({ state: 'setup', unlockedBy: 'abo' }, 'news'), 'aboEndeAt'), false)
const zeiten = await featureView({ state: 'setup', unlockedBy: 'abo', einbau: 'angestossen' }, 'zeiten')
assert.deepEqual([zeiten.unlockedBy, zeiten.einbau, Object.hasOwn(zeiten, 'aboEndeAt')], ['abo', 'angestossen', true])
})
test('ersetztes Ticket trägt die Abrechnung des Dokuments', async () => {
aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Bäckerei' })
aw.seed('portalFeatures', { $id: 'f_p1_news', customerId: 'c1', projectId: 'p1', featureKey: 'news', siteKey: 'abcdefghijklmnop', state: 'setup', enabled: false, unlockedBy: 'abo', einrichtungAbrechnung: 'abo', ticketId: 'weg' })
const { doc } = await ticketNachlegen({ customerId: 'c1', projectId: 'p1', key: 'news', by: 'Kenso' })
const ticket = aw.docs('workorders').get(doc.ticketId)
assert.deepEqual([ticket.einrichtungAbrechnung, ticket.einrichtungFuer], ['abo', 'news'])
assert.ok(ticket.details.endsWith(ABRECHNUNGS_ZEILEN.abo))
})
test('ersetztes Ticket ohne Abrechnung am Dokument: wie bisher, keine neuen Felder', async () => {
aw.seed('portalFeatures', { $id: 'f_p1_termine', customerId: 'c1', projectId: 'p1', featureKey: 'termine', siteKey: 'bbcdefghijklmnop', state: 'setup', enabled: false, unlockedByPurchase: true, ticketId: 'weg2' })
const { doc } = await ticketNachlegen({ customerId: 'c1', projectId: 'p1', key: 'termine', by: 'Kenso' })
assert.deepEqual(nutzer(aw.docs('workorders').get(doc.ticketId)), ALT)
})