News: Aufbewahrung, Datenschutz-Abschnitt und Leistungsbeschreibung (Entwurf)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-09-30 17:23:41 +00:00
1 parent 0e0776b406
commit 0dc57fe5e8
6 files changed
+220 -4

No files matched your search

+42
View File
@@ -15,11 +15,14 @@ const aw = await startAppwriteMock({
subscriptions: schemaKeys('subscriptions'),
contractEvents: schemaKeys('contractEvents'),
takeoverCredits: schemaKeys('takeoverCredits'),
newsPosts: schemaKeys('newsPosts'),
portalFeatures: schemaKeys('portalFeatures'),
},
})
const st = await startFakeStripe()
const uploadDir = mkdtempSync(path.join(os.tmpdir(), 'loeschfristen-'))
process.env.TAKEOVER_UPLOAD_DIR = uploadDir
process.env.NEWS_UPLOAD_DIR = path.join(uploadDir, 'news')
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
const { findDueDeletions, applyDeletions, formatReport, CONSENT_METADATA_KEYS } = await import('../server/services/retention.js')
const { run } = await import('../scripts/loeschfristen.mjs')
@@ -264,3 +267,42 @@ test('genehmigter Antrag ohne decidedAt: übersprungen und gemeldet, kein Rückg
assert.ok(plan.skipped.some((t) => t.id === 'nd_tk'))
assert.match(formatReport(plan), /Übersprungen[\s\S]*nd_tk/)
})
test('retention_loescht_news: Beiträge, Fotos und Funktionen fälliger Kunden', async () => {
const { storeNewsImage, newsImagePath } = await import('../server/services/newsImages.js')
const sharp = (await import('sharp')).default
const png = () => sharp({ create: { width: 40, height: 30, channels: 3, background: '#c33' } }).png().toBuffer()
sub('rn_sub', 'rn', { status: 'canceled', endedAt: '2022-03-15T10:00:00.000Z' })
sub('rn2_sub', 'rn2', { status: 'active' }) // laufendes Abo: nichts davon wird angefasst
stripeSub('rn_sub', 'rn')
const { imageId } = await storeNewsImage({ customerId: 'rn', projectId: 'rnp', buffer: await png() })
const other = await storeNewsImage({ customerId: 'rn2', projectId: 'rntwop', buffer: await png() })
aw.seed('newsPosts', { $id: 'rn_post1', customerId: 'rn', projectId: 'rnp', type: 'photo', imageId, title: '', text: '' })
aw.seed('newsPosts', { $id: 'rn_post2', customerId: 'rn', projectId: 'rnp', type: 'note', imageId: null, title: 'Hi', text: 'Hallo' })
aw.seed('newsPosts', { $id: 'rn2_post', customerId: 'rn2', projectId: 'rntwop', type: 'photo', imageId: other.imageId, title: '', text: '' })
aw.seed('portalFeatures', { $id: 'rn_feat', customerId: 'rn', projectId: 'rnp', featureKey: 'news', state: 'active', enabled: true })
aw.seed('portalFeatures', { $id: 'rn2_feat', customerId: 'rn2', projectId: 'rntwop', featureKey: 'news', state: 'active', enabled: true })
const plan = await findDueDeletions(JAN_2026)
const entry = plan.news.find((n) => n.customerId === 'rn')
assert.deepEqual(entry.posts.map((p) => p.id).sort(), ['rn_post1', 'rn_post2'])
assert.deepEqual(entry.posts.find((p) => p.id === 'rn_post1'), { id: 'rn_post1', projectId: 'rnp', imageId })
assert.deepEqual(entry.featureIds, ['rn_feat'])
assert.ok(!plan.news.some((n) => n.customerId === 'rn2'))
assert.match(formatReport(plan), /Kunde rn: 2 News-Beitrag\/-Beiträge mit 1 Foto\(s\) zu löschen, 1 Funktion\(en\) abzuschalten/)
const result = await applyDeletions(plan)
assert.deepEqual(result.failed, [])
assert.equal(result.deleted.newsPosts, 2)
assert.equal(result.deleted.newsImages, 1)
assert.equal(result.deleted.features, 1)
assert.match(formatReport(plan, result), /News: 2 Beitrag\/Beiträge und 1 Foto\(s\) gelöscht, 1 Funktion\(en\) abgeschaltet/)
assert.equal([...aw.docs('newsPosts').values()].filter((d) => d.customerId === 'rn').length, 0)
assert.equal([...aw.docs('newsPosts').values()].filter((d) => d.customerId === 'rn2').length, 1)
assert.equal(existsSync(newsImagePath('rnp', imageId, 'l')), false)
assert.equal(existsSync(newsImagePath('rntwop', other.imageId, 'l')), true)
const feat = [...aw.docs('portalFeatures').values()].find((d) => d.$id === 'rn_feat')
assert.equal(feat.state, 'disabled')
assert.equal(feat.enabled, false)
assert.equal([...aw.docs('portalFeatures').values()].find((d) => d.$id === 'rn2_feat').state, 'active')
})