cruxmont_pg_test_macro/
lib.rs1extern crate proc_macro;
28use proc_macro::TokenStream;
29use proc_macro2::Span;
30use quote::quote;
31use syn::{ItemFn, LitStr, parse_macro_input};
32use uuid::Uuid;
33
34#[proc_macro_attribute]
35pub fn pg_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
36 let input_fn = parse_macro_input!(item as ItemFn);
37
38 let func_name = &input_fn.sig.ident;
40 let stmts = &input_fn.block.stmts; let db_tag = Uuid::new_v4().simple().to_string(); let env_name = format!("DATABASE_URL_{}", db_tag); let env_lit = LitStr::new(&env_name, Span::call_site()); let db_url = format!("postgres://username:password@localhost:5433/{db_tag}");
47 let db_name = LitStr::new(&db_tag, Span::call_site());
48 let db_url_lit = LitStr::new(&db_url, Span::call_site());
49 let pg_url_lit = LitStr::new(
50 "postgres://username:password@localhost:5433/main_db",
51 Span::call_site(),
52 );
53
54 let expanded = quote! {
55 #[test]
56 fn #func_name() {
57 use sqlx::Executor;
58 use tokio::runtime::Builder;
59
60 unsafe {
62 std::env::set_var(#env_lit, #db_url_lit);
63 std::env::set_var("DB_MAX_CONNECTIONS", "1");
64 }
65
66 let rt = Builder::new_current_thread()
67 .enable_all()
68 .build()
69 .expect("create Tokio runtime");
70
71 rt.block_on(async {
73 let db_name = #db_name;
74 let mut admin = sqlx::postgres::PgPoolOptions::new()
75 .max_connections(1)
76 .connect(#pg_url_lit)
77 .await.expect("make admin DB connection pool");
78 admin.execute(format!(r#"CREATE DATABASE "{db_name}""#).as_str()).await.expect("execute create DB transaction");
79 admin.close().await;
80 });
81
82 let test_result = rt.block_on(async {
83 cruxmont::pg_pool::define_pg_pool!(SQLX_POSTGRES_TEST_POOL, #env_lit, "DB_MAX_CONNECTIONS");
85
86 struct TestDbHandle;
87
88 impl cruxmont::dal::connections::sqlx_postgres::YieldPostGresPool for TestDbHandle {
89 fn yield_pool() -> &'static sqlx::Pool<sqlx::Postgres> {
90 &*SQLX_POSTGRES_TEST_POOL
91 }
92 }
93
94 let handle = tokio::spawn(async {
96 #(#stmts)*
97 });
98 handle.await });
100
101 rt.block_on(async {
103 let db_name = #db_name;
104 let mut admin = sqlx::postgres::PgPoolOptions::new()
105 .max_connections(1)
106 .connect(#pg_url_lit)
107 .await.expect("make admin DB connection pool");
108 admin.execute(format!(r#"DROP DATABASE "{db_name}" WITH (FORCE)"#).as_str()).await.expect("execute drop DB transaction");
109 admin.close().await;
110 });
111 test_result.unwrap();
112 }
113 };
114 TokenStream::from(expanded)
115}