Files
erikandClaude Opus 5.5 fd44db2213 Update über den Git-Server, Tests, Repo ohne node_modules und Sicherungen
- Admin → Update: neuere Versionen (Tags) vom Git-Server anzeigen und per
  Knopf einspielen; ausgeführt vom Dienst auf dem Rechner
  (Docker/update-einrichten.sh, Docker/update-ausfuehren.sh), nur vorwärts,
  Fehler bei ausbleibendem Neustart, abgebrochene Läufe sperren nicht
- Tests (npm test): Zusammenführen paralleler Änderungen, Rechteprüfung
  beim Speichern; SESSIONS_DIR lenkt den Datenordner für Testserver um
- node_modules, Sicherungen und Uploads aus dem Repo genommen
- cors und requireRole() entfernt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 11:10:56 +01:00

88 lines
4.7 KiB
JavaScript

// Test: Rechteprüfung beim Speichern (fehlendesBearbeitungsrecht() in
// server.js). Startet einen echten Server mit leerer Datenbank in einem
// Wegwerf-Ordner (SESSIONS_DIR/DB_PATH), legt eine Rolle nur mit „Kommentar
// und Fortschritt“ an und prüft über POST /save-projects, was ein Konto mit
// dieser Rolle ändern darf. Aufruf: npm test
const test = require('node:test');
const assert = require('node:assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawn } = require('child_process');
const ordner = fs.mkdtempSync(path.join(os.tmpdir(), 'projektuebersicht-test-'));
const PORT = 3990 + Math.floor(Math.random() * 9);
const url = p => 'http://127.0.0.1:' + PORT + p;
let server;
async function anfrage(methode, pfad, cookie, body){
const r = await fetch(url(pfad), {
method: methode,
headers: Object.assign({ 'Content-Type': 'application/json' }, cookie ? { Cookie: cookie } : {}),
body: body === undefined ? undefined : JSON.stringify(body)
});
return { status: r.status, daten: await r.json().catch(() => null), cookie: (r.headers.get('set-cookie') || '').split(';')[0] };
}
async function anmelden(username, password){
const r = await anfrage('POST', '/login', null, { username, password });
assert.strictEqual(r.status, 200, 'Anmeldung ' + username + ': ' + JSON.stringify(r.daten));
return r.cookie;
}
test.before(async () => {
server = spawn(process.execPath, [path.join(__dirname, '../server.js')], {
env: Object.assign({}, process.env, { PORT: String(PORT), SESSIONS_DIR: ordner, DB_PATH: path.join(ordner, 'test.db'),
SPEICHER_MAX_MB: '', HAENGER_MAX_S: '' }),
stdio: 'ignore'
});
for(let i = 0; i < 50; i++){
try { if((await fetch(url('/health'))).ok) return; } catch(e){ /* startet noch */ }
await new Promise(r => setTimeout(r, 200));
}
throw new Error('Testserver startet nicht');
});
test.after(() => { if(server) server.kill(); fs.rmSync(ordner, { recursive: true, force: true }); });
test('Konto nur mit Kommentar-Recht: Kommentar ja, Terminplan nein', async () => {
const pw = /Passwort:\s+(\S+)/.exec(fs.readFileSync(path.join(ordner, 'INITIAL_ADMIN_PASSWORD.txt'), 'utf8'))[1];
const admin = await anmelden('admin', pw);
// Rolle mit „Anwendung nutzen“ und „Kommentar und Fortschritt“, sonst nichts.
const rolle = await anfrage('POST', '/rollen', admin, { name: 'Nur Kommentar' });
assert.strictEqual(rolle.status, 200, JSON.stringify(rolle.daten));
const key = rolle.daten.neu;
assert.strictEqual((await anfrage('PUT', '/rollen/' + key + '/rechte', admin, { rechte: ['app', 'wb.kommentar'], an: true })).status, 200);
const konto = await anfrage('POST', '/admin/users', admin, { username: 'leser', firstName: 'Lea', lastName: 'Leser',
role: key, department: 'ELT', password: 'geheim-1234' });
assert.strictEqual(konto.status, 200, JSON.stringify(konto.daten));
// Ein Projekt in der aktuellen Sitzung anlegen (als Administrator).
const geladen = (await anfrage('GET', '/load-all', admin)).daten;
const woche = geladen.weeks[geladen.weeks.length - 1];
woche.projects.push({ id: 'p1', projectUid: 'u1', nr: '99-0001', name: 'Testprojekt', group: 'Aktive Projekte',
gewerke: ['ELT'], plan: { todos: [] } });
const angelegt = await anfrage('POST', '/save-week', admin, { week: woche, baseUpdatedAt: woche.updatedAt });
assert.strictEqual(angelegt.status, 200, JSON.stringify(angelegt.daten));
const leser = await anmelden('leser', 'geheim-1234');
const projekt = () => anfrage('GET', '/load-all', leser).then(r => {
const w = r.daten.weeks[r.daten.weeks.length - 1];
return { label: w.label, p: w.projects.find(x => x.id === 'p1') };
});
// Aufgabe im Terminplan anlegen: braucht „Terminplan bearbeiten“.
let { label, p } = await projekt();
const base = p.updatedAt;
p.plan.todos.push({ id: 't1', title: 'Darf nicht gespeichert werden', start: '', end: '', assignees: [], dependsOn: [] });
const verboten = await anfrage('POST', '/save-projects', leser, { label, projects: [{ project: p, baseUpdatedAt: base }] });
assert.strictEqual(verboten.status, 403);
assert.strictEqual(verboten.daten.recht, 'proj.plan');
// Kommentar des Gewerks schreiben: erlaubt.
({ label, p } = await projekt());
assert.strictEqual(p.plan.todos.length, 0, 'die verbotene Aufgabe darf nicht gespeichert sein');
p.kommentare = { ELT: { status: 'Abstimmung mit TGA steht aus', statusAuthor: 'Lea Leser', statusAt: new Date().toISOString(), besprochen: false } };
const erlaubt = await anfrage('POST', '/save-projects', leser, { label, projects: [{ project: p, baseUpdatedAt: p.updatedAt }] });
assert.strictEqual(erlaubt.status, 200, JSON.stringify(erlaubt.daten));
});