Files
Webklar-Kundenbereich/test/analyticsContract.test.js
T
2026-10-01 13:32:21 +00:00

63 lines
3.2 KiB
JavaScript

// Vertragstest: echtes Portal-Client gegen die echten Collector-Server (im Prozess, In-Memory-DB).
import { test, before, after } from 'node:test'
import assert from 'node:assert/strict'
import { createAnalyticsClient } from '../server/services/analyticsClient.js'
import { createAdminServer, createPublicServer } from '../analytics/src/server.js'
import { openDb } from '../analytics/src/db.js'
const SITE = 'abcdef0123456789abcd'
const DOMAIN = 'kunde.example.com'
const config = { adminToken: 'contract-admin-token', heatSecret: 'contract-heat-secret', distDir: new URL('../analytics/client/dist', import.meta.url).pathname, baseHost: 'project.webklar.com' }
let db, admin, pub, client, pubUrl
const listen = (s) => new Promise((r) => s.listen(0, '127.0.0.1', () => r(`http://127.0.0.1:${s.address().port}`)))
before(async () => {
db = openDb(':memory:')
const adminServer = createAdminServer({ config, db })
admin = adminServer
pub = createPublicServer({ config, db })
const adminUrl = await listen(admin)
pubUrl = await listen(pub)
client = createAnalyticsClient({ url: adminUrl, adminToken: config.adminToken, heatSecret: config.heatSecret })
})
after(() => { admin.close(); pub.close() })
const post = (body) => fetch(`${pubUrl}/a/e`, {
method: 'POST', body: JSON.stringify({ site: SITE, ...body }),
headers: { 'Content-Type': 'text/plain', Origin: `https://${DOMAIN}`, 'X-Forwarded-For': '203.0.113.7', 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Mobile Safari' },
})
test('Vertrag: putSite -> Ereignisse -> getStats -> heatToken -> deleteSite', async () => {
await client.putSite(SITE, { projectId: 'p1', domains: [DOMAIN], active: true })
await post({ t: 'pv', path: '/', ref: 'google.com', vp: 'd' })
await post({ t: 'pv', path: '/', ref: '', vp: 'd' })
await post({ t: 'pv', path: '/kontakt', ref: '', vp: 'd' })
await post({ t: 'ck', path: '/', vp: 'd', x: 0.25, y: 0.5, sel: 'body:nth-of-type(1) > a:nth-of-type(1)' })
await post({ t: 'ck', path: '/', vp: 'd', x: 0.75, y: 0.1, sel: 'body:nth-of-type(1) > a:nth-of-type(2)' })
await post({ t: 'ct', path: '/', kind: 'tel' })
await post({ t: 'bn', a: 'accept' })
const stats = await client.getStats(SITE, '7')
assert.equal(stats.totals.pv, 3)
assert.equal(stats.totals.contacts, 1)
assert.equal(stats.series.length, 7)
assert.equal(stats.series.reduce((n, d) => n + d.pv, 0), 3)
assert.deepEqual(stats.pages, [{ key: '/', count: 2 }, { key: '/kontakt', count: 1 }])
assert.deepEqual(stats.refs, [{ key: '', count: 2 }, { key: 'google.com', count: 1 }])
assert.equal(stats.banner.accept, 1)
assert.ok(Array.isArray(stats.devices) && Array.isArray(stats.countries) && Array.isArray(stats.contacts))
const token = client.heatToken({ siteKey: SITE, path: '/', vp: 'd' })
const heat = await fetch(`${pubUrl}/a/heat?token=${encodeURIComponent(token)}`)
assert.equal(heat.status, 200)
const { points } = await heat.json()
assert.equal(points.length, 2)
assert.deepEqual(points.map((p) => p.x).sort(), [0.25, 0.75])
await client.deleteSite(SITE)
await assert.rejects(client.getStats(SITE, '7'), (e) => e.status === 404)
assert.equal(db.prepare('SELECT COUNT(*) AS n FROM events_raw').get().n, 0)
})