mod common;
use common::{TestDir, TestGitRepo, run_pg_ephemeral};
#[tokio::test]
async fn test_populate_cache() {
let backend = ociman::test_backend_setup!();
let instance_name: pg_ephemeral::InstanceName = "populate-cache-test".parse().unwrap();
let name: ociman::reference::Name = format!("localhost/pg-ephemeral/{instance_name}")
.parse()
.unwrap();
for reference in backend.image_references_by_name(&name).await {
backend.remove_image_force(&reference).await;
}
let definition = pg_ephemeral::Definition::new(backend.clone(), pg_ephemeral::Image::default(), instance_name.clone())
.wait_available_timeout(std::time::Duration::from_secs(30))
.apply_script(
"schema-and-data".parse().unwrap(),
r##"psql -c "CREATE TABLE test_cache (id INTEGER PRIMARY KEY); INSERT INTO test_cache VALUES (42);""##,
)
.unwrap();
let loaded_seeds = definition.load_seeds(&instance_name).await.unwrap();
for seed in loaded_seeds.iter_seeds() {
assert!(!seed.cache_status().is_hit());
}
definition.populate_cache(&instance_name).await.unwrap();
let loaded_seeds = definition.load_seeds(&instance_name).await.unwrap();
for seed in loaded_seeds.iter_seeds() {
assert!(seed.cache_status().is_hit());
}
definition
.with_container(async |container| {
container
.with_connection(async |connection| {
let row: (i32,) = sqlx::query_as("SELECT id FROM test_cache")
.fetch_one(&mut *connection)
.await
.unwrap();
assert_eq!(row.0, 42);
})
.await;
})
.await
.unwrap();
for reference in backend.image_references_by_name(&name).await {
backend.remove_image_force(&reference).await;
}
}
#[tokio::test]
async fn test_cache_status() {
let _backend = ociman::test_backend_setup!();
let repo = TestGitRepo::new("cache-test").await;
repo.write_file("schema.sql", "CREATE TABLE users (id INTEGER PRIMARY KEY);");
repo.write_file("data.sql", "INSERT INTO users (id) VALUES (1);");
let commit_hash = repo.commit("Initial").await;
let config_content = indoc::formatdoc! {r#"
image = "17.1"
[instances.main.seeds.a-schema]
type = "sql-file"
path = "schema.sql"
[instances.main.seeds.b-data-from-git]
type = "sql-file"
path = "data.sql"
git_revision = "{commit_hash}"
[instances.main.seeds.c-run-command]
type = "command"
command = "echo"
arguments = ["hello"]
cache.type = "command-hash"
[instances.main.seeds.d-run-script]
type = "script"
script = "echo 'hello world'"
"#};
repo.write_file("database.toml", &config_content);
let expected = indoc::indoc! {r#"
{
"instance": "main",
"image": "17.1",
"version": "0.1.3",
"seeds": [
{
"name": "a-schema",
"type": "sql-file",
"status": "miss",
"reference": "pg-ephemeral/main:57941e80df3c4ad587ecac9aa120a2d8a0b9656a570aa759fe8eec11f0d5c83e"
},
{
"name": "b-data-from-git",
"type": "sql-file-git-revision",
"status": "miss",
"reference": "pg-ephemeral/main:b6a86dc33b32e763a7d7cb8ffee589cac014f54e3613f2c4819a7fb908f352dc"
},
{
"name": "c-run-command",
"type": "command",
"status": "miss",
"reference": "pg-ephemeral/main:9a9df3cc4ab171a5fbbb81851ebad22e5e13ac9e79615c78ffd08833ea857f29"
},
{
"name": "d-run-script",
"type": "script",
"status": "miss",
"reference": "pg-ephemeral/main:e8be9aa953b57b554e85af07c661a42a106687aa431ab6a959aaa5d42bb52f0e"
}
]
}
"#};
let stdout = run_pg_ephemeral(&["cache", "status", "--json"], &repo.path).await;
assert_eq!(stdout, expected);
}
#[tokio::test]
async fn test_cache_status_deterministic() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-deterministic-test");
dir.write_file("schema.sql", "CREATE TABLE users (id INTEGER PRIMARY KEY);");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let expected = indoc::indoc! {r#"
{
"instance": "main",
"image": "17.1",
"version": "0.1.3",
"seeds": [
{
"name": "schema",
"type": "sql-file",
"status": "miss",
"reference": "pg-ephemeral/main:57941e80df3c4ad587ecac9aa120a2d8a0b9656a570aa759fe8eec11f0d5c83e"
}
]
}
"#};
let stdout = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_eq!(stdout, expected);
}
#[tokio::test]
async fn test_cache_status_change_with_content() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-changes-test");
dir.write_file("schema.sql", "CREATE TABLE users (id INTEGER PRIMARY KEY);");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let stdout1 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
dir.write_file(
"schema.sql",
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);",
);
let stdout2 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_ne!(stdout2, stdout1);
}
#[tokio::test]
async fn test_cache_status_change_with_image() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-image-test");
dir.write_file("schema.sql", "CREATE TABLE users (id INTEGER PRIMARY KEY);");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let stdout1 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.2"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let stdout2 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_ne!(stdout2, stdout1);
}
#[tokio::test]
async fn test_cache_status_chain_propagates() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-chain-test");
dir.write_file("first.sql", "CREATE TABLE first (id INTEGER);");
dir.write_file("second.sql", "CREATE TABLE second (id INTEGER);");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.a-first]
type = "sql-file"
path = "first.sql"
[instances.main.seeds.b-second]
type = "sql-file"
path = "second.sql"
"#},
);
let expected_before = indoc::indoc! {r#"
{
"instance": "main",
"image": "17.1",
"version": "0.1.3",
"seeds": [
{
"name": "a-first",
"type": "sql-file",
"status": "miss",
"reference": "pg-ephemeral/main:6164076d610c538efb15112f21074a214f123d2479f0772d12dfd15bf6ce0d72"
},
{
"name": "b-second",
"type": "sql-file",
"status": "miss",
"reference": "pg-ephemeral/main:40087fb6c1e02d31b4b83cdcaff4c77e0b5b4df1ed6152606a1b80e4c0100de4"
}
]
}
"#};
let stdout1 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_eq!(stdout1, expected_before);
dir.write_file("first.sql", "CREATE TABLE first (id INTEGER, name TEXT);");
let stdout2 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_ne!(stdout2, expected_before);
}
#[tokio::test]
async fn test_cache_status_key_command() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-key-command-test");
dir.write_file("version.txt", "1.0.0");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.run-migrations]
type = "command"
command = "migrate"
arguments = ["up"]
[instances.main.seeds.run-migrations.cache]
type = "key-command"
command = "cat"
arguments = ["version.txt"]
"#},
);
let expected_before = indoc::indoc! {r#"
{
"instance": "main",
"image": "17.1",
"version": "0.1.3",
"seeds": [
{
"name": "run-migrations",
"type": "command",
"status": "miss",
"reference": "pg-ephemeral/main:533867333ac2bd6b902f7cfd8f374bf7e6ef1ddd63c0f1ad0e561e41532efec5"
}
]
}
"#};
let stdout1 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_eq!(stdout1, expected_before);
dir.write_file("version.txt", "2.0.0");
let stdout2 = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_ne!(stdout2, expected_before);
}
#[tokio::test]
async fn test_cache_status_change_with_ssl() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-ssl-test");
dir.write_file("schema.sql", "CREATE TABLE users (id INTEGER PRIMARY KEY);");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let output_no_ssl = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[ssl_config]
hostname = "localhost"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let output_with_ssl = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_ne!(output_no_ssl, output_with_ssl);
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[ssl_config]
hostname = "example.com"
[instances.main.seeds.schema]
type = "sql-file"
path = "schema.sql"
"#},
);
let output_different_ssl = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
assert_ne!(output_with_ssl, output_different_ssl);
}
#[tokio::test]
async fn test_cache_status_container_script() {
let _backend = ociman::test_backend_setup!();
let dir = TestDir::new("cache-container-script-test");
dir.write_file(
"database.toml",
indoc::indoc! {r#"
image = "17.1"
[instances.main.seeds.install-ext]
type = "container-script"
script = "touch /container-script-marker"
"#},
);
let stdout = run_pg_ephemeral(&["cache", "status", "--json"], &dir.path).await;
let output: serde_json::Value = serde_json::from_str(&stdout).unwrap();
assert_eq!(output["seeds"][0]["name"], "install-ext");
assert_eq!(output["seeds"][0]["type"], "container-script");
assert_eq!(output["seeds"][0]["status"], "miss");
assert!(output["seeds"][0]["reference"].is_string());
}
#[tokio::test]
async fn test_populate_cache_container_script() {
let backend = ociman::test_backend_setup!();
let instance_name: pg_ephemeral::InstanceName =
"populate-cache-container-script-test".parse().unwrap();
let name: ociman::reference::Name = format!("localhost/pg-ephemeral/{instance_name}")
.parse()
.unwrap();
for reference in backend.image_references_by_name(&name).await {
backend.remove_image_force(&reference).await;
}
let definition = pg_ephemeral::Definition::new(
backend.clone(),
pg_ephemeral::Image::default(),
instance_name.clone(),
)
.wait_available_timeout(std::time::Duration::from_secs(30))
.apply_container_script(
"create-marker".parse().unwrap(),
"touch /container-script-marker",
)
.unwrap();
let loaded_seeds = definition.load_seeds(&instance_name).await.unwrap();
for seed in loaded_seeds.iter_seeds() {
assert!(!seed.cache_status().is_hit());
}
definition.populate_cache(&instance_name).await.unwrap();
let loaded_seeds = definition.load_seeds(&instance_name).await.unwrap();
for seed in loaded_seeds.iter_seeds() {
assert!(seed.cache_status().is_hit());
}
definition
.with_container(async |container| {
container
.with_connection(async |connection| {
let row: (bool,) = sqlx::query_as("SELECT true")
.fetch_one(&mut *connection)
.await
.unwrap();
assert!(row.0);
})
.await;
})
.await
.unwrap();
for reference in backend.image_references_by_name(&name).await {
backend.remove_image_force(&reference).await;
}
}
#[tokio::test]
async fn test_container_script_with_pg_cron() {
let backend = ociman::test_backend_setup!();
let instance_name: pg_ephemeral::InstanceName =
"container-script-pg-cron-test".parse().unwrap();
let name: ociman::reference::Name = format!("localhost/pg-ephemeral/{instance_name}")
.parse()
.unwrap();
for reference in backend.image_references_by_name(&name).await {
backend.remove_image_force(&reference).await;
}
let definition = pg_ephemeral::Definition::new(
backend.clone(),
"17".parse().unwrap(),
instance_name.clone(),
)
.wait_available_timeout(std::time::Duration::from_secs(30))
.apply_container_script(
"install-pg-cron".parse().unwrap(),
"apt-get update && apt-get install -y --no-install-recommends postgresql-17-cron \
&& printf '#!/bin/bash\\necho \"shared_preload_libraries = '\"'\"'pg_cron'\"'\"'\" >> \"$PGDATA/postgresql.conf\"\\n' \
> /docker-entrypoint-initdb.d/pg-cron.sh \
&& chmod +x /docker-entrypoint-initdb.d/pg-cron.sh",
)
.unwrap()
.apply_script(
"enable-pg-cron".parse().unwrap(),
r#"psql -c "CREATE EXTENSION pg_cron""#,
)
.unwrap();
definition
.with_container(async |container| {
container
.with_connection(async |connection| {
let row: (String,) = sqlx::query_as(
"SELECT extname::text FROM pg_extension WHERE extname = 'pg_cron'",
)
.fetch_one(&mut *connection)
.await
.unwrap();
assert_eq!(row.0, "pg_cron");
})
.await;
})
.await
.unwrap();
for reference in backend.image_references_by_name(&name).await {
backend.remove_image_force(&reference).await;
}
}