Files
profilarr/tests/logger/cleanupLogs.test.ts
Sam Chau 5cd1bf82ff refactor(createBackup): extract logic and add comprehensive tests
- Extract pure backup logic to /src/jobs/logic/createBackup.ts
  - Update job definition to use extracted logic
  - Fix absolute path parsing for tar command
  - Add 7 tests: success cases, filename format, file extraction,
    error handling (non-existent dir, file instead of dir), empty dir
  - Uses BaseTest framework with temp directories and tar extraction
2025-10-21 09:02:58 +10:30

291 lines
9.9 KiB
TypeScript

/**
* Tests for cleanupLogs job
* Tests the cleanup of daily log files based on retention policy
*/
import { BaseTest } from "../base/BaseTest.ts";
import { cleanupLogs } from "../../src/jobs/logic/cleanupLogs.ts";
import { assertEquals } from "@std/assert";
class CleanupLogsTest extends BaseTest {
/**
* Test 1: Deletes files older than retention period
*/
testDeletesOldFiles(): void {
this.test("deletes files older than retention period", async (context) => {
// Setup: Create log files with dates
const today = new Date();
const oldDate = new Date(today);
oldDate.setDate(oldDate.getDate() - 35); // 35 days ago (older than 30 day retention)
const oldLogFile = `${context.tempDir}/${oldDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(
oldLogFile,
JSON.stringify({ test: "old log" }) + "\n",
);
// Assert old file exists before cleanup
await this.assertFileExists(oldLogFile);
// Run cleanup with 30 days retention
const result = await cleanupLogs(context.tempDir, 30);
// Assert 1 file was deleted, no errors
assertEquals(result.deletedCount, 1);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
// Assert old file was deleted
await this.assertFileNotExists(oldLogFile);
});
}
/**
* Test 2: Keeps files within retention period
*/
testKeepsRecentFiles(): void {
this.test("keeps files within retention period", async (context) => {
// Setup: Create a recent log file
const today = new Date();
const recentDate = new Date(today);
recentDate.setDate(recentDate.getDate() - 10); // 10 days ago (within 30 day retention)
const recentLogFile = `${context.tempDir}/${recentDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(
recentLogFile,
JSON.stringify({ test: "recent log" }) + "\n",
);
// Assert recent file exists before cleanup
await this.assertFileExists(recentLogFile);
// Run cleanup with 30 days retention
const result = await cleanupLogs(context.tempDir, 30);
// Assert no files were deleted, no errors
assertEquals(result.deletedCount, 0);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
// Assert recent file still exists
await this.assertFileExists(recentLogFile);
});
}
/**
* Test 3: Correctly parses YYYY-MM-DD.log format
*/
testParsesDateFormat(): void {
this.test("correctly parses YYYY-MM-DD.log format", async (context) => {
// Setup: Create log files with valid date format
const today = new Date();
// Recent file (6 days old - should keep)
const recentDate = new Date(today);
recentDate.setDate(recentDate.getDate() - 6);
const recentFile = `${context.tempDir}/${recentDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(recentFile, JSON.stringify({ test: "recent" }) + "\n");
// Old file (36 days old - should delete)
const oldDate = new Date(today);
oldDate.setDate(oldDate.getDate() - 36);
const oldFile = `${context.tempDir}/${oldDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(oldFile, JSON.stringify({ test: "old" }) + "\n");
// Assert both files exist before cleanup
await this.assertFileExists(recentFile);
await this.assertFileExists(oldFile);
// Run cleanup with 30 days retention
const result = await cleanupLogs(context.tempDir, 30);
// Assert only 1 file was deleted (the old one)
assertEquals(result.deletedCount, 1);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
// Assert recent file still exists, old file was deleted
await this.assertFileExists(recentFile);
await this.assertFileNotExists(oldFile);
});
}
/**
* Test 4: Ignores files that don't match pattern
*/
testIgnoresInvalidPatterns(): void {
this.test("ignores files that don't match pattern", async (context) => {
// Setup: Create files with various invalid names
const invalidFiles = [
`${context.tempDir}/app.log`, // No date
`${context.tempDir}/backup.txt`, // Not a log file
`${context.tempDir}/2025-10-15.txt`, // Wrong extension
`${context.tempDir}/log-2025-10-15.log`, // Date in wrong position
];
// Create all invalid files
for (const file of invalidFiles) {
await Deno.writeTextFile(file, "test content\n");
}
// Create one valid old file (36 days old - should be deleted)
const today = new Date();
const oldDate = new Date(today);
oldDate.setDate(oldDate.getDate() - 36);
const validOldFile = `${context.tempDir}/${oldDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(validOldFile, JSON.stringify({ test: "old" }) + "\n");
// Assert all files exist before cleanup
for (const file of invalidFiles) {
await this.assertFileExists(file);
}
await this.assertFileExists(validOldFile);
// Run cleanup with 30 days retention
const result = await cleanupLogs(context.tempDir, 30);
// Assert only 1 file was deleted (the valid old file)
assertEquals(result.deletedCount, 1);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
// Assert all invalid files still exist
for (const file of invalidFiles) {
await this.assertFileExists(file);
}
// Assert valid old file was deleted
await this.assertFileNotExists(validOldFile);
});
}
/**
* Test 5: Handles empty logs directory
*/
testHandlesEmptyDirectory(): void {
this.test("handles empty logs directory", async (context) => {
// Setup: context.tempDir is already created but empty (no files)
// No need to create any files
// Run cleanup with 30 days retention on empty directory
const result = await cleanupLogs(context.tempDir, 30);
// Assert no files were processed, no errors
assertEquals(result.deletedCount, 0);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
});
}
/**
* Test 6: Handles non-existent logs directory
*/
testHandlesNonExistentDirectory(): void {
this.test("handles non-existent logs directory", async (context) => {
// Setup: Create a path to a directory that doesn't exist
const nonExistentDir = `${context.tempDir}/does-not-exist`;
// Attempt to run cleanup on non-existent directory
// This should throw an error
try {
await cleanupLogs(nonExistentDir, 30);
throw new Error("Expected cleanupLogs to throw an error, but it didn't");
} catch (error) {
// Assert that we got a meaningful error about failing to read directory
const errorMessage = error instanceof Error ? error.message : String(error);
if (!errorMessage.includes("Failed to read logs directory")) {
throw new Error(`Expected error message to include "Failed to read logs directory", but got: ${errorMessage}`);
}
}
});
}
/**
* Test 7: File exactly on retention boundary
*/
testRetentionBoundary(): void {
this.test("file exactly on retention boundary", async (context) => {
// Setup: Create a file exactly 30 days old
const today = new Date();
const boundaryDate = new Date(today);
boundaryDate.setDate(boundaryDate.getDate() - 30); // Exactly 30 days ago
const boundaryFile = `${context.tempDir}/${boundaryDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(
boundaryFile,
JSON.stringify({ test: "boundary log" }) + "\n",
);
// Assert file exists before cleanup
await this.assertFileExists(boundaryFile);
// Run cleanup with 30 days retention
const result = await cleanupLogs(context.tempDir, 30);
// Assert no files were deleted (boundary file should be kept)
assertEquals(result.deletedCount, 0);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
// Assert boundary file still exists (30 days old is within retention)
await this.assertFileExists(boundaryFile);
});
}
/**
* Test 8: Multiple old files - deletes all
*/
testDeletesMultipleOldFiles(): void {
this.test("multiple old files - deletes all", async (context) => {
// Setup: Create multiple old log files
const today = new Date();
const oldDays = [35, 40, 45, 50]; // All older than 30 day retention
const oldFiles: string[] = [];
for (const days of oldDays) {
const oldDate = new Date(today);
oldDate.setDate(oldDate.getDate() - days);
const oldFile = `${context.tempDir}/${oldDate.toISOString().split("T")[0]}.log`;
await Deno.writeTextFile(
oldFile,
JSON.stringify({ test: `log from ${days} days ago` }) + "\n",
);
oldFiles.push(oldFile);
}
// Assert all old files exist before cleanup
for (const file of oldFiles) {
await this.assertFileExists(file);
}
// Run cleanup with 30 days retention
const result = await cleanupLogs(context.tempDir, 30);
// Assert all 4 files were deleted, no errors
assertEquals(result.deletedCount, 4);
assertEquals(result.errorCount, 0);
assertEquals(result.errors.length, 0);
// Assert all old files were deleted
for (const file of oldFiles) {
await this.assertFileNotExists(file);
}
});
}
runTests(): void {
this.testDeletesOldFiles();
this.testKeepsRecentFiles();
this.testParsesDateFormat();
this.testIgnoresInvalidPatterns();
this.testHandlesEmptyDirectory();
this.testHandlesNonExistentDirectory();
this.testRetentionBoundary();
this.testDeletesMultipleOldFiles();
}
}
// Run tests
const test = new CleanupLogsTest();
await test.runTests();