Export: Formelschutz nur fuer Text (inkl. Tab/CR), CSV mit Rechten 0600, Runbook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-01 18:50:36 +00:00
1 parent 495c3a2c40
commit 546d3c84d0
2 files changed
+28 -3

No files matched your search

+21 -1
View File
@@ -1,7 +1,7 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import crypto from 'node:crypto';
import { mkdtempSync, readFileSync, readdirSync } from 'node:fs';
import { mkdtempSync, readFileSync, readdirSync, statSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
@@ -71,3 +71,23 @@ test('CLI: unbekannter oder ungültiger Schlüssel => Exit 1 ohne Dateien', () =
assert.equal(ok.status, 0);
assert.equal(readdirSync(join(dir, 'ok')).length, 3);
});
test('Export: Formelschutz nur fuer Text (Tab, CR, Minus), Zahlen bleiben unveraendert, Dateien 0600', () => {
const { dir, dbPath } = setup();
const db = new DatabaseSync(dbPath);
const ts = Date.UTC(2026, 8, 1, 10);
const ins = db.prepare('INSERT INTO clicks (site, ts, path, vp, x, y, sel) VALUES (?,?,?,?,?,?,?)');
ins.run(KEY, ts, '/', 'd', -3, -4.5, '\tcmd');
ins.run(KEY, ts, '/', 'd', 1, 2, '\rcmd');
ins.run(KEY, ts, '/', 'd', 1, 2, '-cmd');
db.close();
const out = join(dir, 'out');
const files = exportSite({ dbPath, siteKey: KEY, outDir: out });
const c = readFileSync(join(out, `${KEY}-klicks.csv`), 'utf8');
assert.ok(c.includes(',-3,-4.5,'), 'negative Zahlen ohne Praefix');
assert.ok(!c.includes("'-3"));
assert.ok(c.includes(",\"'\tcmd\"") || c.includes(",'\tcmd"), 'Tab gesichert');
assert.ok(c.includes(`"'\rcmd"`), 'CR gesichert');
assert.ok(c.includes(",'-cmd"));
for (const f of files) assert.equal(statSync(f).mode & 0o777, 0o600);
});