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:
ClaudeandClaude Opus 5.5 committed 2026-10-01 18:50:36 +00:00
1 parent fbc40252a3
commit 78170567b4
17 files changed
+616 -70

No files matched your search

+11 -7
View File
@@ -33,11 +33,13 @@ after(() => {
})
const puts = []
const stops = []
const statsCalls = []
let statsFail = false
const realClient = createAnalyticsClient({ heatSecret: 'h'.repeat(32), adminToken: 't' })
an.analyseDeps.client = {
putSite: async (siteKey, body) => puts.push({ siteKey, ...body }),
stopSite: async (siteKey) => stops.push(siteKey),
getStats: async (siteKey, range) => {
statsCalls.push({ siteKey, range })
if (statsFail) throw Object.assign(new Error('x'), { status: 502 })
@@ -71,6 +73,7 @@ const featureDoc = (c, p) => sf.getFeatureDoc(c, p, 'analyse')
beforeEach(() => {
puts.length = 0
stops.length = 0
statsCalls.length = 0
statsFail = false
config.analyse.enabled = true
@@ -132,7 +135,7 @@ test('Aktivieren und Deaktivieren (Kunde)', async () => {
assert.equal(view.accentColor, '#112233')
const off = await post(`${A}/deactivate`, { projectId: 'p1' }, { cookie })
assert.deepEqual(off.body, { ok: true, state: 'off' })
assert.equal(puts[1].active, false)
assert.equal(stops.length, 1)
const notElig = await post(`${A}/activate`, { projectId: 'p2', consent: consent() }, { cookie })
assert.equal(notElig.status, 409)
})
@@ -270,18 +273,19 @@ test('Fix a: Kunde ändert gesperrtes Dokument nicht, Mitarbeiter schon', async
assert.equal(staff.doc.state, 'off')
})
test('Fix b: Abschalten durch Mitarbeiter meldet Sammeldienst active:false', async () => {
test('Fix b: Abschalten durch Mitarbeiter meldet Sammeldienst per stopSite', async () => {
await adminPost('p6', { on: true, ...tf() })
const cur = await featureDoc('c1', 'p6')
puts.length = 0
stops.length = 0
await sf.disable(cur.$id, { by: 'Test' })
assert.equal(puts.length, 1)
assert.deepEqual(puts[0], { siteKey: cur.siteKey, projectId: 'p6', domains: ['neu.project.webklar.com'], active: false })
assert.deepEqual(stops, [cur.siteKey])
assert.equal(puts.length, 0)
// Ausfall des Sammeldienstes verhindert das Abschalten nicht
const real = an.analyseDeps.client.putSite
an.analyseDeps.client.putSite = async () => { throw new Error('weg') }
const real = an.analyseDeps.client.stopSite
an.analyseDeps.client.stopSite = async () => { throw new Error('weg') }
const again = await sf.disable(cur.$id, { by: 'Test' })
an.analyseDeps.client.putSite = real
an.analyseDeps.client.stopSite = real
assert.equal(again.state, 'disabled')
})