mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-22 23:25:22 +01:00
Fixed error with wrong placeholder (#1069)
* Fixed error with wrong placeholder Add concept of schema, so each test is isolated
This commit is contained in:
@@ -229,6 +229,7 @@ macro_rules! mint_db_test {
|
|||||||
add_mint_quote_only_once,
|
add_mint_quote_only_once,
|
||||||
register_payments,
|
register_payments,
|
||||||
read_mint_from_db_and_tx,
|
read_mint_from_db_and_tx,
|
||||||
|
get_proofs_by_keyset_id,
|
||||||
reject_duplicate_payments_same_tx,
|
reject_duplicate_payments_same_tx,
|
||||||
reject_duplicate_payments_diff_tx,
|
reject_duplicate_payments_diff_tx,
|
||||||
reject_over_issue_same_tx,
|
reject_over_issue_same_tx,
|
||||||
@@ -241,7 +242,12 @@ macro_rules! mint_db_test {
|
|||||||
$(
|
$(
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn $name() {
|
async fn $name() {
|
||||||
cdk_common::database::mint::test::$name($make_db_fn().await).await;
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
let now = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards");
|
||||||
|
|
||||||
|
cdk_common::database::mint::test::$name($make_db_fn(format!("test_{}_{}", now.as_nanos(), stringify!($name))).await).await;
|
||||||
}
|
}
|
||||||
)+
|
)+
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,61 @@
|
|||||||
//! Proofs tests
|
//! Proofs tests
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use cashu::secret::Secret;
|
use cashu::secret::Secret;
|
||||||
use cashu::{Amount, SecretKey};
|
use cashu::{Amount, Id, SecretKey};
|
||||||
|
|
||||||
use crate::database::mint::test::setup_keyset;
|
use crate::database::mint::test::setup_keyset;
|
||||||
use crate::database::mint::{Database, Error, KeysDatabase, Proof, QuoteId};
|
use crate::database::mint::{Database, Error, KeysDatabase, Proof, QuoteId};
|
||||||
|
|
||||||
|
/// Test get proofs by keyset id
|
||||||
|
pub async fn get_proofs_by_keyset_id<DB>(db: DB)
|
||||||
|
where
|
||||||
|
DB: Database<Error> + KeysDatabase<Err = Error>,
|
||||||
|
{
|
||||||
|
let keyset_id = setup_keyset(&db).await;
|
||||||
|
let quote_id = QuoteId::new_uuid();
|
||||||
|
let proofs = vec![
|
||||||
|
Proof {
|
||||||
|
amount: Amount::from(100),
|
||||||
|
keyset_id,
|
||||||
|
secret: Secret::generate(),
|
||||||
|
c: SecretKey::generate().public_key(),
|
||||||
|
witness: None,
|
||||||
|
dleq: None,
|
||||||
|
},
|
||||||
|
Proof {
|
||||||
|
amount: Amount::from(200),
|
||||||
|
keyset_id,
|
||||||
|
secret: Secret::generate(),
|
||||||
|
c: SecretKey::generate().public_key(),
|
||||||
|
witness: None,
|
||||||
|
dleq: None,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add proofs to database
|
||||||
|
let mut tx = Database::begin_transaction(&db).await.unwrap();
|
||||||
|
tx.add_proofs(proofs, Some(quote_id)).await.unwrap();
|
||||||
|
assert!(tx.commit().await.is_ok());
|
||||||
|
|
||||||
|
let (proofs, states) = db.get_proofs_by_keyset_id(&keyset_id).await.unwrap();
|
||||||
|
assert_eq!(proofs.len(), 2);
|
||||||
|
assert_eq!(proofs.len(), states.len());
|
||||||
|
assert_eq!(
|
||||||
|
states
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.map(|x| x.to_string()).unwrap_or_default())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
vec!["UNSPENT".to_owned(), "UNSPENT".to_owned()]
|
||||||
|
);
|
||||||
|
|
||||||
|
let keyset_id = Id::from_str("00916bbf7ef91a34").unwrap();
|
||||||
|
let (proofs, states) = db.get_proofs_by_keyset_id(&keyset_id).await.unwrap();
|
||||||
|
assert_eq!(proofs.len(), 0);
|
||||||
|
assert_eq!(proofs.len(), states.len());
|
||||||
|
}
|
||||||
|
|
||||||
/// Test the basic storing and retrieving proofs from the database. Probably the database would use
|
/// Test the basic storing and retrieving proofs from the database. Probably the database would use
|
||||||
/// binary/`Vec<u8>` to store data, that's why this test would quickly identify issues before running
|
/// binary/`Vec<u8>` to store data, that's why this test would quickly identify issues before running
|
||||||
/// other tests
|
/// other tests
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ impl Debug for SslMode {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PgConfig {
|
pub struct PgConfig {
|
||||||
url: String,
|
url: String,
|
||||||
|
schema: Option<String>,
|
||||||
tls: SslMode,
|
tls: SslMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +68,29 @@ impl DatabaseConfig for PgConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PgConfig {
|
||||||
|
/// strip schema from the connection string
|
||||||
|
fn strip_schema(input: &str) -> (Option<String>, String) {
|
||||||
|
let mut schema: Option<String> = None;
|
||||||
|
|
||||||
|
// Split by whitespace
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
for token in input.split_whitespace() {
|
||||||
|
if let Some(rest) = token.strip_prefix("schema=") {
|
||||||
|
schema = Some(rest.to_string());
|
||||||
|
} else {
|
||||||
|
parts.push(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cleaned = parts.join(" ");
|
||||||
|
(schema, cleaned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for PgConfig {
|
impl From<&str> for PgConfig {
|
||||||
fn from(conn_str: &str) -> Self {
|
fn from(conn_str: &str) -> Self {
|
||||||
|
let (schema, conn_str) = Self::strip_schema(conn_str);
|
||||||
fn build_tls(accept_invalid_certs: bool, accept_invalid_hostnames: bool) -> SslMode {
|
fn build_tls(accept_invalid_certs: bool, accept_invalid_hostnames: bool) -> SslMode {
|
||||||
let mut builder = TlsConnector::builder();
|
let mut builder = TlsConnector::builder();
|
||||||
if accept_invalid_certs {
|
if accept_invalid_certs {
|
||||||
@@ -105,6 +127,7 @@ impl From<&str> for PgConfig {
|
|||||||
|
|
||||||
PgConfig {
|
PgConfig {
|
||||||
url: conn_str.to_owned(),
|
url: conn_str.to_owned(),
|
||||||
|
schema,
|
||||||
tls,
|
tls,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,6 +172,17 @@ impl PostgresConnection {
|
|||||||
let result_clone = result.clone();
|
let result_clone = result.clone();
|
||||||
let notify_clone = notify.clone();
|
let notify_clone = notify.clone();
|
||||||
|
|
||||||
|
async fn select_schema(conn: &Client, schema: &str) -> Result<(), Error> {
|
||||||
|
conn.batch_execute(&format!(
|
||||||
|
r#"
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "{schema}";
|
||||||
|
SET search_path TO "{schema}"
|
||||||
|
"#
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.map_err(|e| Error::Database(Box::new(e)))
|
||||||
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match config.tls {
|
match config.tls {
|
||||||
SslMode::NoTls(tls) => {
|
SslMode::NoTls(tls) => {
|
||||||
@@ -163,11 +197,21 @@ impl PostgresConnection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let still_valid_for_spawn = still_valid.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _ = connection.await;
|
let _ = connection.await;
|
||||||
still_valid.store(false, std::sync::atomic::Ordering::Release);
|
still_valid_for_spawn.store(false, std::sync::atomic::Ordering::Release);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(schema) = config.schema.as_ref() {
|
||||||
|
if let Err(err) = select_schema(&client, schema).await {
|
||||||
|
*error_clone.lock().await = Some(err);
|
||||||
|
still_valid.store(false, std::sync::atomic::Ordering::Release);
|
||||||
|
notify_clone.notify_waiters();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _ = result_clone.set(client);
|
let _ = result_clone.set(client);
|
||||||
notify_clone.notify_waiters();
|
notify_clone.notify_waiters();
|
||||||
}
|
}
|
||||||
@@ -183,11 +227,21 @@ impl PostgresConnection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let still_valid_for_spawn = still_valid.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _ = connection.await;
|
let _ = connection.await;
|
||||||
still_valid.store(false, std::sync::atomic::Ordering::Release);
|
still_valid_for_spawn.store(false, std::sync::atomic::Ordering::Release);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(schema) = config.schema.as_ref() {
|
||||||
|
if let Err(err) = select_schema(&client, schema).await {
|
||||||
|
*error_clone.lock().await = Some(err);
|
||||||
|
still_valid.store(false, std::sync::atomic::Ordering::Release);
|
||||||
|
notify_clone.notify_waiters();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let _ = result_clone.set(client);
|
let _ = result_clone.set(client);
|
||||||
notify_clone.notify_waiters();
|
notify_clone.notify_waiters();
|
||||||
}
|
}
|
||||||
@@ -275,22 +329,19 @@ pub type WalletPgDatabase = SQLWalletDatabase<PgConnectionPool>;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use cdk_common::mint_db_test;
|
use cdk_common::mint_db_test;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
static MIGRATION_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
async fn provide_db(test_id: String) -> MintPgDatabase {
|
||||||
|
|
||||||
async fn provide_db() -> MintPgDatabase {
|
|
||||||
let m = MIGRATION_LOCK.lock().await;
|
|
||||||
let db_url = std::env::var("CDK_MINTD_DATABASE_URL")
|
let db_url = std::env::var("CDK_MINTD_DATABASE_URL")
|
||||||
.or_else(|_| std::env::var("PG_DB_URL")) // Fallback for compatibility
|
.or_else(|_| std::env::var("PG_DB_URL")) // Fallback for compatibility
|
||||||
.unwrap_or("host=localhost user=test password=test dbname=testdb port=5433".to_owned());
|
.unwrap_or("host=localhost user=test password=test dbname=testdb port=5433".to_owned());
|
||||||
|
|
||||||
|
let db_url = format!("{db_url} schema={test_id}");
|
||||||
|
|
||||||
let db = MintPgDatabase::new(db_url.as_str())
|
let db = MintPgDatabase::new(db_url.as_str())
|
||||||
.await
|
.await
|
||||||
.expect("database");
|
.expect("database");
|
||||||
drop(m);
|
|
||||||
db
|
db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -923,7 +923,7 @@ VALUES (:quote_id, :amount, :timestamp);
|
|||||||
query(
|
query(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM melt_quote
|
DELETE FROM melt_quote
|
||||||
WHERE id=?
|
WHERE id=:id
|
||||||
"#,
|
"#,
|
||||||
)?
|
)?
|
||||||
.bind("id", quote_id.to_string())
|
.bind("id", quote_id.to_string())
|
||||||
@@ -1452,7 +1452,7 @@ where
|
|||||||
FROM
|
FROM
|
||||||
proof
|
proof
|
||||||
WHERE
|
WHERE
|
||||||
keyset_id=?
|
keyset_id=:keyset_id
|
||||||
"#,
|
"#,
|
||||||
)?
|
)?
|
||||||
.bind("keyset_id", keyset_id.to_string())
|
.bind("keyset_id", keyset_id.to_string())
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::Config;
|
use crate::common::Config;
|
||||||
|
|
||||||
async fn provide_db() -> MintSqliteDatabase {
|
async fn provide_db(_test_name: String) -> MintSqliteDatabase {
|
||||||
memory::empty().await.unwrap()
|
memory::empty().await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user