Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/modules/postgresql.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ tests very modular, since they always run on a brand-new database.
not work if the database for the container is set to `"postgres"`.

<!--codeinclude-->
[Test with a reusable Postgres container](../../packages/modules/postgresql/src/postgresql-container.test.ts) inside_block:createAndRestoreFromSnapshot
[Test with a reusable Postgres container](../../packages/modules/postgresql/src/postgresql-container-snapshot.test.ts) inside_block:createAndRestoreFromSnapshot
<!--/codeinclude-->
19 changes: 0 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"scripts": {
"pre-commit": "lint-staged",
"docs:serve": "docker-compose up",
"test": "cross-env NODE_ENV=test DEBUG=testcontainers* vitest run",
"test": "vitest run",
"test:ci": "npm run test -- --coverage",
"format": "prettier --write package.json \"packages/**/*.ts\"",
"lint": "eslint --fix package.json \"packages/**/*.ts\"",
Expand All @@ -21,7 +21,6 @@
"@eslint/js": "^9.22.0",
"@eslint/json": "^0.11.0",
"@vitest/coverage-v8": "^3.1.1",
"cross-env": "^7.0.3",
"eslint": "^9.22.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-prettier": "^5.2.3",
Expand Down
45 changes: 45 additions & 0 deletions packages/modules/postgresql/src/pgvector-container.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Client } from "pg";
import { PostgreSqlContainer } from "./postgresql-container";

const IMAGE = "pgvector/pgvector:pg16";

describe("PgvectorContainer", { timeout: 180_000 }, () => {
it("should work", async () => {
const container = await new PostgreSqlContainer(IMAGE).start();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
password: container.getPassword(),
});
await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": 1 });

await client.end();
await container.stop();
});

it("should restart", async () => {
const container = await new PostgreSqlContainer(IMAGE).start();
await container.restart();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
password: container.getPassword(),
});
await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": 1 });

await client.end();
await container.stop();
});
});
45 changes: 45 additions & 0 deletions packages/modules/postgresql/src/postgis-container.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Client } from "pg";
import { PostgreSqlContainer } from "./postgresql-container";

const IMAGE = "postgis/postgis:16-3.4";

describe("PostgisContainer", { timeout: 180_000 }, () => {
it("should work", async () => {
const container = await new PostgreSqlContainer(IMAGE).start();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
password: container.getPassword(),
});
await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": 1 });

await client.end();
await container.stop();
});

it("should restart", async () => {
const container = await new PostgreSqlContainer(IMAGE).start();
await container.restart();

const client = new Client({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
password: container.getPassword(),
});
await client.connect();

const result = await client.query("SELECT 1");
expect(result.rows[0]).toEqual({ "?column?": 1 });

await client.end();
await container.stop();
});
});
208 changes: 208 additions & 0 deletions packages/modules/postgresql/src/postgresql-container-snapshot.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { Client } from "pg";
import { PostgreSqlContainer } from "./postgresql-container";

describe("PostgreSqlContainer snapshot and restore", { timeout: 180_000 }, () => {
// createAndRestoreFromSnapshot {
it("should create and restore from snapshot", async () => {
const container = await new PostgreSqlContainer().start();

// Connect to the database
let client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Create some test data
await client.query("CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT)");
await client.query("INSERT INTO test_table (name) VALUES ('initial data')");

// Close connection before snapshot (otherwise we'll get an error because user is already connected)
await client.end();

// Take a snapshot
await container.snapshot();

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Modify the database
await client.query("INSERT INTO test_table (name) VALUES ('data after snapshot')");

// Verify both records exist
let result = await client.query("SELECT * FROM test_table ORDER BY id");
expect(result.rows).toHaveLength(2);
expect(result.rows[0].name).toEqual("initial data");
expect(result.rows[1].name).toEqual("data after snapshot");

// Close connection before restore (same reason as above)
await client.end();

// Restore to the snapshot
await container.restoreSnapshot();

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Verify only the initial data exists after restore
result = await client.query("SELECT * FROM test_table ORDER BY id");
expect(result.rows).toHaveLength(1);
expect(result.rows[0].name).toEqual("initial data");

await client.end();
await container.stop();
});
// }

it("should use custom snapshot name", async () => {
const container = await new PostgreSqlContainer().start();
const customSnapshotName = "my_custom_snapshot";

// Connect to the database
let client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Create a test table and insert data
await client.query("CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT)");
await client.query("INSERT INTO test_table (name) VALUES ('initial data')");

// Close connection before snapshot
await client.end();

// Take a snapshot with custom name
await container.snapshot(customSnapshotName);

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Modify the database
await client.query("INSERT INTO test_table (name) VALUES ('data after snapshot')");

// Close connection before restore
await client.end();

// Restore using the custom snapshot name
await container.restoreSnapshot(customSnapshotName);

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Verify only the initial data exists after restore
const result = await client.query("SELECT * FROM test_table ORDER BY id");
expect(result.rows).toHaveLength(1);
expect(result.rows[0].name).toEqual("initial data");

await client.end();
await container.stop();
});

it("should handle multiple snapshots", async () => {
const container = await new PostgreSqlContainer().start();

// Connect to the database
let client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Create a test table
await client.query("CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT)");

// Close connection before snapshot
await client.end();

// Take first snapshot with empty table
await container.snapshot("snapshot1");

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Add first record
await client.query("INSERT INTO test_table (name) VALUES ('data for snapshot 2')");

// Close connection before snapshot
await client.end();

// Take second snapshot with one record
await container.snapshot("snapshot2");

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Add second record
await client.query("INSERT INTO test_table (name) VALUES ('data after snapshots')");

// Verify we have two records
let result = await client.query("SELECT COUNT(*) as count FROM test_table");
expect(result.rows[0].count).toEqual("2");

// Close connection before restore
await client.end();

// Restore to first snapshot (empty table)
await container.restoreSnapshot("snapshot1");

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Verify table is empty
result = await client.query("SELECT COUNT(*) as count FROM test_table");
expect(result.rows[0].count).toEqual("0");

// Close connection before restore
await client.end();

// Restore to second snapshot (one record)
await container.restoreSnapshot("snapshot2");

// Reconnect to database
client = new Client({
connectionString: container.getConnectionUri(),
});
await client.connect();

// Verify we have one record
result = await client.query("SELECT * FROM test_table");
expect(result.rows).toHaveLength(1);
expect(result.rows[0].name).toEqual("data for snapshot 2");

await client.end();
await container.stop();
});

it("should throw an error when trying to snapshot postgres system database", async () => {
const container = await new PostgreSqlContainer().withDatabase("postgres").start();

await expect(container.snapshot()).rejects.toThrow(
"Snapshot feature is not supported when using the postgres system database"
);

await expect(container.restoreSnapshot()).rejects.toThrow(
"Snapshot feature is not supported when using the postgres system database"
);

await container.stop();
});
});
Loading