Website-Analyse: AVV als Seite, www/Apex-Domains, Abo-Check unabhängig vom Schalter, Dev-Hinweis

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-01 11:04:50 +00:00
1 parent 7ead59ff73
commit 714687fe3b
10 files changed
+267 -20

No files matched your search

+12 -1
View File
@@ -89,7 +89,7 @@ test('Einschalten: Dokument, Sammeldienst, Neubau, Ticket, Mail K18', async () =
assert.equal(d.avvVersion, config.analyse.avvVersion)
assert.equal(d.avvAcceptedBy, 'customer')
assert.ok(d.avvAcceptedAt && d.activatedAt)
assert.deepEqual(puts, [{ siteKey: d.siteKey, projectId: 'p1', domains: ['baeckerei.project.webklar.com', 'baeckerei.de'], active: true }])
assert.deepEqual(puts, [{ siteKey: d.siteKey, projectId: 'p1', domains: ['baeckerei.project.webklar.com', 'baeckerei.de', 'www.baeckerei.de'], active: true }])
assert.deepEqual(rebuilds, ['p1'])
const tickets = analyseTickets()
assert.equal(tickets.length, 1)
@@ -243,3 +243,14 @@ test('gesperrte Analyse: Kunde 403, Mitarbeiter darf', async () => {
const r = await an.activateAnalyse({ customerId: 'c1', projectId: 'p10', consent: consent(), by: 'Kenso' })
assert.equal(r.doc.state, 'active')
})
test('analyseDomains: www-/Apex-Gegenstück der Live-Domain, ohne Doppelte, max. 5, nur gültige Hosts', () => {
const d = (live, sub = 'x') => an.analyseDomains({ subdomain: sub, liveDomain: live })
assert.deepEqual(d('example.de'), ['x.project.webklar.com', 'example.de', 'www.example.de'])
assert.deepEqual(d('https://www.Example.de/start'), ['x.project.webklar.com', 'www.example.de', 'example.de'])
assert.deepEqual(d(''), ['x.project.webklar.com'])
assert.deepEqual(d('kaputt!'), ['x.project.webklar.com'])
assert.ok(an.analyseDomains({ subdomain: 'x', liveDomain: 'a.b.c.example.de' }).length <= 5)
// gespeicherter Host bleibt die Live-Domain (Heatmap-Link, Ticket)
assert.deepEqual(an.analyseHosts({ subdomain: 'x', liveDomain: 'example.de' }), ['x.project.webklar.com', 'example.de'])
})
+15 -2
View File
@@ -52,6 +52,8 @@ aw.seed('subscriptions', wartungDoc)
aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Bäckerei', subdomain: 'baeckerei', liveDomain: 'baeckerei.de', isPublic: true })
aw.seed('websiteProjects', { $id: 'p2', customerId: 'c1', projectName: 'Privat', subdomain: 'privat', isPublic: false })
aw.seed('websiteProjects', { $id: 'p3', customerId: 'c1', projectName: 'Café', subdomain: 'cafe', isPublic: true })
aw.seed('websiteProjects', { $id: 'pd1', customerId: 'c1', projectName: 'Dev', subdomain: 'devsite', isPublic: true, previewDetected: 'dev:npm' })
aw.seed('websiteProjects', { $id: 'pd2', customerId: 'c1', projectName: 'Build', subdomain: 'buildsite', isPublic: true, previewDetected: 'build:npm' })
aw.seed('websiteProjects', { $id: 'p5', customerId: 'c2', projectName: 'Fremd', subdomain: 'fremd', isPublic: true })
const cookie = customerCookie('c1')
@@ -100,7 +102,7 @@ test('GET /api/funktionen: analyse-Zustand je Projekt, mit und ohne Schalter', a
const body = await get('/api/funktionen', { cookie })
const p = (id) => body.projects.find((x) => x.projectId === id)
assert.equal(p('p1').features.news.state, 'off')
assert.deepEqual(p('p1').analyse, { state: 'off', accentColor: '#111111', eligible: true, reason: null, reasonText: null, avvVersion: config.analyse.avvVersion })
assert.deepEqual(p('p1').analyse, { state: 'off', devMode: false, accentColor: '#111111', eligible: true, reason: null, reasonText: null, avvVersion: config.analyse.avvVersion })
assert.equal(p('p2').analyse.eligible, false)
assert.equal(p('p2').analyse.reason, 'nicht_oeffentlich')
assert.equal(p('p2').analyse.reasonText, an.ELIGIBILITY_MESSAGES.nicht_oeffentlich)
@@ -150,6 +152,17 @@ test('Stats: Weiterleitung, Zeitraum, 503 bei Ausfall', async () => {
assert.equal((await rawGet(`${A}/stats?projectId=p6`, { cookie })).status, 409)
})
test('GET /api/funktionen: devMode nur bei aktiver Analyse eines Dev-Projekts', async () => {
const view = async (id) => (await get('/api/funktionen', { cookie })).projects.find((x) => x.projectId === id).analyse
assert.equal((await view('pd1')).devMode, false, 'nicht aktiv: kein Hinweis')
await post(`${A}/activate`, { projectId: 'pd1', consent: consent() }, { cookie })
await post(`${A}/activate`, { projectId: 'pd2', consent: consent() }, { cookie })
assert.equal((await view('pd1')).devMode, true)
assert.equal((await view('pd2')).devMode, false, 'node_build wird gemessen')
await post(`${A}/deactivate`, { projectId: 'pd1' }, { cookie })
await post(`${A}/deactivate`, { projectId: 'pd2' }, { cookie })
})
test('Heatmap-Link: Prüfung, URL, Token', async () => {
await post(`${A}/activate`, { projectId: 'p1', consent: consent() }, { cookie })
const key = (await featureDoc('c1', 'p1')).siteKey
@@ -245,7 +258,7 @@ test('Fix b: Abschalten durch Mitarbeiter meldet Sammeldienst active:false', asy
test('Härtung: ungültige Hosts, Pfadzeichen, Fehler bei einem Projekt', async () => {
// analyseDomains: ungueltige Hosts werden uebersprungen
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'https://Cafe.DE/x' }), ['cafe.project.webklar.com', 'cafe.de'])
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'https://Cafe.DE/x' }), ['cafe.project.webklar.com', 'cafe.de', 'www.cafe.de'])
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'evil.de@x y' }), ['cafe.project.webklar.com'])
assert.deepEqual(an.analyseDomains({ subdomain: 'ca fe', liveDomain: 'a_b.de' }), [])
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'localhost' }), ['cafe.project.webklar.com'])
+35
View File
@@ -0,0 +1,35 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { existsSync, readFileSync } from 'node:fs'
const { config } = await import('../server/config.js')
const root = (rel) => new URL(`../${rel}`, import.meta.url)
test('AVV Website-Analyse: öffentliche Seite existiert, trägt Fassung und Entwurfshinweis, keine internen Kommentare', () => {
const file = root('public/rechtliches/avv-website-analyse.html')
assert.ok(existsSync(file))
const html = readFileSync(file, 'utf8')
assert.ok(html.includes(`Fassung ${config.analyse.avvVersion}`), 'avvVersion aus der Konfiguration steht auf der Seite')
assert.match(html, /Entwurf – vor Verwendung anwaltlich prüfen lassen/)
assert.doesNotMatch(html, /<!--/)
assert.match(html, /<h2>12\. Haftung/)
})
test('AVV-Link im Portal zeigt auf eine vorhandene Datei unter public/', () => {
const fn = readFileSync(root('public/funktionen.js'), 'utf8')
const url = fn.match(/const AVV_URL = '([^']+)'/)?.[1]
assert.equal(url, '/rechtliches/avv-website-analyse.html')
assert.ok(existsSync(root(`public${url}`)))
})
test('Portal: Dev-Modus-Hinweis an der Analyse-Kachel', () => {
const fn = readFileSync(root('public/funktionen.js'), 'utf8')
assert.match(fn, /Im Dev-Modus wird nicht gemessen/)
assert.match(fn, /a\.devMode/)
})
test('Texte: Akzentfarbe wird beim Einschalten gewählt (kein späteres Portal-Setting behauptet)', () => {
for (const f of ['docs/rechtliches/avv-website-analyse.md', 'docs/rechtliches/leistungsbeschreibung-website-analyse.md']) {
assert.match(readFileSync(root(f), 'utf8'), /beim Einschalten gewählt/, f)
}
})