Retention: Zustimmungsnachweise der Funktionsbestellung drei Jahre nach Abo-Ende loeschen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-05 04:38:00 +00:00
1 parent a629b2519b
commit 5b5b4b90eb
4 files changed
+106 -15

No files matched your search

+12 -10
View File
@@ -25,7 +25,7 @@ const { ANALYSE_ATTRIBUTES } = await import('../scripts/analyse-setup.mjs')
const CONSENT_KEYS = [
'customerId', 'projectId', 'featureKey', 'stripeSessionId', 'acceptedAt', 'fassungen', 'texte', 'textHash', 'ip', 'userAgent', 'konto',
]
const CONSENT_INDEX_KEYS = ['idx_session', 'idx_project_feature']
const CONSENT_INDEX_KEYS = ['idx_session', 'idx_project_feature', 'idx_customer']
// Ohne Angaben ist featureConsents komplett vorhanden (Attribute und Indizes): die Faelle fuer portalFeatures bleiben so
// unberuehrt; die Faelle fuer featureConsents setzen consents/consentAttrs/indexes selbst.
@@ -177,10 +177,11 @@ test('Schema featureConsents: 11 Schluessel mit Typ und Groesse, alle optional',
assert.ok(nach.textHash.size >= 64 && nach.customerId.size >= 36 && nach.projectId.size >= 36)
})
test('Schema featureConsents: beide Indizes mit den Attributen aus der Vorgabe', () => {
test('Schema featureConsents: drei Indizes mit den Attributen aus der Vorgabe (idx_customer für die Löschfristen)', () => {
assert.deepEqual(FEATURE_CONSENT_INDEXES, [
{ key: 'idx_session', type: 'key', attributes: ['stripeSessionId'] },
{ key: 'idx_project_feature', type: 'key', attributes: ['projectId', 'featureKey', 'acceptedAt'] },
{ key: 'idx_customer', type: 'key', attributes: ['customerId'] },
])
const keys = new Set(CONSENT_KEYS)
for (const index of FEATURE_CONSENT_INDEXES) for (const attr of index.attributes) assert.ok(keys.has(attr), `${index.key}: ${attr}`)
@@ -199,7 +200,7 @@ test('schemaKeys(featureConsents) liefert dieselben Schluessel, der Nachbau nimm
await assert.rejects(() => createDocument(config.collections.featureConsents, { gibtEsNicht: 1 }, 'fc_schema2'), /Unknown attribute/)
})
test('main legt bei fehlender Collection genau featureConsents mit 11 Attributen und 2 Indizes an', async () => {
test('main legt bei fehlender Collection genau featureConsents mit 11 Attributen und 3 Indizes an', async () => {
const client = mockClient({ attrs: KEYS, consents: false, consentAttrs: [], indexes: [] })
const log = []
const result = await main({ client, log: (zeile) => log.push(zeile) })
@@ -210,17 +211,17 @@ test('main legt bei fehlender Collection genau featureConsents mit 11 Attributen
assert.equal(client.calls.attrs.find((a) => a.key === 'texte').size, 20000)
assert.ok(client.calls.attrs.every((a) => a.required === false))
assert.deepEqual(client.calls.waits, CONSENT_KEYS.map((key) => `featureConsents.${key}`))
assert.deepEqual(client.calls.indexes, ['featureConsents.idx_session', 'featureConsents.idx_project_feature'])
assert.deepEqual(client.calls.indexes, ['featureConsents.idx_session', 'featureConsents.idx_project_feature', 'featureConsents.idx_customer'])
// Reihenfolge: Collection, alle Attribute, Warten auf alle Attribute, erst dann die Indizes
const order = client.calls.order
assert.equal(order[0], 'collection featureConsents')
assert.equal(order.findIndex((e) => e.startsWith('index ')), order.length - 2, 'die Indizes kommen zuletzt')
assert.equal(order.findIndex((e) => e.startsWith('index ')), order.length - 3, 'die Indizes kommen zuletzt')
assert.ok(order.lastIndexOf('wait featureConsents.konto') < order.indexOf('index featureConsents.idx_session'))
assert.ok(order.indexOf('attribute featureConsents.konto') < order.indexOf('wait featureConsents.customerId'))
assert.equal(result.missingCollection, true)
assert.equal(result.missing.length, 11)
assert.ok(result.missing.every((a) => a.collection === 'featureConsents'))
assert.deepEqual(result.missingIndexes.map((i) => i.key), ['idx_session', 'idx_project_feature'])
assert.deepEqual(result.missingIndexes.map((i) => i.key), ['idx_session', 'idx_project_feature', 'idx_customer'])
assert.ok(log.includes('+ Collection featureConsents'))
assert.ok(log.includes('+ Index featureConsents.idx_project_feature'))
})
@@ -238,7 +239,7 @@ test('main ergaenzt an vorhandener Collection nur fehlende Attribute und Indizes
const client = mockClient({
attrs: KEYS,
consentAttrs: CONSENT_KEYS.filter((key) => key !== 'konto' && key !== 'textHash'),
indexes: ['idx_session'],
indexes: ['idx_session', 'idx_customer'],
})
const result = await main({ client, log: () => {} })
assert.deepEqual(client.calls.collections, [], 'die Collection wird nicht noch einmal angelegt')
@@ -272,17 +273,18 @@ test('main --check schreibt nie, meldet featureConsents FEHLT und liefert die Lu
for (const key of CONSENT_KEYS) assert.ok(log.includes(`- featureConsents.${key} FEHLT`), key)
assert.ok(log.includes('- featureConsents.idx_session FEHLT'))
assert.ok(log.includes('- featureConsents.idx_project_feature FEHLT'))
assert.ok(log.includes('- featureConsents.idx_customer FEHLT'))
assert.ok(!log.some((zeile) => zeile.includes('portalFeatures') && zeile.includes('FEHLT')))
assert.ok(!log.some((zeile) => zeile.startsWith('+')))
assert.equal(result.missingCollection, true)
assert.deepEqual(result.missing.map((a) => a.key), CONSENT_KEYS)
assert.equal(result.missingIndexes.length, 2)
assert.equal(result.missingIndexes.length, 3)
assert.equal(hatLuecken(result), true)
assert.equal(log.at(-1), '14 Eintrag/Eintraege fehlen.')
assert.equal(log.at(-1), '15 Eintrag/Eintraege fehlen.')
})
test('main --check meldet je fehlendes Attribut und je fehlenden Index einer vorhandenen Collection', async () => {
const client = mockClient({ attrs: KEYS, consentAttrs: CONSENT_KEYS.filter((key) => key !== 'texte'), indexes: ['idx_project_feature'] })
const client = mockClient({ attrs: KEYS, consentAttrs: CONSENT_KEYS.filter((key) => key !== 'texte'), indexes: ['idx_project_feature', 'idx_customer'] })
const log = []
const result = await main({ check: true, client, log: (zeile) => log.push(zeile) })
assert.deepEqual(client.calls, KEINE)