Files
profilarr/tests/unit/backups/createBackup.test.ts
2026-04-08 06:52:33 +09:30

288 lines
9.0 KiB
TypeScript

/**
* Tests for createBackup job
* Tests the creation of compressed backups
*/
import { BaseTest } from '../base/BaseTest.ts';
import { createBackup } from '../../../src/lib/server/jobs/logic/createBackup.ts';
import { assertEquals } from '@std/assert';
class CreateBackupTest extends BaseTest {
/**
* Test 1: Basic successful backup
*/
testBasicSuccessfulBackup(): void {
this.test('basic successful backup', async (context) => {
// Setup: Create source directory with test files
const sourceDir = `${context.tempDir}/source`;
await Deno.mkdir(sourceDir, { recursive: true });
// Create some test files
await Deno.writeTextFile(`${sourceDir}/file1.txt`, 'test content 1\n');
await Deno.writeTextFile(`${sourceDir}/file2.txt`, 'test content 2\n');
await Deno.writeTextFile(`${sourceDir}/file3.json`, JSON.stringify({ test: 'data' }) + '\n');
// Create backup directory
const backupDir = `${context.tempDir}/backups`;
// Run backup
const result = await createBackup(sourceDir, backupDir);
// Assert success
assertEquals(result.success, true);
assertEquals(typeof result.filename, 'string');
assertEquals(typeof result.sizeBytes, 'number');
// Assert size is greater than 0
if (result.sizeBytes) {
assertEquals(result.sizeBytes > 0, true);
}
// Assert no error
assertEquals(result.error, undefined);
});
}
/**
* Test 2: Backup filename format
*/
testBackupFilenameFormat(): void {
this.test('backup filename format', async (context) => {
// Setup: Create source directory with test files
const sourceDir = `${context.tempDir}/source`;
await Deno.mkdir(sourceDir, { recursive: true });
await Deno.writeTextFile(`${sourceDir}/test.txt`, 'content\n');
const backupDir = `${context.tempDir}/backups`;
// Use a custom timestamp for predictable filename
const customTimestamp = new Date('2025-01-15T14:30:45.000Z');
// Run backup with custom timestamp
const result = await createBackup(sourceDir, backupDir, customTimestamp);
// Assert success
assertEquals(result.success, true);
// Assert filename matches expected format: backup-2025-01-15-143045.tar.gz
assertEquals(result.filename, 'backup-2025-01-15-143045.tar.gz');
});
}
/**
* Test 3: Backup file exists and is valid
*/
testBackupFileExistsAndValid(): void {
this.test('backup file exists and is valid', async (context) => {
// Setup: Create source directory with test files
const sourceDir = `${context.tempDir}/source`;
await Deno.mkdir(sourceDir, { recursive: true });
await Deno.writeTextFile(`${sourceDir}/file1.txt`, 'test content\n');
await Deno.writeTextFile(`${sourceDir}/file2.txt`, 'more content\n');
const backupDir = `${context.tempDir}/backups`;
// Run backup
const result = await createBackup(sourceDir, backupDir);
// Assert success
assertEquals(result.success, true);
// Construct backup file path
const backupFilePath = `${backupDir}/${result.filename}`;
// Assert backup file exists
await this.assertFileExists(backupFilePath);
// Get file stats and verify size
const stat = await Deno.stat(backupFilePath);
assertEquals(stat.size > 0, true);
// Verify returned sizeBytes matches actual file size
assertEquals(result.sizeBytes, stat.size);
});
}
/**
* Test 4: Backup contains expected files
*/
testBackupContainsExpectedFiles(): void {
this.test('backup contains expected files', async (context) => {
// Setup: Create source directory with specific test files
const sourceDir = `${context.tempDir}/source`;
await Deno.mkdir(sourceDir, { recursive: true });
const testContent1 = 'This is test file 1\n';
const testContent2 = 'This is test file 2\n';
const testContent3 = JSON.stringify({ key: 'value', data: [1, 2, 3] });
await Deno.writeTextFile(`${sourceDir}/file1.txt`, testContent1);
await Deno.writeTextFile(`${sourceDir}/file2.txt`, testContent2);
await Deno.writeTextFile(`${sourceDir}/data.json`, testContent3);
const backupDir = `${context.tempDir}/backups`;
// Run backup
const result = await createBackup(sourceDir, backupDir);
// Assert success
assertEquals(result.success, true);
// Extract backup to verify contents
const extractDir = `${context.tempDir}/extract`;
await Deno.mkdir(extractDir, { recursive: true });
const backupFilePath = `${backupDir}/${result.filename}`;
// Extract tar.gz
const extractCommand = new Deno.Command('tar', {
args: ['-xzf', backupFilePath, '-C', extractDir],
stdout: 'piped',
stderr: 'piped'
});
const { code } = await extractCommand.output();
assertEquals(code, 0);
// Verify extracted files exist and have correct content
const extractedSourceDir = `${extractDir}/data`;
await this.assertFileExists(`${extractedSourceDir}/file1.txt`);
await this.assertFileExists(`${extractedSourceDir}/file2.txt`);
await this.assertFileExists(`${extractedSourceDir}/data.json`);
// Verify file contents
const extractedContent1 = await Deno.readTextFile(`${extractedSourceDir}/file1.txt`);
const extractedContent2 = await Deno.readTextFile(`${extractedSourceDir}/file2.txt`);
const extractedContent3 = await Deno.readTextFile(`${extractedSourceDir}/data.json`);
assertEquals(extractedContent1, testContent1);
assertEquals(extractedContent2, testContent2);
assertEquals(extractedContent3, testContent3);
});
}
/**
* Test 5: Non-existent source directory
*/
testNonExistentSourceDirectory(): void {
this.test('non-existent source directory', async (context) => {
// Setup: Create path to non-existent directory
const sourceDir = `${context.tempDir}/does-not-exist`;
const backupDir = `${context.tempDir}/backups`;
// Run backup on non-existent directory
const result = await createBackup(sourceDir, backupDir);
// Assert failure
assertEquals(result.success, false);
// Assert error message mentions source directory
assertEquals(typeof result.error, 'string');
if (result.error) {
assertEquals(result.error.includes('Source directory does not exist'), true);
}
// Assert no filename or size returned
assertEquals(result.filename, undefined);
assertEquals(result.sizeBytes, undefined);
});
}
/**
* Test 6: Source path is a file, not directory
*/
testSourcePathIsFile(): void {
this.test('source path is a file, not directory', async (context) => {
// Setup: Create a file instead of a directory
const sourcePath = `${context.tempDir}/somefile.txt`;
await Deno.writeTextFile(sourcePath, 'this is a file\n');
const backupDir = `${context.tempDir}/backups`;
// Run backup on file path
const result = await createBackup(sourcePath, backupDir);
// Assert failure
assertEquals(result.success, false);
// Assert error message mentions not a directory
assertEquals(typeof result.error, 'string');
if (result.error) {
assertEquals(result.error.includes('Source path is not a directory'), true);
}
// Assert no filename or size returned
assertEquals(result.filename, undefined);
assertEquals(result.sizeBytes, undefined);
});
}
/**
* Test 7: Empty source directory
*/
testEmptySourceDirectory(): void {
this.test('empty source directory', async (context) => {
// Setup: Create an empty source directory
const sourceDir = `${context.tempDir}/empty-source`;
await Deno.mkdir(sourceDir, { recursive: true });
const backupDir = `${context.tempDir}/backups`;
// Run backup on empty directory
const result = await createBackup(sourceDir, backupDir);
// Assert success
assertEquals(result.success, true);
assertEquals(typeof result.filename, 'string');
assertEquals(typeof result.sizeBytes, 'number');
// Assert backup file exists
const backupFilePath = `${backupDir}/${result.filename}`;
await this.assertFileExists(backupFilePath);
// Assert file has size > 0 (even empty tar.gz has some size for headers)
if (result.sizeBytes) {
assertEquals(result.sizeBytes > 0, true);
}
// Extract and verify empty directory structure is preserved
const extractDir = `${context.tempDir}/extract`;
await Deno.mkdir(extractDir, { recursive: true });
const extractCommand = new Deno.Command('tar', {
args: ['-xzf', backupFilePath, '-C', extractDir],
stdout: 'piped',
stderr: 'piped'
});
const { code } = await extractCommand.output();
assertEquals(code, 0);
// Verify extracted directory exists and is empty
const extractedSourceDir = `${extractDir}/data`;
await this.assertFileExists(extractedSourceDir);
// Read directory to verify it's empty
const entries = [];
for await (const entry of Deno.readDir(extractedSourceDir)) {
entries.push(entry);
}
assertEquals(entries.length, 0);
});
}
runTests(): void {
this.testBasicSuccessfulBackup();
this.testBackupFilenameFormat();
this.testBackupFileExistsAndValid();
this.testBackupContainsExpectedFiles();
this.testNonExistentSourceDirectory();
this.testSourcePathIsFile();
this.testEmptySourceDirectory();
}
}
// Run tests
const test = new CreateBackupTest();
await test.runTests();