| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
#![cfg(feature = "postgres")] |
| 6 |
|
| 7 |
use oxygenhub::config::Config; |
| 8 |
use std::collections::{BTreeMap, BTreeSet}; |
| 9 |
|
| 10 |
type Tables = BTreeMap<String, BTreeSet<String>>; |
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
fn compared(table: &str) -> bool { |
| 15 |
!table.starts_with("search") && !table.starts_with("_sqlx") |
| 16 |
} |
| 17 |
|
| 18 |
async fn sqlite() -> Tables { |
| 19 |
use sqlx::Connection; |
| 20 |
let mut conn = sqlx::SqliteConnection::connect("sqlite::memory:").await.unwrap(); |
| 21 |
sqlx::migrate!("./migrations").run(&mut conn).await.unwrap(); |
| 22 |
let names: Vec<String> = |
| 23 |
sqlx::query_scalar("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").fetch_all(&mut conn).await.unwrap(); |
| 24 |
let mut out = Tables::new(); |
| 25 |
for t in names.into_iter().filter(|t| compared(t)) { |
| 26 |
let cols: Vec<String> = |
| 27 |
sqlx::query_scalar(&format!("SELECT name FROM pragma_table_info('{t}')")).fetch_all(&mut conn).await.unwrap(); |
| 28 |
out.insert(t, cols.into_iter().collect()); |
| 29 |
} |
| 30 |
out |
| 31 |
} |
| 32 |
|
| 33 |
async fn postgres() -> Tables { |
| 34 |
let dir = tempfile::tempdir().unwrap(); |
| 35 |
let config = Config::load(dir.path()).unwrap(); |
| 36 |
let pool = oxygenhub::db::open(&config).await.unwrap(); |
| 37 |
let rows: Vec<(String, String)> = sqlx::query_as( |
| 38 |
"SELECT CAST(t.relname AS TEXT), CAST(a.attname AS TEXT) FROM pg_attribute a JOIN pg_class t ON t.oid = a.attrelid \ |
| 39 |
WHERE t.relkind = 'r' AND t.relnamespace = CAST(current_schema() AS regnamespace) AND a.attnum > 0 AND NOT a.attisdropped", |
| 40 |
) |
| 41 |
.fetch_all(&pool) |
| 42 |
.await |
| 43 |
.unwrap(); |
| 44 |
let mut out = Tables::new(); |
| 45 |
for (t, c) in rows.into_iter().filter(|(t, _)| compared(t)) { |
| 46 |
out.entry(t).or_default().insert(c); |
| 47 |
} |
| 48 |
out |
| 49 |
} |
| 50 |
|
| 51 |
#[tokio::test] |
| 52 |
async fn the_postgres_schema_has_every_table_and_column_sqlite_has() { |
| 53 |
let (lite, pg) = (sqlite().await, postgres().await); |
| 54 |
let only = |a: &Tables, b: &Tables| -> Vec<String> { |
| 55 |
a.iter() |
| 56 |
.flat_map(|(t, cols)| match b.get(t) { |
| 57 |
None => vec![format!("table {t}")], |
| 58 |
Some(other) => cols.difference(other).map(|c| format!("{t}.{c}")).collect(), |
| 59 |
}) |
| 60 |
.collect() |
| 61 |
}; |
| 62 |
assert_eq!(only(&lite, &pg), Vec::<String>::new(), "in SQLite's migrations but not migrations-pg"); |
| 63 |
assert_eq!(only(&pg, &lite), Vec::<String>::new(), "in migrations-pg but not SQLite's migrations"); |
| 64 |
} |