Rechtliches: Karten für Leistungsbeschreibung, AVV und Widerrufsbelehrung des Terminplaners
Das Setup-Skript legt drei weitere Karten an (sort 7 bis 9, liveUrl = Alias-Seiten ohne Fassungsdatum). Es legt nur Karten an (idempotent); Ausfuehrung mit API-Key und Upload der Versionen macht der Inhaber. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
45e5dc30dc
commit
225e2f1b74
2 files changed
+66
-1
No files matched your search
@@ -95,6 +95,10 @@ export const DOCUMENTS = [
|
|||||||
{ key: "datenschutz", title: "Datenschutzerklärung (Website und Portal)", liveUrl: "https://project.webklar.com/datenschutz.html" },
|
{ key: "datenschutz", title: "Datenschutzerklärung (Website und Portal)", liveUrl: "https://project.webklar.com/datenschutz.html" },
|
||||||
{ key: "impressum", title: "Impressum", liveUrl: "https://webklar.com/impressum" },
|
{ key: "impressum", title: "Impressum", liveUrl: "https://webklar.com/impressum" },
|
||||||
{ key: "widerruf", title: "Widerrufsbelehrung und -formular", liveUrl: "https://project.webklar.com/widerrufen.html" },
|
{ key: "widerruf", title: "Widerrufsbelehrung und -formular", liveUrl: "https://project.webklar.com/widerrufen.html" },
|
||||||
|
// Terminplaner (TP2): live ist die Alias-Seite ohne Fassungsdatum (Portal, Aufgabe F1); die Versionen (PDF/HTML der Fassung) laedt der Inhaber hoch
|
||||||
|
{ key: "leistungsbeschreibung-terminplaner", title: "Leistungsbeschreibung Terminplaner", liveUrl: "https://project.webklar.com/rechtliches/leistungsbeschreibung-terminplaner.html" },
|
||||||
|
{ key: "avv-terminplaner", title: "AVV Terminplaner (Auftragsverarbeitung)", liveUrl: "https://project.webklar.com/rechtliches/avv-terminplaner.html" },
|
||||||
|
{ key: "widerrufsbelehrung-funktionen", title: "Widerrufsbelehrung und Muster-Widerrufsformular (Funktionen)", liveUrl: "https://project.webklar.com/rechtliches/widerrufsbelehrung-funktionen.html" },
|
||||||
].map((d, i) => ({ ...d, sort: i + 1 }));
|
].map((d, i) => ({ ...d, sort: i + 1 }));
|
||||||
|
|
||||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { test } from 'node:test'
|
import { test } from 'node:test'
|
||||||
import assert from 'node:assert/strict'
|
import assert from 'node:assert/strict'
|
||||||
import { COLLECTIONS, waitForAttributes } from '../scripts/setup-legal.mjs'
|
import { spawnSync } from 'node:child_process'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { COLLECTIONS, DOCUMENTS, waitForAttributes } from '../scripts/setup-legal.mjs'
|
||||||
|
|
||||||
|
const SCRIPT = fileURLToPath(new URL('../scripts/setup-legal.mjs', import.meta.url))
|
||||||
|
|
||||||
const indexOn = (id, attr) =>
|
const indexOn = (id, attr) =>
|
||||||
COLLECTIONS.find((c) => c.id === id).indexes?.some((i) => i.attributes.includes(attr))
|
COLLECTIONS.find((c) => c.id === id).indexes?.some((i) => i.attributes.includes(attr))
|
||||||
@@ -28,3 +32,60 @@ test('waitForAttributes bricht bei fehlgeschlagenem Attribut ab', async () => {
|
|||||||
const db = { listAttributes: async () => ({ attributes: [{ key: 'b', status: 'failed', error: 'kaputt' }] }) }
|
const db = { listAttributes: async () => ({ attributes: [{ key: 'b', status: 'failed', error: 'kaputt' }] }) }
|
||||||
await assert.rejects(waitForAttributes(db, 'x', { intervalMs: 1, timeoutMs: 1000 }), /b/)
|
await assert.rejects(waitForAttributes(db, 'x', { intervalMs: 1, timeoutMs: 1000 }), /b/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('documents_keys_eindeutig_und_sort_lueckenlos', () => {
|
||||||
|
const keys = DOCUMENTS.map((d) => d.key)
|
||||||
|
for (const k of keys) assert.match(k, /^[a-z0-9-]{1,64}$/, k)
|
||||||
|
assert.equal(new Set(keys).size, keys.length, 'Schluessel eindeutig')
|
||||||
|
assert.deepEqual(DOCUMENTS.map((d) => d.sort), DOCUMENTS.map((_, i) => i + 1), 'sort 1..n in Reihenfolge, ohne Luecke')
|
||||||
|
for (const d of DOCUMENTS) {
|
||||||
|
assert.ok(d.title.length > 0 && d.title.length <= 200, `${d.key}: Titel (Spalte title: 200 Zeichen)`)
|
||||||
|
assert.ok(d.liveUrl.length <= 500, `${d.key}: liveUrl (Spalte liveUrl: 500 Zeichen)`)
|
||||||
|
assert.match(d.liveUrl, /^https:\/\//, `${d.key}: liveUrl https`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('terminplaner_karten_vorhanden', () => {
|
||||||
|
const base = 'https://project.webklar.com/rechtliches/'
|
||||||
|
const erwartet = [
|
||||||
|
{ key: 'leistungsbeschreibung-terminplaner', title: 'Leistungsbeschreibung Terminplaner', sort: 7 },
|
||||||
|
{ key: 'avv-terminplaner', title: 'AVV Terminplaner (Auftragsverarbeitung)', sort: 8 },
|
||||||
|
{ key: 'widerrufsbelehrung-funktionen', title: 'Widerrufsbelehrung und Muster-Widerrufsformular (Funktionen)', sort: 9 },
|
||||||
|
]
|
||||||
|
for (const e of erwartet) {
|
||||||
|
const d = DOCUMENTS.find((x) => x.key === e.key)
|
||||||
|
assert.ok(d, e.key)
|
||||||
|
assert.equal(d.title, e.title)
|
||||||
|
assert.equal(d.sort, e.sort)
|
||||||
|
assert.equal(d.liveUrl, `${base}${e.key}.html`)
|
||||||
|
assert.ok(d.liveUrl.startsWith(base))
|
||||||
|
assert.ok(!/\d{4}-\d{2}-\d{2}/.test(d.liveUrl), 'Alias-Seite ohne Fassungsdatum')
|
||||||
|
}
|
||||||
|
assert.equal(DOCUMENTS.length, 9, 'keine Analyse-Karten und keine weiteren')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('bestehende_karten_unveraendert', () => {
|
||||||
|
assert.deepEqual(DOCUMENTS.slice(0, 6), [
|
||||||
|
{ key: 'agb', title: 'AGB', liveUrl: 'https://webklar.com/agb', sort: 1 },
|
||||||
|
{ key: 'abo-bedingungen', title: 'Abo-Bedingungen', liveUrl: 'https://project.webklar.com/abo-bedingungen.html', sort: 2 },
|
||||||
|
{ key: 'avv', title: 'AVV (Auftragsverarbeitung)', liveUrl: 'https://project.webklar.com/abo-bedingungen.html', sort: 3 },
|
||||||
|
{ key: 'datenschutz', title: 'Datenschutzerklärung (Website und Portal)', liveUrl: 'https://project.webklar.com/datenschutz.html', sort: 4 },
|
||||||
|
{ key: 'impressum', title: 'Impressum', liveUrl: 'https://webklar.com/impressum', sort: 5 },
|
||||||
|
{ key: 'widerruf', title: 'Widerrufsbelehrung und -formular', liveUrl: 'https://project.webklar.com/widerrufen.html', sort: 6 },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dry_run_listet_karten', () => {
|
||||||
|
// Kein API-Key, Endpunkt auf einen geschlossenen Port: ein Netzaufruf wuerde scheitern, das Skript muss trotzdem sauber enden
|
||||||
|
const env = { ...process.env, APPWRITE_ENDPOINT: 'http://127.0.0.1:9/v1' }
|
||||||
|
delete env.APPWRITE_API_KEY
|
||||||
|
const r = spawnSync(process.execPath, ['--max-old-space-size=256', SCRIPT, '--dry-run'], { env, encoding: 'utf8', timeout: 30000 })
|
||||||
|
assert.equal(r.status, 0, r.stderr)
|
||||||
|
assert.ok(!/ECONNREFUSED|fetch failed|APPWRITE_API_KEY required/.test(r.stderr), r.stderr)
|
||||||
|
const zeilen = r.stdout.split('\n')
|
||||||
|
for (const d of DOCUMENTS) assert.ok(zeilen.includes(`Rechtstext ${d.key}: ${d.title}`), d.key)
|
||||||
|
assert.ok(zeilen.some((z) => z.startsWith('Rechtstext leistungsbeschreibung-terminplaner:')))
|
||||||
|
assert.ok(zeilen.some((z) => z.startsWith('Rechtstext avv-terminplaner:')))
|
||||||
|
assert.ok(zeilen.some((z) => z.startsWith('Rechtstext widerrufsbelehrung-funktionen:')))
|
||||||
|
assert.ok(!/angelegt|vorhanden|Fertig/.test(r.stdout), 'Trockenlauf legt nichts an')
|
||||||
|
})
|
||||||
Reference in new issue
Block a user