mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
Merge pull request #413 from PR0M3TH3AN/jules/test-cmd-backup-16743310224352411678
Add tests for cmdBackup
This commit is contained in:
2 files changed
+109
-7
No files matched your search
+10
-7
@@ -2,8 +2,6 @@ import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
const BACKUP_ROOT = path.resolve(process.cwd(), '.torch/backups');
|
||||
|
||||
// Paths to snapshot on each backup run
|
||||
const STATE_SOURCES = [
|
||||
'.scheduler-memory/memory-store.json',
|
||||
@@ -22,19 +20,21 @@ const STATE_SOURCES = [
|
||||
*
|
||||
* @param {Object} [opts]
|
||||
* @param {string|null} [opts.output] - Override destination directory (default: .torch/backups/<ts>)
|
||||
* @param {string} [opts.cwd] - Working directory (default: process.cwd())
|
||||
* @returns {Promise<{backupDir: string, manifest: Object}>}
|
||||
*/
|
||||
export async function cmdBackup({ output = null } = {}) {
|
||||
export async function cmdBackup({ output = null, cwd = process.cwd() } = {}) {
|
||||
const BACKUP_ROOT = path.resolve(cwd, '.torch/backups');
|
||||
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const backupDir = output
|
||||
? path.resolve(process.cwd(), output)
|
||||
? path.resolve(cwd, output)
|
||||
: path.join(BACKUP_ROOT, ts);
|
||||
|
||||
await fs.mkdir(backupDir, { recursive: true });
|
||||
|
||||
let gitCommit = null;
|
||||
try {
|
||||
gitCommit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
|
||||
gitCommit = execSync('git rev-parse HEAD', { stdio: 'pipe', cwd }).toString().trim();
|
||||
} catch {
|
||||
// Not in a git repo or git unavailable
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export async function cmdBackup({ output = null } = {}) {
|
||||
const skipped = [];
|
||||
|
||||
for (const relPath of STATE_SOURCES) {
|
||||
const src = path.resolve(process.cwd(), relPath);
|
||||
const src = path.resolve(cwd, relPath);
|
||||
const dest = path.join(backupDir, relPath.replace(/\//g, '__'));
|
||||
try {
|
||||
await fs.copyFile(src, dest);
|
||||
@@ -77,9 +77,12 @@ export async function cmdBackup({ output = null } = {}) {
|
||||
/**
|
||||
* Lists all available backups under .torch/backups/, newest first.
|
||||
*
|
||||
* @param {Object} [opts]
|
||||
* @param {string} [opts.cwd] - Working directory (default: process.cwd())
|
||||
* @returns {Promise<Array<{id: string, createdAt: string|null, backupDir: string}>>}
|
||||
*/
|
||||
export async function listBackups() {
|
||||
export async function listBackups({ cwd = process.cwd() } = {}) {
|
||||
const BACKUP_ROOT = path.resolve(cwd, '.torch/backups');
|
||||
try {
|
||||
const entries = await fs.readdir(BACKUP_ROOT, { withFileTypes: true });
|
||||
const backups = [];
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { test, after } from 'node:test';
|
||||
import assert from 'node:assert';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { cmdBackup, listBackups } from '../src/cmd-backup.mjs';
|
||||
|
||||
const tempBase = fs.mkdtempSync(path.join(process.cwd(), 'test-backup-'));
|
||||
|
||||
// Helper to setup dummy state files
|
||||
function setupStateFiles(cwd) {
|
||||
const memoryDir = path.join(cwd, '.scheduler-memory');
|
||||
const logsDir = path.join(cwd, 'task-logs', 'daily');
|
||||
|
||||
fs.mkdirSync(memoryDir, { recursive: true });
|
||||
fs.mkdirSync(logsDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(path.join(memoryDir, 'memory-store.json'), '{"dummy": "memory"}');
|
||||
fs.writeFileSync(path.join(logsDir, '.scheduler-run-state.json'), '{"dummy": "state"}');
|
||||
}
|
||||
|
||||
after(() => {
|
||||
fs.rmSync(tempBase, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('cmdBackup creates backup directory and manifest', async () => {
|
||||
const cwd = path.join(tempBase, 'scenario-1');
|
||||
fs.mkdirSync(cwd, { recursive: true });
|
||||
setupStateFiles(cwd);
|
||||
|
||||
const { backupDir } = await cmdBackup({ cwd });
|
||||
|
||||
assert.ok(fs.existsSync(backupDir), 'Backup directory created');
|
||||
assert.ok(fs.existsSync(path.join(backupDir, 'backup-manifest.json')), 'Manifest created');
|
||||
|
||||
const manifestContent = JSON.parse(fs.readFileSync(path.join(backupDir, 'backup-manifest.json'), 'utf8'));
|
||||
assert.strictEqual(manifestContent.backupDir, backupDir);
|
||||
assert.ok(manifestContent.createdAt);
|
||||
|
||||
// Verify files are copied
|
||||
const backupMemory = path.join(backupDir, '.scheduler-memory__memory-store.json');
|
||||
assert.ok(fs.existsSync(backupMemory), 'Memory file copied');
|
||||
|
||||
// captured should contain the 2 files
|
||||
assert.strictEqual(manifestContent.captured.length, 2);
|
||||
});
|
||||
|
||||
test('cmdBackup handles missing files gracefully', async () => {
|
||||
const cwd = path.join(tempBase, 'scenario-2');
|
||||
fs.mkdirSync(cwd, { recursive: true });
|
||||
// Do NOT setup files
|
||||
|
||||
const { manifest } = await cmdBackup({ cwd });
|
||||
|
||||
assert.strictEqual(manifest.captured.length, 0);
|
||||
assert.strictEqual(manifest.skipped.length, 2);
|
||||
assert.strictEqual(manifest.skipped[0].reason, 'not found');
|
||||
});
|
||||
|
||||
test('cmdBackup respects output override', async () => {
|
||||
const cwd = path.join(tempBase, 'scenario-3');
|
||||
fs.mkdirSync(cwd, { recursive: true });
|
||||
setupStateFiles(cwd);
|
||||
|
||||
const outputDir = 'custom-backup-location';
|
||||
const { backupDir } = await cmdBackup({ cwd, output: outputDir });
|
||||
|
||||
const expectedPath = path.resolve(cwd, outputDir);
|
||||
assert.strictEqual(backupDir, expectedPath);
|
||||
assert.ok(fs.existsSync(expectedPath));
|
||||
assert.ok(fs.existsSync(path.join(expectedPath, 'backup-manifest.json')), 'Manifest in custom output');
|
||||
});
|
||||
|
||||
test('listBackups lists created backups', async () => {
|
||||
const cwd = path.join(tempBase, 'scenario-4');
|
||||
fs.mkdirSync(cwd, { recursive: true });
|
||||
setupStateFiles(cwd);
|
||||
|
||||
// Create two backups
|
||||
await cmdBackup({ cwd });
|
||||
|
||||
// Ensure we have a slight delay or just rely on execution time for timestamp diff
|
||||
// Windows/Filesystem might have low resolution, but listBackups sorts by ISO string in manifest
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
|
||||
await cmdBackup({ cwd });
|
||||
|
||||
const backups = await listBackups({ cwd });
|
||||
assert.strictEqual(backups.length, 2);
|
||||
|
||||
// Verify sorting (newest first)
|
||||
const t0 = new Date(backups[0].createdAt).getTime();
|
||||
const t1 = new Date(backups[1].createdAt).getTime();
|
||||
assert.ok(t0 >= t1, 'Backups sorted newest first');
|
||||
|
||||
// Verify properties
|
||||
assert.ok(backups[0].id);
|
||||
assert.ok(backups[0].backupDir);
|
||||
assert.ok(Array.isArray(backups[0].captured));
|
||||
});
|
||||
Reference in new issue
Block a user