- 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>
88 lines
4.7 KiB
JavaScript
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));
|
|
});
|