mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-23 10:55:54 +02:00
fix(sqlite): tag per-migration database backups with version and prevent collisions
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { formatBackupTimestamp } from "./backupFileName";
|
||||
import { formatBackupFileName, formatBackupTimestamp } from "./backupFileName";
|
||||
import { assertEquals } from "@test/assert";
|
||||
|
||||
// Local-time constructors are used throughout, matching formatBackupTimestamp,
|
||||
@@ -29,7 +29,9 @@ function testMonthIsOneIndexed() {
|
||||
}
|
||||
|
||||
{
|
||||
const result = formatBackupTimestamp(new Date(2026, 11, 31, 23, 59, 59));
|
||||
const result = formatBackupTimestamp(
|
||||
new Date(2026, 11, 31, 23, 59, 59)
|
||||
);
|
||||
assertEquals(
|
||||
result,
|
||||
"2026-12-31_23-59-59",
|
||||
@@ -73,9 +75,7 @@ function testNamesSortChronologically() {
|
||||
new Date(2026, 11, 31, 23, 59, 59)
|
||||
];
|
||||
|
||||
const sorted = taken
|
||||
.map((date) => formatBackupTimestamp(date))
|
||||
.sort();
|
||||
const sorted = taken.map((date) => formatBackupTimestamp(date)).sort();
|
||||
|
||||
assertEquals(
|
||||
sorted.join(","),
|
||||
@@ -89,11 +89,48 @@ function testNamesSortChronologically() {
|
||||
);
|
||||
}
|
||||
|
||||
function testFormatBackupFileName() {
|
||||
console.log("Running backup file name formatting tests...");
|
||||
|
||||
const date = new Date(2026, 8, 12, 20, 35, 56);
|
||||
|
||||
// With semver version string without leading 'v'
|
||||
assertEquals(
|
||||
formatBackupFileName("1.22.0", date),
|
||||
"db_2026-09-12_20-35-56_v1.22.0.sqlite",
|
||||
"Filename must include timestamp and prefixed version tag"
|
||||
);
|
||||
|
||||
// With version string already containing 'v'
|
||||
assertEquals(
|
||||
formatBackupFileName("v1.22.0", date),
|
||||
"db_2026-09-12_20-35-56_v1.22.0.sqlite",
|
||||
"Filename must not duplicate 'v' prefix if already present"
|
||||
);
|
||||
|
||||
// Without version (fallback/default)
|
||||
assertEquals(
|
||||
formatBackupFileName(undefined, date),
|
||||
"db_2026-09-12_20-35-56.sqlite",
|
||||
"Filename without version must match default timestamped format"
|
||||
);
|
||||
|
||||
// Distinct versions within the exact same second do not collide
|
||||
const sameSecondFile1 = formatBackupFileName("1.21.0", date);
|
||||
const sameSecondFile2 = formatBackupFileName("1.22.0", date);
|
||||
if (sameSecondFile1 === sameSecondFile2) {
|
||||
throw new Error(
|
||||
"Backup file names for different versions in the same second must not collide"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Run all tests
|
||||
try {
|
||||
testMonthIsOneIndexed();
|
||||
testEveryFieldIsZeroPadded();
|
||||
testNamesSortChronologically();
|
||||
testFormatBackupFileName();
|
||||
console.log("All tests passed successfully!");
|
||||
} catch (error) {
|
||||
console.error("Test failed:", error);
|
||||
|
||||
@@ -26,3 +26,26 @@ export function formatBackupTimestamp(date: Date = new Date()): string {
|
||||
|
||||
return `${datePart}_${timePart}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the full database backup file name, including timestamp and optional version tag.
|
||||
*
|
||||
* When a migration version is provided, the filename includes `_v<version>`,
|
||||
* preventing collisions between multiple migrations running in the same second and making it easy
|
||||
* to identify the migration state contained in the backup.
|
||||
*
|
||||
* @param version Optional migration version being run.
|
||||
* @param date The moment the backup is being taken. Defaults to now.
|
||||
* @returns A filename of the form `db_YYYY-MM-DD_HH-MM-SS_v<version>.sqlite` or `db_YYYY-MM-DD_HH-MM-SS.sqlite`.
|
||||
*/
|
||||
export function formatBackupFileName(
|
||||
version?: string,
|
||||
date: Date = new Date()
|
||||
): string {
|
||||
const timestamp = formatBackupTimestamp(date);
|
||||
if (version) {
|
||||
const versionTag = version.startsWith("v") ? version : `v${version}`;
|
||||
return `db_${timestamp}_${versionTag}.sqlite`;
|
||||
}
|
||||
return `db_${timestamp}.sqlite`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user