Files
foxhunt/testing/integration/smoke_tests/basic_order_flow.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt,
consolidate 10 test crates into testing/, split config crate
from deployment config files.

Root directory reduced from 38+ to ~17 directories.
All Cargo.toml paths and build.rs proto refs updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:56:00 +01:00

414 lines
12 KiB
Rust

//! Basic Order Flow Smoke Tests
//!
//! Tests to validate core trading functionality including order submission,
//! querying, cancellation, and position management.
use super::common::*;
#[tokio::test]
async fn test_database_order_submission() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Create a test order
let order_id = uuid::Uuid::new_v4();
let user_id = "smoke_test_user";
let symbol = "BTC-USD";
let order_type = "market";
let side = "buy";
let quantity = 1.0;
let price = None::<f64>;
// Insert order
sqlx::query(
r#"
INSERT INTO orders (
id, user_id, symbol, order_type, side, quantity, price,
status, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
"#
)
.bind(&order_id)
.bind(user_id)
.bind(symbol)
.bind(order_type)
.bind(side)
.bind(quantity)
.bind(price)
.bind("pending")
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
println!("✅ Order submitted to database");
println!(" Order ID: {}", order_id);
println!(" Symbol: {}", symbol);
println!(" Side: {}", side);
println!(" Quantity: {}", quantity);
// Cleanup
sqlx::query("DELETE FROM orders WHERE id = $1")
.bind(&order_id)
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
pool.close().await;
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_database_order_query() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Create a test order
let order_id = uuid::Uuid::new_v4();
let user_id = "smoke_test_user";
let symbol = "ETH-USD";
sqlx::query(
r#"
INSERT INTO orders (
id, user_id, symbol, order_type, side, quantity,
status, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
"#
)
.bind(&order_id)
.bind(user_id)
.bind(symbol)
.bind("limit")
.bind("sell")
.bind(2.0)
.bind("pending")
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Query order
let order: (uuid::Uuid, String, String) = sqlx::query_as(
"SELECT id, user_id, symbol FROM orders WHERE id = $1"
)
.bind(&order_id)
.fetch_one(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(order.0, order_id);
assert_eq!(order.1, user_id);
assert_eq!(order.2, symbol);
println!("✅ Order query successful");
println!(" Order ID: {}", order.0);
println!(" User ID: {}", order.1);
println!(" Symbol: {}", order.2);
// Cleanup
sqlx::query("DELETE FROM orders WHERE id = $1")
.bind(&order_id)
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
pool.close().await;
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_database_order_cancellation() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Create a test order
let order_id = uuid::Uuid::new_v4();
sqlx::query(
r#"
INSERT INTO orders (
id, user_id, symbol, order_type, side, quantity,
status, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
"#
)
.bind(&order_id)
.bind("smoke_test_user")
.bind("BTC-USD")
.bind("limit")
.bind("buy")
.bind(0.5)
.bind("pending")
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Cancel order
sqlx::query(
"UPDATE orders SET status = $1, updated_at = NOW() WHERE id = $2"
)
.bind("cancelled")
.bind(&order_id)
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Verify cancellation
let status: (String,) = sqlx::query_as(
"SELECT status FROM orders WHERE id = $1"
)
.bind(&order_id)
.fetch_one(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(status.0, "cancelled");
println!("✅ Order cancellation successful");
println!(" Order ID: {}", order_id);
println!(" Status: {}", status.0);
// Cleanup
sqlx::query("DELETE FROM orders WHERE id = $1")
.bind(&order_id)
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
pool.close().await;
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_database_position_management() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let position_id = uuid::Uuid::new_v4();
let user_id = "smoke_test_user";
let symbol = "BTC-USD";
let quantity = 1.5;
let entry_price = 50000.0;
// Create position
sqlx::query(
r#"
INSERT INTO positions (
id, user_id, symbol, quantity, entry_price, current_price,
unrealized_pnl, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
"#
)
.bind(&position_id)
.bind(user_id)
.bind(symbol)
.bind(quantity)
.bind(entry_price)
.bind(entry_price) // current_price = entry_price initially
.bind(0.0) // unrealized_pnl = 0 initially
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
// Query position
let position: (uuid::Uuid, String, f64, f64) = sqlx::query_as(
"SELECT id, symbol, quantity, entry_price FROM positions WHERE id = $1"
)
.bind(&position_id)
.fetch_one(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(position.0, position_id);
assert_eq!(position.1, symbol);
assert_eq!(position.2, quantity);
assert_eq!(position.3, entry_price);
println!("✅ Position management successful");
println!(" Position ID: {}", position.0);
println!(" Symbol: {}", position.1);
println!(" Quantity: {}", position.2);
println!(" Entry Price: ${}", position.3);
// Cleanup
sqlx::query("DELETE FROM positions WHERE id = $1")
.bind(&position_id)
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
pool.close().await;
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_database_order_history() {
let result = with_timeout(async {
let pool = sqlx::PgPool::connect(&database_url())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
let user_id = "smoke_test_user";
// Create multiple test orders
let order_ids: Vec<uuid::Uuid> = (0..3)
.map(|_| uuid::Uuid::new_v4())
.collect();
for order_id in &order_ids {
sqlx::query(
r#"
INSERT INTO orders (
id, user_id, symbol, order_type, side, quantity,
status, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
"#
)
.bind(order_id)
.bind(user_id)
.bind("BTC-USD")
.bind("market")
.bind("buy")
.bind(0.1)
.bind("filled")
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
}
// Query order history
let orders: Vec<(uuid::Uuid, String)> = sqlx::query_as(
"SELECT id, status FROM orders WHERE user_id = $1 ORDER BY created_at DESC LIMIT 10"
)
.bind(user_id)
.fetch_all(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert!(orders.len() >= 3, "Should find at least 3 orders");
println!("✅ Order history query successful");
println!(" Found {} orders for user {}", orders.len(), user_id);
// Cleanup
for order_id in &order_ids {
sqlx::query("DELETE FROM orders WHERE id = $1")
.bind(order_id)
.execute(&pool)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
}
pool.close().await;
Ok(())
}).await;
skip_if_unavailable!("PostgreSQL", { result });
}
#[tokio::test]
async fn test_complete_order_lifecycle() {
println!("🔄 Running complete order lifecycle...");
let mut steps_passed = 0;
let total_steps = 4;
if let Ok(pool) = sqlx::PgPool::connect(&database_url()).await {
let order_id = uuid::Uuid::new_v4();
// Step 1: Submit order
if sqlx::query(
r#"
INSERT INTO orders (
id, user_id, symbol, order_type, side, quantity,
status, created_at, updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
"#
)
.bind(&order_id)
.bind("smoke_test_user")
.bind("BTC-USD")
.bind("market")
.bind("buy")
.bind(1.0)
.bind("pending")
.execute(&pool)
.await
.is_ok() {
println!(" ✓ Order submission");
steps_passed += 1;
}
// Step 2: Query order
if sqlx::query_as::<_, (String,)>(
"SELECT status FROM orders WHERE id = $1"
)
.bind(&order_id)
.fetch_one(&pool)
.await
.is_ok() {
println!(" ✓ Order query");
steps_passed += 1;
}
// Step 3: Update order
if sqlx::query(
"UPDATE orders SET status = $1, updated_at = NOW() WHERE id = $2"
)
.bind("filled")
.bind(&order_id)
.execute(&pool)
.await
.is_ok() {
println!(" ✓ Order update");
steps_passed += 1;
}
// Step 4: Cancel/cleanup
if sqlx::query("DELETE FROM orders WHERE id = $1")
.bind(&order_id)
.execute(&pool)
.await
.is_ok() {
println!(" ✓ Order cleanup");
steps_passed += 1;
}
pool.close().await;
} else {
println!(" ⚠ Database unavailable - skipping order lifecycle test");
return;
}
println!("\n📊 Order Lifecycle Summary:");
println!(" Steps passed: {}/{}", steps_passed, total_steps);
assert_eq!(steps_passed, total_steps, "Complete order lifecycle should pass all steps");
println!("✅ Order lifecycle check complete");
}