Analyse: Abgleich schaltet neu aktivierte Analysen nicht ab (Sperre + Nachlesen), weitere Abgleich-Tests

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 78170567b4
commit 495c3a2c40
2 files changed
+76 -9

No files matched your search

+62
View File
@@ -703,3 +703,65 @@ test('deleteCustomerWithPortalAccess: Analyse aus (kein Ticket, keine Mail), Lö
assert.equal((await doc('kd2', 'kd2p')).state, 'off')
assert.ok(stops.includes(b.siteKey))
})
// ---- Fix round 1 ----
test('Abgleich off-Zweig: wird das Dokument nach dem Lesen wieder aktiv, bleibt der Messdienst an (Sperre + Nachlesen)', async () => {
const { withLocks } = await import('../server/services/keyedLock.js')
aw.seed('websiteProjects', { $id: 'f1p', customerId: 'c1', projectName: 'F1', subdomain: 'f1', isPublic: true })
const d = await seedFeature('c1', 'f1p', 'off', { offSince: '2026-10-04T09:00:00.000Z', removalTicketId: 'x' })
let run
await withLocks(['analyse:f1p'], async () => {
run = an.runAnalyseReconcile({ now: NOW })
await new Promise((r) => setTimeout(r, 150)) // Abgleich hat gelesen und wartet auf die Sperre
await updateDocument(config.collections.portalFeatures, d.$id, { ...sf.stateFields('active') })
})
await run
assert.ok(!stops.includes(d.siteKey), 'frisch aktivierte Analyse nicht abgeschaltet')
assert.equal((await doc('c1', 'f1p')).state, 'active')
})
test('Abgleich: Appwrite-Lesefehler (nicht 404) beendet nichts', async () => {
aw.seed('websiteProjects', { $id: 'f2p', customerId: 'c1', projectName: 'F2', subdomain: 'f2', isPublic: true })
const d = await seedFeature('c1', 'f2p', 'active')
const real = globalThis.fetch
globalThis.fetch = async (url, opts) => {
if (String(url).includes('/collections/websiteProjects/documents/f2p')) return new Response(JSON.stringify({ message: 'kaputt' }), { status: 500, headers: { 'content-type': 'application/json' } })
return real(url, opts)
}
try {
await an.runAnalyseReconcile({ now: NOW })
} finally {
globalThis.fetch = real
}
assert.ok(!stops.includes(d.siteKey))
assert.equal((await doc('c1', 'f2p')).state, 'active')
})
test('Abgleich: nicht öffentliches Projekt / ohne Subdomain => beendet mit Entfernen-Ticket, ohne K19', async () => {
aw.seed('websiteProjects', { $id: 'f3p', customerId: 'c1', projectName: 'F3', subdomain: 'f3', isPublic: false, repoFullName: 'WEBklar/f3' })
aw.seed('websiteProjects', { $id: 'f4p', customerId: 'c1', projectName: 'F4', isPublic: true })
const a = await seedFeature('c1', 'f3p', 'active')
const b = await seedFeature('c1', 'f4p', 'active')
await an.runAnalyseReconcile({ now: NOW })
for (const [p, name, key] of [['f3p', 'F3', a.siteKey], ['f4p', 'F4', b.siteKey]]) {
const d = await doc('c1', p)
assert.equal(d.state, 'off')
assert.ok(stops.includes(key))
assert.equal(removalTopics(name).length, 1)
assert.ok(d.removalTicketId)
}
assert.equal(mails.sent.filter((m) => /beendet/.test(m.subject)).length, 0)
})
test('Abgleich: stopSite wirft => Dokument bleibt unverändert aktiv', async () => {
aw.seed('websiteProjects', { $id: 'f5p', customerId: 'c1', projectName: 'F5', subdomain: 'f5', isPublic: false })
const d = await seedFeature('c1', 'f5p', 'active')
const real = an.analyseDeps.client.stopSite
an.analyseDeps.client.stopSite = async () => { throw new Error('Messdienst weg') }
await an.runAnalyseReconcile({ now: NOW })
an.analyseDeps.client.stopSite = real
const after = await doc('c1', 'f5p')
assert.equal(after.state, 'active')
assert.ok(!after.offSince)
assert.equal(removalTopics('F5').length, 0)
})