Kundenansicht P6: Abo-Ende-Abgleich im Tagesjob
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7a03f073bc
commit
db47f13510
3 files changed
+94
No files matched your search
@@ -0,0 +1,56 @@
|
||||
import { test, after, beforeEach } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { startAppwriteMock } from './support/appwriteMock.js'
|
||||
import { startFakeStripe } from './support/fakeStripe.js'
|
||||
import { mailSink } from './support/mailSink.js'
|
||||
import { schemaKeys } from './support/schema.js'
|
||||
import { useTestEnv } from './support/testEnv.js'
|
||||
|
||||
// Tagesjob des Abo-Ende-Abgleichs (P6 Task 11): Überlappungsschutz, wirft nie, Einbindung in server/index.js vor den Löschjobs.
|
||||
const aw = await startAppwriteMock({
|
||||
schema: {
|
||||
portalFeatures: schemaKeys('portalFeatures'),
|
||||
subscriptions: schemaKeys('subscriptions'),
|
||||
contractEvents: schemaKeys('contractEvents'),
|
||||
termineConfig: schemaKeys('termineConfig'),
|
||||
newsPosts: schemaKeys('newsPosts'),
|
||||
},
|
||||
})
|
||||
const st = await startFakeStripe()
|
||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
||||
const { seedFixtures, subscriptionDoc, wartungDoc } = await import('./support/fixtures.js')
|
||||
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
|
||||
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
||||
const { aboFunktionenTagesJob } = await import('../server/services/aboFunktionenAbgleich.js')
|
||||
const mails = mailSink()
|
||||
setMailTransportForTests(mails)
|
||||
after(() => {
|
||||
setMailTransportForTests(null)
|
||||
return Promise.all([aw.close(), st.close()])
|
||||
})
|
||||
|
||||
const JETZT = new Date('2026-10-17T08:00:00.000Z')
|
||||
const seed = (key, over = {}) => aw.seed('portalFeatures', { $id: `f_p1_${key}`, customerId: 'c1', projectId: 'p1', featureKey: key, siteKey: 'abcdefghijklmnop', state: 'active', enabled: true, activatedAt: '2026-10-01T09:00:00.000Z', unlockedBy: 'abo', unlockedByPurchase: false, einrichtungAbrechnung: 'abo', ...over })
|
||||
const abo = (doc) => aw.seed('subscriptions', subscriptionDoc('sub_h', doc))
|
||||
|
||||
seedFixtures(aw)
|
||||
aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Bäckerei' })
|
||||
beforeEach(async () => {
|
||||
for (const col of ['portalFeatures', 'subscriptions', 'contractEvents', 'newsPosts']) for (const id of aw.docs(col).keys()) await deleteDocument(col, id)
|
||||
mails.sent.length = 0
|
||||
})
|
||||
|
||||
test('Überlappungsschutz: der zweite gleichzeitige Lauf wird übersprungen, danach läuft der Job wieder', async () => {
|
||||
abo({ ...wartungDoc, status: 'canceled' }); seed('news')
|
||||
const [a, b] = await Promise.all([aboFunktionenTagesJob(JETZT), aboFunktionenTagesJob(JETZT)])
|
||||
assert.deepEqual([a, b].filter((r) => r.uebersprungen).length, 1); assert.equal([a, b].find((r) => !r.uebersprungen).abgeschaltet, 1)
|
||||
assert.equal((await aboFunktionenTagesJob(JETZT)).uebersprungen, undefined)
|
||||
})
|
||||
test('Der Job wirft nie (Listenfehler → fehler 1) und index.js startet ihn vor den Löschjobs', async (t) => {
|
||||
t.mock.method(console, 'error', () => {}); aw.failNext({ collection: 'portalFeatures', method: 'GET', status: 500 })
|
||||
assert.equal((await aboFunktionenTagesJob(JETZT)).fehler, 1)
|
||||
const src = readFileSync(new URL('../server/index.js', import.meta.url), 'utf8')
|
||||
const start = src.indexOf('setTimeout(aboFunktionenAbgleich, 150 * 1000)'); assert.ok(start > src.indexOf('const analyseAboCheck'))
|
||||
assert.ok(start < src.indexOf('setTimeout(termineTagesJob, 180 * 1000)')); assert.match(src, /setInterval\(aboFunktionenAbgleich, 24 \* 60 \* 60 \* 1000\)/)
|
||||
})
|
||||
Reference in new issue
Block a user