fix: resolve PostgreSQL type mismatches and add willexecutor debug logging
- Fix get_next_address_index: use try_get::<i32> for PG SERIAL/INTEGER columns - Fix search_tx: use try_get::<i32> for PG status column - Fix execute_insert: parse locktime (String→i64) and in_vout (String→i32) before binding to PG INTEGER columns - Fix execute_insert: bind tbl_out vout as i32, amount as String for PG - Fix save_new_address: cast xpub i64 to i32 for PG INTEGER column - Fix get_pending_txs: cast i64 bind params to i32, use try_get::<i32> for reads - Fix get_stats: use try_get::<i32> for all numeric PG INTEGER columns - Add trace logging in parse_request_transactions for xpub address matching - Add trace logging in get_all_addresses_by_xpub for query debugging
This commit is contained in:
@@ -1,15 +1,62 @@
|
||||
use bal_server::db::open_db;
|
||||
use sqlite::State;
|
||||
use std::fs;
|
||||
use bal_server::db::{DatabasePool, create_database, open_database};
|
||||
use sqlx::Row;
|
||||
|
||||
#[test]
|
||||
fn test_open_db_blocks_traversal() {
|
||||
let res = open_db("../etc/passwd");
|
||||
#[tokio::test]
|
||||
async fn test_open_database_sqlite() {
|
||||
let pool = open_database("sqlite", "sqlite::memory:").await;
|
||||
assert!(pool.is_ok(), "Opening SQLite in-memory should succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_open_database_invalid_backend() {
|
||||
let pool = open_database("oracle", "connection_string").await;
|
||||
assert!(pool.is_err(), "Unknown backend should fail");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_database_sqlite() {
|
||||
let pool = open_database("sqlite", "sqlite::memory:").await.unwrap();
|
||||
let result = create_database(&pool).await;
|
||||
assert!(result.is_ok(), "Creating SQLite schema should succeed");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_database_is_idempotent() {
|
||||
let pool = open_database("sqlite", "sqlite::memory:").await.unwrap();
|
||||
create_database(&pool).await.unwrap();
|
||||
let result = create_database(&pool).await;
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Creating schema twice should succeed (idempotent)"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sqlite_wal_mode() {
|
||||
let tmp_path = std::env::temp_dir().join("tmp_test_wal_mode.db");
|
||||
let path_str = tmp_path.to_str().unwrap();
|
||||
let _ = std::fs::remove_file(&tmp_path);
|
||||
let dsn = format!("sqlite:{}?mode=rwc", path_str);
|
||||
let pool = open_database("sqlite", &dsn).await.unwrap();
|
||||
if let DatabasePool::SQLite(p) = &pool {
|
||||
let row = sqlx::query("PRAGMA journal_mode")
|
||||
.fetch_one(p)
|
||||
.await
|
||||
.unwrap();
|
||||
let mode: String = row.try_get(0).unwrap();
|
||||
assert_eq!(mode, "wal", "SQLite journal mode should be WAL");
|
||||
}
|
||||
drop(pool);
|
||||
let _ = std::fs::remove_file(&tmp_path);
|
||||
let _ = std::fs::remove_file(format!("{}-shm", path_str));
|
||||
let _ = std::fs::remove_file(format!("{}-wal", path_str));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_open_database_blocks_traversal() {
|
||||
let res = open_database("sqlite", "sqlite:../etc/passwd").await;
|
||||
assert!(res.is_err(), "Path with '..' should be rejected");
|
||||
let err = match res {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("Expected error for traversal path"),
|
||||
};
|
||||
let err = res.err().unwrap();
|
||||
assert!(
|
||||
err.contains("'..'"),
|
||||
"Error should mention directory traversal: {}",
|
||||
@@ -17,15 +64,17 @@ fn test_open_db_blocks_traversal() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_open_db_blocks_forbidden_absolute() {
|
||||
for path in ["/etc/passwd", "/proc/self/mem", "/dev/null", "/usr/bin/ls"] {
|
||||
let res = open_db(path);
|
||||
#[tokio::test]
|
||||
async fn test_open_database_blocks_forbidden_absolute() {
|
||||
for path in [
|
||||
"sqlite:/etc/passwd",
|
||||
"sqlite:/proc/self/mem",
|
||||
"sqlite:/dev/null",
|
||||
"sqlite:/usr/bin/ls",
|
||||
] {
|
||||
let res = open_database("sqlite", path).await;
|
||||
assert!(res.is_err(), "Absolute path {} should be rejected", path);
|
||||
let err = match res {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("Expected error for forbidden path {}", path),
|
||||
};
|
||||
let err = res.err().unwrap();
|
||||
assert!(
|
||||
err.contains("forbidden"),
|
||||
"Error should mention forbidden prefix: {}",
|
||||
@@ -34,59 +83,26 @@ fn test_open_db_blocks_forbidden_absolute() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_open_db_allows_relative() {
|
||||
let test_path = "tmp_test_bal.db";
|
||||
let _ = fs::remove_file(test_path);
|
||||
let res = open_db(test_path);
|
||||
assert!(res.is_ok(), "Valid relative path should be allowed");
|
||||
let db = res.unwrap();
|
||||
drop(db);
|
||||
let _ = fs::remove_file(test_path);
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn test_open_database_rejects_symlink() {
|
||||
let tmp_dir = std::env::temp_dir();
|
||||
let real = tmp_dir.join("tmp_test_real_symlink.db");
|
||||
let link = tmp_dir.join("tmp_test_link_symlink.db");
|
||||
let _ = std::fs::remove_file(&real);
|
||||
let _ = std::fs::remove_file(&link);
|
||||
std::fs::File::create(&real).unwrap();
|
||||
std::os::unix::fs::symlink(&real, &link).unwrap();
|
||||
|
||||
#[test]
|
||||
fn test_open_db_wal_pragmas_set() {
|
||||
let test_path = "tmp_test_wal.db";
|
||||
let _ = fs::remove_file(test_path);
|
||||
let _ = fs::remove_file(format!("{}-shm", test_path));
|
||||
let _ = fs::remove_file(format!("{}-wal", test_path));
|
||||
|
||||
let db = open_db(test_path).expect("Should open DB");
|
||||
let mut stmt = db.prepare("PRAGMA journal_mode;").unwrap();
|
||||
if let Ok(State::Row) = stmt.next() {
|
||||
let mode: String = stmt.read(0).unwrap();
|
||||
assert_eq!(mode, "wal", "SQLite journal mode should be WAL");
|
||||
} else {
|
||||
panic!("Could not read journal_mode pragma");
|
||||
}
|
||||
|
||||
let _ = fs::remove_file(test_path);
|
||||
let _ = fs::remove_file(format!("{}-shm", test_path));
|
||||
let _ = fs::remove_file(format!("{}-wal", test_path));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_open_db_rejects_symlink() {
|
||||
let real = "tmp_test_real.db";
|
||||
let link = "tmp_test_link.db";
|
||||
let _ = fs::remove_file(real);
|
||||
let _ = fs::remove_file(link);
|
||||
fs::File::create(real).unwrap();
|
||||
std::os::unix::fs::symlink(real, link).unwrap();
|
||||
|
||||
let res = open_db(link);
|
||||
let dsn = format!("sqlite:{}?mode=rwc", link.to_str().unwrap());
|
||||
let res = open_database("sqlite", &dsn).await;
|
||||
assert!(res.is_err(), "Symlink DB path should be rejected");
|
||||
let err = match res {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("Expected error for symlink"),
|
||||
};
|
||||
let err = res.err().unwrap();
|
||||
assert!(
|
||||
err.contains("symlink"),
|
||||
"Error should mention symlink: {}",
|
||||
err
|
||||
);
|
||||
|
||||
let _ = fs::remove_file(real);
|
||||
let _ = fs::remove_file(link);
|
||||
let _ = std::fs::remove_file(&real);
|
||||
let _ = std::fs::remove_file(&link);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user