@testcontainers/postgresql
Advanced tools
Comparing version
@@ -16,2 +16,3 @@ import { AbstractStartedContainer, GenericContainer, StartedTestContainer } from "testcontainers"; | ||
private readonly password; | ||
private snapshotName; | ||
constructor(startedTestContainer: StartedTestContainer, database: string, username: string, password: string); | ||
@@ -26,2 +27,39 @@ getPort(): number; | ||
getConnectionUri(): string; | ||
/** | ||
* Sets the name to be used for database snapshots. | ||
* This name will be used as the default for snapshot() and restore() methods. | ||
* | ||
* @param snapshotName The name to use for snapshots (default is "migrated_template" if this method is not called) | ||
* @returns this (for method chaining) | ||
*/ | ||
withSnapshotName(snapshotName: string): this; | ||
/** | ||
* Takes a snapshot of the current state of the database as a template, which can then be restored using | ||
* the restore method. | ||
* | ||
* @param snapshotName Name for the snapshot, defaults to the value set by withSnapshotName() or "migrated_template" if not specified | ||
* @returns Promise resolving when snapshot is complete | ||
* @throws Error if attempting to snapshot the postgres system database or if using the same name as the database | ||
*/ | ||
snapshot(snapshotName?: string): Promise<void>; | ||
/** | ||
* Restores the database to a specific snapshot. | ||
* | ||
* @param snapshotName Name of the snapshot to restore from, defaults to the value set by withSnapshotName() or "migrated_template" if not specified | ||
* @returns Promise resolving when restore is complete | ||
* @throws Error if attempting to restore the postgres system database or if using the same name as the database | ||
*/ | ||
restoreSnapshot(snapshotName?: string): Promise<void>; | ||
/** | ||
* Executes a series of SQL commands against the Postgres database | ||
* | ||
* @param commands Array of SQL commands to execute in sequence | ||
* @throws Error if any command fails to execute with details of the failure | ||
*/ | ||
private execCommandsSQL; | ||
/** | ||
* Checks if the snapshot name is valid and if the database is not the postgres system database | ||
* @param snapshotName The name of the snapshot to check | ||
*/ | ||
private snapshotSanityCheck; | ||
} |
@@ -50,2 +50,3 @@ "use strict"; | ||
password; | ||
snapshotName = "migrated_template"; | ||
constructor(startedTestContainer, database, username, password) { | ||
@@ -81,4 +82,96 @@ super(startedTestContainer); | ||
} | ||
/** | ||
* Sets the name to be used for database snapshots. | ||
* This name will be used as the default for snapshot() and restore() methods. | ||
* | ||
* @param snapshotName The name to use for snapshots (default is "migrated_template" if this method is not called) | ||
* @returns this (for method chaining) | ||
*/ | ||
withSnapshotName(snapshotName) { | ||
this.snapshotName = snapshotName; | ||
return this; | ||
} | ||
/** | ||
* Takes a snapshot of the current state of the database as a template, which can then be restored using | ||
* the restore method. | ||
* | ||
* @param snapshotName Name for the snapshot, defaults to the value set by withSnapshotName() or "migrated_template" if not specified | ||
* @returns Promise resolving when snapshot is complete | ||
* @throws Error if attempting to snapshot the postgres system database or if using the same name as the database | ||
*/ | ||
async snapshot(snapshotName = this.snapshotName) { | ||
this.snapshotSanityCheck(snapshotName); | ||
// Execute the commands to create the snapshot, in order | ||
await this.execCommandsSQL([ | ||
// Update pg_database to remove the template flag, then drop the database if it exists. | ||
// This is needed because dropping a template database will fail. | ||
`UPDATE pg_database SET datistemplate = FALSE WHERE datname = '${snapshotName}'`, | ||
`DROP DATABASE IF EXISTS "${snapshotName}"`, | ||
// Create a copy of the database to another database to use as a template now that it was fully migrated | ||
`CREATE DATABASE "${snapshotName}" WITH TEMPLATE "${this.getDatabase()}" OWNER "${this.getUsername()}"`, | ||
// Snapshot the template database so we can restore it onto our original database going forward | ||
`ALTER DATABASE "${snapshotName}" WITH is_template = TRUE`, | ||
]); | ||
} | ||
/** | ||
* Restores the database to a specific snapshot. | ||
* | ||
* @param snapshotName Name of the snapshot to restore from, defaults to the value set by withSnapshotName() or "migrated_template" if not specified | ||
* @returns Promise resolving when restore is complete | ||
* @throws Error if attempting to restore the postgres system database or if using the same name as the database | ||
*/ | ||
async restoreSnapshot(snapshotName = this.snapshotName) { | ||
this.snapshotSanityCheck(snapshotName); | ||
// Execute the commands to restore the snapshot, in order | ||
await this.execCommandsSQL([ | ||
// Drop the entire database by connecting to the postgres global database | ||
`DROP DATABASE "${this.getDatabase()}" WITH (FORCE)`, | ||
// Then restore the previous snapshot | ||
`CREATE DATABASE "${this.getDatabase()}" WITH TEMPLATE "${snapshotName}" OWNER "${this.getUsername()}"`, | ||
]); | ||
} | ||
/** | ||
* Executes a series of SQL commands against the Postgres database | ||
* | ||
* @param commands Array of SQL commands to execute in sequence | ||
* @throws Error if any command fails to execute with details of the failure | ||
*/ | ||
async execCommandsSQL(commands) { | ||
for (const command of commands) { | ||
try { | ||
const result = await this.exec([ | ||
"psql", | ||
"-v", | ||
"ON_ERROR_STOP=1", | ||
"-U", | ||
this.getUsername(), | ||
"-d", | ||
"postgres", | ||
"-c", | ||
command, | ||
]); | ||
if (result.exitCode !== 0) { | ||
throw new Error(`Command failed with exit code ${result.exitCode}: ${result.output}`); | ||
} | ||
} | ||
catch (error) { | ||
console.error(`Failed to execute command: ${command}`, error); | ||
throw error; | ||
} | ||
} | ||
} | ||
/** | ||
* Checks if the snapshot name is valid and if the database is not the postgres system database | ||
* @param snapshotName The name of the snapshot to check | ||
*/ | ||
snapshotSanityCheck(snapshotName) { | ||
if (this.getDatabase() === "postgres") { | ||
throw new Error("Snapshot feature is not supported when using the postgres system database"); | ||
} | ||
if (this.getDatabase() === snapshotName) { | ||
throw new Error("Snapshot name cannot be the same as the database name"); | ||
} | ||
} | ||
} | ||
exports.StartedPostgreSqlContainer = StartedPostgreSqlContainer; | ||
//# sourceMappingURL=postgresql-container.js.map |
{ | ||
"name": "@testcontainers/postgresql", | ||
"version": "10.22.0", | ||
"version": "10.23.0", | ||
"license": "MIT", | ||
@@ -37,4 +37,4 @@ "keywords": [ | ||
"dependencies": { | ||
"testcontainers": "^10.22.0" | ||
"testcontainers": "^10.23.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
18026
78.07%244
115.93%Updated