Analyse: alle Beendigungswege schalten den Messdienst ab (Kunde/Projekt weg, Domain, Löschfrist, Widerruf Wechsel)
Messdienst: POST /a/admin/sites/:key/off setzt active=0 und offSince einmalig; Portal-Client stopSite. analyse.js: endAnalyse als gemeinsamer Kern, endAnalyseForCustomer, analyseOnProjectChanged, analyseDisabled (Sperre/Löschfrist) und täglicher Abgleich runAnalyseReconcile (Projekt/Kunde weg, Domains nachziehen, K18-Wiederholung, Messdienst für off/disabled, fehlende Entfernen-Tickets nachholen). Entfernen-Ticket je Abschaltung gekennzeichnet (kein Doppelticket, auch wenn das Speichern der ID scheiterte). Hooks: Kunde löschen, updateWebsiteProject, withdrawOrder (Wechsel), siteFeatures.disable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fbc40252a3
commit
78170567b4
17 files changed
+616
-70
No files matched your search
@@ -15,6 +15,7 @@ const aw = await startAppwriteMock({
|
||||
subscriptions: schemaKeys('subscriptions'),
|
||||
contractEvents: schemaKeys('contractEvents'),
|
||||
hourCredits: schemaKeys('hourCredits'),
|
||||
portalFeatures: [...schemaKeys('portalFeatures'), 'siteKey', 'offSince', 'activatedAt', 'accentColor', 'removalTicketId'],
|
||||
},
|
||||
})
|
||||
const st = await startFakeStripe()
|
||||
@@ -28,6 +29,7 @@ const { withdrawalDeadline } = await import('../server/services/deadlines.js')
|
||||
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
|
||||
const { setMailTransportForTests } = await import('../server/services/mailer.js')
|
||||
|
||||
const { analyseDeps } = await import('../server/services/analyse.js')
|
||||
const app = await startTestApp({ '/api/abo': aboRoutes })
|
||||
// Mails landen in der Senke statt bei SMTP (F25): K03/K04 an den Kunden, T08/T09 ans Team sind pruefbar
|
||||
const mails = mailSink()
|
||||
@@ -396,6 +398,61 @@ test('Widerruf Wechsel in der Frist: Ereignis widerrufen, kein automatischer Pre
|
||||
assert.doesNotMatch(t09().text, /Abo in Stripe sofort beenden/)
|
||||
})
|
||||
|
||||
// Analyse hat nur mit Wartungs-Abo Bestand: der Widerruf des Wechsels beendet sie (Messdienst aus, K19)
|
||||
async function withActiveAnalyse(fn) {
|
||||
const stops = []
|
||||
const real = analyseDeps.client
|
||||
analyseDeps.client = { stopSite: async (k) => { stops.push(k) } }
|
||||
aw.seed('portalFeatures', { $id: 'wf_feat', customerId: 'c1', projectId: 'wf_p', featureKey: 'analyse', state: 'active', enabled: true, siteKey: 'wfsitekey0123456789' })
|
||||
try {
|
||||
return await fn(stops)
|
||||
} finally {
|
||||
analyseDeps.client = real
|
||||
}
|
||||
}
|
||||
const wechselIn = async (days) => {
|
||||
const consentAt = daysAgo(days)
|
||||
await recordContractEvent({
|
||||
customerId: 'c1', subscriptionId: 'sub_h', type: 'wechsel', productKey: 'wartung',
|
||||
details: { from: 'hosting', switchedAt: consentAt, effectiveAt: consentAt, paidAt: consentAt },
|
||||
consent: { ...consent, consentAt }, status: 'erledigt',
|
||||
})
|
||||
}
|
||||
|
||||
test('Widerruf Wechsel in der Frist beendet die Analyse des Kunden (stopSite, off, K19); Fehler lässt den Widerruf auf 200', async () => {
|
||||
await resetSubH({ units: 0 })
|
||||
await wechselIn(3)
|
||||
await withActiveAnalyse(async (stops) => {
|
||||
const res = await withdraw({ order: 'wechsel' })
|
||||
assert.equal(res.status, 200)
|
||||
assert.deepEqual(stops, ['wfsitekey0123456789'])
|
||||
const d = aw.docs('portalFeatures').get('wf_feat')
|
||||
assert.equal(d.state, 'off')
|
||||
assert.ok(d.offSince)
|
||||
assert.ok(bySubject('Deine Website-Analyse wurde beendet'))
|
||||
})
|
||||
// wirft der Messdienst, bleibt der Widerruf 200
|
||||
await resetSubH({ units: 0 })
|
||||
await wechselIn(3)
|
||||
await withActiveAnalyse(async () => {
|
||||
analyseDeps.client = { stopSite: async () => { throw new Error('Messdienst weg') } }
|
||||
const res = await withdraw({ order: 'wechsel' })
|
||||
assert.equal(res.status, 200)
|
||||
assert.equal((await events('wechsel'))[0].status, 'widerrufen')
|
||||
})
|
||||
})
|
||||
|
||||
test('Widerruf Wechsel nach Fristende oder Zusatzspeicher ändert die Analyse nicht', async () => {
|
||||
await resetSubH({ units: 0 })
|
||||
await wechselIn(30)
|
||||
await withActiveAnalyse(async (stops) => {
|
||||
assert.equal((await withdraw({ order: 'wechsel' })).status, 200)
|
||||
assert.equal((await withdraw({ order: 'zusatzspeicher' })).status, 200)
|
||||
assert.deepEqual(stops, [])
|
||||
assert.equal(aw.docs('portalFeatures').get('wf_feat').state, 'active')
|
||||
})
|
||||
})
|
||||
|
||||
test('Widerruf nach Fristende: 200, nichts geändert', async () => {
|
||||
await resetSubH({ units: 0 })
|
||||
const consentAt = daysAgo(30)
|
||||
|
||||
Reference in new issue
Block a user