fix(api): Logzeile, wenn die Rückgabe von delete(owner) nach einem Fehler beim Hochladen scheitert

Scheitert das Festlegen oder die zweite Prüfung beim Hochladen eines Logos, nimmt die API die Zeile zurück und gibt der Datei delete("user:<ich>")
zurück, damit der Client sie aufräumen kann. Scheiterte diese Rückgabe, blieb die Datei ohne Zeile und ohne dieses Recht liegen, und das Log
schwieg: die Antwort bleibt der ursprüngliche Fehler, ein Rest ohne Zeile ist sonst nicht zu finden.

- logos.js: eine Logzeile `restore failed for logo <Zeilen-ID> (file <Datei-ID>)` (nur IDs, wie `rollback failed for logo …`).
- Test: zweite Prüfung scheitert, Zeile wird zurückgenommen, die Rückgabe scheitert: genau diese Zeile, Antwort 502, Datei bleibt festgelegt;
  ohne Fehler und bei gescheitertem Zurücknehmen steht keine solche Zeile im Log.
- README: neue Logzeile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 05:11:50 +00:00
1 parent 6134fd26d7
commit 7cb1381566
3 files changed
+32 -5

No files matched your search

+23 -1
View File
@@ -540,15 +540,37 @@ test('Logo hochladen: scheitert auch das Zurücknehmen, bleibt die Zeile stehen,
const lines = logs.filter((line) => line.includes('rollback failed'));
assert.equal(lines.length, 1);
assert.match(lines[0], new RegExp(`^\\[aircard-api\\] \\S+ rollback failed for logo ${rowOf('logo1')} \\(file logo1\\)$`));
assert.equal(logs.filter((line) => line.includes('restore failed')).length, 0, 'die Datei wird gar nicht erst angefasst');
assert.ok(!logs.join('\n').includes(JWT_ME));
});
test('Logo hochladen: scheitert nach dem Zurücknehmen der Zeile die Rückgabe von delete(owner), steht `restore failed for logo <id> (file <id>)` im Log, die Antwort bleibt der ursprüngliche Fehler', async () => {
const { upload, fake, logs } = await setup();
logoFile(fake, 'logo1');
let reads = 0;
let puts = 0;
fake.failNext((c) => c.method === 'GET' && c.path.endsWith('/files/logo1/view') && (reads += 1) === 2, { status: 500 }); // zweite Prüfung: Rücknahme
fake.failNext((c) => c.method === 'PUT' && c.path.endsWith('/files/logo1') && (puts += 1) === 2, { status: 500 }); // das 2. PUT gibt delete(owner) zurück
expectError(await upload(uploadBody()), 502, 'unavailable');
assert.equal(fake.rows('logos').length, 0, 'die Zeile ist zurückgenommen');
assert.deepEqual(fake.file('logo1').$permissions, ['read("any")'], 'die Datei blieb festgelegt, ohne delete(owner): der Client kann sie nicht mehr löschen');
assert.equal(filePuts(fake, 'logo1').length, 2);
// eine Zeile im Log, mit Zeilen- und Datei-ID (ohne die Datei findet niemand mehr den Rest: die Zeile ist ja weg), nie JWT oder Konto
const lines = logs.filter((line) => line.includes('restore failed'));
assert.equal(lines.length, 1);
assert.match(lines[0], new RegExp(`^\\[aircard-api\\] \\S+ restore failed for logo ${rowOf('logo1')} \\(file logo1\\)$`));
assert.equal(logs.filter((line) => line.includes('rollback failed')).length, 0, 'die Zeile ließ sich zurücknehmen');
assert.ok(!logs.join('\n').includes(JWT_ME));
assert.ok(!logs.join('\n').includes(ME));
});
test('Logo hochladen: gelingt das Zurücknehmen, steht keine Rollback-Fehlerzeile im Log', async () => {
const { upload, fake, logs } = await setup();
logoFile(fake, 'logo1');
fake.failNext((c) => c.method === 'PUT' && c.path.endsWith('/files/logo1'), { status: 500 });
assert.equal((await upload(uploadBody())).status, 502);
assert.equal(logs.filter((line) => line.includes('rollback failed')).length, 0);
assert.equal(logs.filter((line) => line.includes('rollback failed') || line.includes('restore failed')).length, 0);
});
// ---- Hochladen: der Inhalt wird nach dem Festlegen der Rechte noch einmal geprüft --------------------------