mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-13 18:29:32 +01:00
Merge-time fix for #661. The pre-restore copy was forced, so a restore made while the live files equalled the newest backup spent a retention slot on a duplicate. It now goes through the normal change check: when nothing changed since the newest backup, no copy is made and safetySnapshot names that backup. Two tests that relied on the forced copy now change a file before the restore; a new one pins the reuse. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
/**
|
|
* @fileoverview The snapshot origin must survive a hostname change (a Docker container gets a new
|
|
* hostname on every recreate) and still differ between data dirs.
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
const host = vi.hoisted(() => ({ name: 'container-a' }));
|
|
vi.mock('node:os', async (importOriginal) => ({
|
|
...(await importOriginal<typeof import('node:os')>()),
|
|
hostname: () => host.name,
|
|
}));
|
|
|
|
import {
|
|
configBackupOrigin,
|
|
createConfigBackup,
|
|
listConfigBackups,
|
|
pruneConfigBackups,
|
|
restoreConfigBackup,
|
|
} from '../src/config-backup.js';
|
|
|
|
let root: string;
|
|
let data: string;
|
|
let backups: string;
|
|
beforeEach(() => {
|
|
root = mkdtempSync(join(tmpdir(), 'cfg-origin-'));
|
|
data = join(root, 'data');
|
|
backups = join(root, 'backups');
|
|
mkdirSync(data, { recursive: true });
|
|
writeFileSync(join(data, 'settings.json'), '{"a":1}');
|
|
host.name = 'container-a';
|
|
});
|
|
afterEach(() => rmSync(root, { recursive: true, force: true }));
|
|
|
|
describe('configBackupOrigin', () => {
|
|
it('is made once, kept in the data dir, and differs between data dirs', () => {
|
|
const a = configBackupOrigin(data);
|
|
expect(configBackupOrigin(data)).toBe(a);
|
|
expect(readFileSync(join(data, 'config-backup-id'), 'utf8').trim()).toBe(a);
|
|
const other = join(root, 'other');
|
|
expect(configBackupOrigin(other)).not.toBe(a);
|
|
});
|
|
|
|
it('keeps snapshots listed, restorable and prunable after the hostname changes', () => {
|
|
const made = createConfigBackup({ dataDir: data, backupDir: backups, reason: 'auto', now: 1_000_000 }) as {
|
|
id: string;
|
|
};
|
|
createConfigBackup({ dataDir: data, backupDir: backups, reason: 'manual', force: true, now: 2_000_000 });
|
|
host.name = 'container-b'; // the container was recreated
|
|
const origin = configBackupOrigin(data);
|
|
expect(listConfigBackups(backups, origin)).toHaveLength(2);
|
|
writeFileSync(join(data, 'settings.json'), '{"a":2}'); // a change, so the restore takes a pre-restore copy
|
|
expect(() => restoreConfigBackup({ dataDir: data, backupDir: backups, id: made.id })).not.toThrow();
|
|
const removed = pruneConfigBackups(backups, { keepCount: 1, keepDays: 0, origin });
|
|
expect(removed).toHaveLength(2); // 3 now: the restore took a pre-restore copy
|
|
});
|
|
});
|