add rollback tests and update test descriptions #3
@@ -9,7 +9,7 @@ import { join } from 'https://deno.land/std@0.224.0/path/mod.ts';
|
|||||||
import { resolveUnitTargetPath, writeUnitFiles } from '../mod.ts';
|
import { resolveUnitTargetPath, writeUnitFiles } from '../mod.ts';
|
||||||
import { TimerOptions } from '../../types/options.ts';
|
import { TimerOptions } from '../../types/options.ts';
|
||||||
|
|
||||||
Deno.test('writeUnitFiles schreibt .service und .timer korrekt', async () => {
|
Deno.test('writeUnitFiles: writes .service and .timer files correctly', async () => {
|
||||||
const tmp = await Deno.makeTempDir({ prefix: 'test-units-' });
|
const tmp = await Deno.makeTempDir({ prefix: 'test-units-' });
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
@@ -28,15 +28,15 @@ Deno.test('writeUnitFiles schreibt .service und .timer korrekt', async () => {
|
|||||||
options as TimerOptions,
|
options as TimerOptions,
|
||||||
) as { servicePath: string; timerPath: string };
|
) as { servicePath: string; timerPath: string };
|
||||||
|
|
||||||
// Überprüfe Pfade
|
// Check file paths
|
||||||
assertEquals(servicePath, join(tmp, 'test-backup.service'));
|
assertEquals(servicePath, join(tmp, 'test-backup.service'));
|
||||||
assertEquals(timerPath, join(tmp, 'test-backup.timer'));
|
assertEquals(timerPath, join(tmp, 'test-backup.timer'));
|
||||||
|
|
||||||
// Existieren Dateien?
|
// Check if files exist
|
||||||
assertExists(await Deno.stat(servicePath));
|
assertExists(await Deno.stat(servicePath));
|
||||||
assertExists(await Deno.stat(timerPath));
|
assertExists(await Deno.stat(timerPath));
|
||||||
|
|
||||||
// Enthält die Datei den erwarteten Inhalt?
|
// Check if file contents match expectations
|
||||||
const readService = await Deno.readTextFile(servicePath);
|
const readService = await Deno.readTextFile(servicePath);
|
||||||
const readTimer = await Deno.readTextFile(timerPath);
|
const readTimer = await Deno.readTextFile(timerPath);
|
||||||
|
|
||||||
@@ -44,23 +44,23 @@ Deno.test('writeUnitFiles schreibt .service und .timer korrekt', async () => {
|
|||||||
assertStringIncludes(readTimer, 'OnCalendar=daily');
|
assertStringIncludes(readTimer, 'OnCalendar=daily');
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('resolveUnitTargetPath mit --output', () => {
|
Deno.test('resolveUnitTargetPath: with --output', () => {
|
||||||
const result = resolveUnitTargetPath({ output: '/tmp/units', user: false });
|
const result = resolveUnitTargetPath({ output: '/tmp/units', user: false });
|
||||||
assertEquals(result, '/tmp/units');
|
assertEquals(result, '/tmp/units');
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('resolveUnitTargetPath mit --user ohne output', () => {
|
Deno.test('resolveUnitTargetPath: with --user and no output', () => {
|
||||||
Deno.env.set('HOME', '/home/maxp');
|
Deno.env.set('HOME', '/home/maxp');
|
||||||
const result = resolveUnitTargetPath({ output: undefined, user: true });
|
const result = resolveUnitTargetPath({ output: undefined, user: true });
|
||||||
assertEquals(result, '/home/maxp/.config/systemd/user');
|
assertEquals(result, '/home/maxp/.config/systemd/user');
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('resolveUnitTargetPath ohne output und ohne user', () => {
|
Deno.test('resolveUnitTargetPath: with no output and no user', () => {
|
||||||
const result = resolveUnitTargetPath({ output: undefined, user: false });
|
const result = resolveUnitTargetPath({ output: undefined, user: false });
|
||||||
assertEquals(result, '/etc/systemd/system');
|
assertEquals(result, '/etc/systemd/system');
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('writeUnitFiles: Fehler beim Schreiben der .timer-Datei führt zu Rollback', async () => {
|
Deno.test('writeUnitFiles: error writing .timer file triggers rollback', async () => {
|
||||||
const tmp = await Deno.makeTempDir();
|
const tmp = await Deno.makeTempDir();
|
||||||
|
|
||||||
const options: TimerOptions = {
|
const options: TimerOptions = {
|
||||||
@@ -77,14 +77,14 @@ Deno.test('writeUnitFiles: Fehler beim Schreiben der .timer-Datei führt zu Roll
|
|||||||
const servicePath = join(tmp, `${name}.service`);
|
const servicePath = join(tmp, `${name}.service`);
|
||||||
const timerPath = join(tmp, `${name}.timer`);
|
const timerPath = join(tmp, `${name}.timer`);
|
||||||
|
|
||||||
// Simuliere: Schreiben der .timer-Datei schlägt fehl
|
// Simulate: writing the .timer file fails
|
||||||
const originalWrite = Deno.writeTextFile;
|
const originalWrite = Deno.writeTextFile;
|
||||||
const writeStub = stub(
|
const writeStub = stub(
|
||||||
Deno,
|
Deno,
|
||||||
'writeTextFile',
|
'writeTextFile',
|
||||||
async (path: string | URL, data: string | ReadableStream<string>) => {
|
async (path: string | URL, data: string | ReadableStream<string>) => {
|
||||||
if (typeof path === 'string' && path.endsWith('.timer')) {
|
if (typeof path === 'string' && path.endsWith('.timer')) {
|
||||||
throw new Error('Simulierter Schreibfehler');
|
throw new Error('Simulated write error');
|
||||||
} else {
|
} else {
|
||||||
return await originalWrite(path, data);
|
return await originalWrite(path, data);
|
||||||
}
|
}
|
||||||
@@ -98,17 +98,17 @@ Deno.test('writeUnitFiles: Fehler beim Schreiben der .timer-Datei führt zu Roll
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Erwartung: Funktion gibt undefined zurück
|
// Expect: function returns undefined
|
||||||
assertEquals(result, undefined);
|
assertEquals(result, undefined);
|
||||||
|
|
||||||
// Erwartung: Beide Dateien wurden gelöscht (Rollback)
|
// Expect: both files have been deleted (rollback)
|
||||||
assertEquals(await exists(servicePath), false);
|
assertEquals(await exists(servicePath), false);
|
||||||
assertEquals(await exists(timerPath), false);
|
assertEquals(await exists(timerPath), false);
|
||||||
|
|
||||||
writeStub.restore();
|
writeStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('writeUnitFiles: Fehler beim Schreiben der .service-Datei verhindert Folgeaktionen', async () => {
|
Deno.test('writeUnitFiles: error writing .service file prevents further actions', async () => {
|
||||||
const tmp = await Deno.makeTempDir();
|
const tmp = await Deno.makeTempDir();
|
||||||
|
|
||||||
const options: TimerOptions = {
|
const options: TimerOptions = {
|
||||||
@@ -125,13 +125,13 @@ Deno.test('writeUnitFiles: Fehler beim Schreiben der .service-Datei verhindert F
|
|||||||
const servicePath = join(tmp, `${name}.service`);
|
const servicePath = join(tmp, `${name}.service`);
|
||||||
const timerPath = join(tmp, `${name}.timer`);
|
const timerPath = join(tmp, `${name}.timer`);
|
||||||
|
|
||||||
// Simuliere: Fehler beim Schreiben der .service-Datei
|
// Simulate: error writing the .service file
|
||||||
const writeStub = stub(
|
const writeStub = stub(
|
||||||
Deno,
|
Deno,
|
||||||
'writeTextFile',
|
'writeTextFile',
|
||||||
(path: string | URL, _data: string | ReadableStream<string>) => {
|
(path: string | URL, _data: string | ReadableStream<string>) => {
|
||||||
if (typeof path === 'string' && path.endsWith('.service')) {
|
if (typeof path === 'string' && path.endsWith('.service')) {
|
||||||
throw new Error('Simulierter Service-Schreibfehler');
|
throw new Error('Simulated service write error');
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
@@ -144,17 +144,17 @@ Deno.test('writeUnitFiles: Fehler beim Schreiben der .service-Datei verhindert F
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Erwartung: Funktion gibt undefined zurück
|
// Expect: function returns undefined
|
||||||
assertEquals(result, undefined);
|
assertEquals(result, undefined);
|
||||||
|
|
||||||
// Erwartung: Es wurden keine Dateien angelegt
|
// Expect: no files were created
|
||||||
assertEquals(await exists(servicePath), false);
|
assertEquals(await exists(servicePath), false);
|
||||||
assertEquals(await exists(timerPath), false);
|
assertEquals(await exists(timerPath), false);
|
||||||
|
|
||||||
writeStub.restore();
|
writeStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('writeUnitFiles: beide Dateien geschrieben, danach Fehler → vollständiger Rollback', async () => {
|
Deno.test('writeUnitFiles: both files written, then error → full rollback', async () => {
|
||||||
const tmp = await Deno.makeTempDir();
|
const tmp = await Deno.makeTempDir();
|
||||||
|
|
||||||
const options: TimerOptions = {
|
const options: TimerOptions = {
|
||||||
@@ -179,18 +179,16 @@ Deno.test('writeUnitFiles: beide Dateien geschrieben, danach Fehler → vollstä
|
|||||||
'writeTextFile',
|
'writeTextFile',
|
||||||
async (path: string | URL, data: string | ReadableStream<string>) => {
|
async (path: string | URL, data: string | ReadableStream<string>) => {
|
||||||
if (typeof path !== 'string') {
|
if (typeof path !== 'string') {
|
||||||
throw new Error('Unerwarteter Pfadtyp');
|
throw new Error('Unexpected path type');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simuliere beide Schreibvorgänge, aber wirf nach dem zweiten eine Exception
|
// Simulate both writes, then throw an error after the second
|
||||||
writeCount++;
|
writeCount++;
|
||||||
|
|
||||||
await originalWriteTextFile(path, data); // wirklich schreiben
|
await originalWriteTextFile(path, data);
|
||||||
|
|
||||||
if (writeCount === 2) {
|
if (writeCount === 2) {
|
||||||
throw new Error(
|
throw new Error('Simulated error after full write');
|
||||||
'Simulierter Fehler nach vollständigem Schreiben',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -202,17 +200,17 @@ Deno.test('writeUnitFiles: beide Dateien geschrieben, danach Fehler → vollstä
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Erwartung: Funktion gibt undefined zurück
|
// Expect: function returns undefined
|
||||||
assertEquals(result, undefined);
|
assertEquals(result, undefined);
|
||||||
|
|
||||||
// Erwartung: Beide Dateien wurden wieder entfernt
|
// Expect: both files were removed
|
||||||
assertEquals(await exists(servicePath), false);
|
assertEquals(await exists(servicePath), false);
|
||||||
assertEquals(await exists(timerPath), false);
|
assertEquals(await exists(timerPath), false);
|
||||||
|
|
||||||
writeStub.restore();
|
writeStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('writeUnitFiles: Rollback schlägt fehl, wenn Dateien nicht gelöscht werden können', async () => {
|
Deno.test('writeUnitFiles: rollback fails if files cannot be deleted', async () => {
|
||||||
const tmp = await Deno.makeTempDir();
|
const tmp = await Deno.makeTempDir();
|
||||||
|
|
||||||
const options: TimerOptions = {
|
const options: TimerOptions = {
|
||||||
@@ -229,9 +227,8 @@ Deno.test('writeUnitFiles: Rollback schlägt fehl, wenn Dateien nicht gelöscht
|
|||||||
const servicePath = join(tmp, `${name}.service`);
|
const servicePath = join(tmp, `${name}.service`);
|
||||||
const timerPath = join(tmp, `${name}.timer`);
|
const timerPath = join(tmp, `${name}.timer`);
|
||||||
|
|
||||||
// Originale Methoden sichern
|
|
||||||
const originalWriteTextFile = Deno.writeTextFile;
|
const originalWriteTextFile = Deno.writeTextFile;
|
||||||
const originalRemove = Deno.remove;
|
const _originalRemove = Deno.remove;
|
||||||
|
|
||||||
let writeCount = 0;
|
let writeCount = 0;
|
||||||
|
|
||||||
@@ -242,7 +239,7 @@ Deno.test('writeUnitFiles: Rollback schlägt fehl, wenn Dateien nicht gelöscht
|
|||||||
writeCount++;
|
writeCount++;
|
||||||
await originalWriteTextFile(path, data);
|
await originalWriteTextFile(path, data);
|
||||||
if (writeCount === 2) {
|
if (writeCount === 2) {
|
||||||
throw new Error('Fehler nach vollständigem Schreiben');
|
throw new Error('Error after full write');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -252,11 +249,10 @@ Deno.test('writeUnitFiles: Rollback schlägt fehl, wenn Dateien nicht gelöscht
|
|||||||
'remove',
|
'remove',
|
||||||
// deno-lint-ignore require-await
|
// deno-lint-ignore require-await
|
||||||
async (_path: string | URL, _opts?: Deno.RemoveOptions) => {
|
async (_path: string | URL, _opts?: Deno.RemoveOptions) => {
|
||||||
throw new Error('Löschen verboten!');
|
throw new Error('Deletion forbidden!');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// capture console output
|
|
||||||
const logs: string[] = [];
|
const logs: string[] = [];
|
||||||
const consoleStub = stub(console, 'error', (...args) => {
|
const consoleStub = stub(console, 'error', (...args) => {
|
||||||
logs.push(args.map((a) => String(a)).join(' '));
|
logs.push(args.map((a) => String(a)).join(' '));
|
||||||
@@ -270,15 +266,14 @@ Deno.test('writeUnitFiles: Rollback schlägt fehl, wenn Dateien nicht gelöscht
|
|||||||
);
|
);
|
||||||
assertEquals(result, undefined);
|
assertEquals(result, undefined);
|
||||||
|
|
||||||
// Dateien existieren noch, weil löschen fehlschlug
|
// Files still exist because deletion failed
|
||||||
assertEquals(await exists(servicePath), true);
|
assertEquals(await exists(servicePath), true);
|
||||||
assertEquals(await exists(timerPath), true);
|
assertEquals(await exists(timerPath), true);
|
||||||
|
|
||||||
// Fehlerausgabe enthält "rollback_failed"
|
// Error output contains "rollback_failed"
|
||||||
const combinedLogs = logs.join('\n');
|
const combinedLogs = logs.join('\n');
|
||||||
assertStringIncludes(combinedLogs, 'rollback_failed');
|
assertStringIncludes(combinedLogs, 'rollback_failed');
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
writeStub.restore();
|
writeStub.restore();
|
||||||
removeStub.restore();
|
removeStub.restore();
|
||||||
consoleStub.restore();
|
consoleStub.restore();
|
||||||
|
Reference in New Issue
Block a user