348 lines
10 KiB
Rust
348 lines
10 KiB
Rust
use heromodels::db::Collection;
|
|
use heromodels::db::Db;
|
|
use heromodels::db::hero::OurDB;
|
|
use heromodels::models::biz::{BusinessType, Company, CompanyStatus, Payment, PaymentStatus};
|
|
use heromodels_core::Model;
|
|
use std::sync::Arc;
|
|
|
|
fn create_test_db() -> Arc<OurDB> {
|
|
let timestamp = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_nanos();
|
|
let path = format!("/tmp/payment_test_{}", timestamp);
|
|
|
|
// Clean up any existing database at this path
|
|
let _ = std::fs::remove_dir_all(&path);
|
|
|
|
Arc::new(OurDB::new(path, true).expect("Failed to create test database"))
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_creation() {
|
|
let payment = Payment::new(
|
|
"pi_test_123".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
100.0,
|
|
50.0,
|
|
150.0,
|
|
);
|
|
|
|
assert_eq!(payment.payment_intent_id, "pi_test_123");
|
|
assert_eq!(payment.company_id, 1);
|
|
assert_eq!(payment.payment_plan, "monthly");
|
|
assert_eq!(payment.setup_fee, 100.0);
|
|
assert_eq!(payment.monthly_fee, 50.0);
|
|
assert_eq!(payment.total_amount, 150.0);
|
|
assert_eq!(payment.currency, "usd");
|
|
assert_eq!(payment.status, PaymentStatus::Pending);
|
|
assert_eq!(payment.stripe_customer_id, None);
|
|
assert!(payment.created_at > 0);
|
|
assert_eq!(payment.completed_at, None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_status_default() {
|
|
let payment = Payment::new(
|
|
"pi_test".to_string(),
|
|
1,
|
|
"yearly".to_string(),
|
|
500.0,
|
|
99.0,
|
|
1688.0,
|
|
);
|
|
|
|
assert_eq!(payment.status, PaymentStatus::Pending);
|
|
assert!(payment.is_pending());
|
|
assert!(!payment.is_processing());
|
|
assert!(!payment.is_completed());
|
|
assert!(!payment.has_failed());
|
|
assert!(!payment.is_refunded());
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_processing() {
|
|
let payment = Payment::new(
|
|
"pi_processing_test".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
150.0,
|
|
60.0,
|
|
210.0,
|
|
);
|
|
|
|
let processing_payment = payment.process_payment();
|
|
|
|
assert_eq!(processing_payment.status, PaymentStatus::Processing);
|
|
assert!(processing_payment.is_processing());
|
|
assert!(!processing_payment.is_pending());
|
|
assert!(!processing_payment.is_completed());
|
|
assert!(!processing_payment.has_failed());
|
|
assert!(!processing_payment.is_refunded());
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_completion() {
|
|
let payment = Payment::new(
|
|
"pi_complete_test".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
200.0,
|
|
75.0,
|
|
275.0,
|
|
);
|
|
|
|
let stripe_customer_id = Some("cus_test_123".to_string());
|
|
let completed_payment = payment.complete_payment(stripe_customer_id.clone());
|
|
|
|
assert_eq!(completed_payment.status, PaymentStatus::Completed);
|
|
assert_eq!(completed_payment.stripe_customer_id, stripe_customer_id);
|
|
assert!(completed_payment.is_completed());
|
|
assert!(!completed_payment.is_pending());
|
|
assert!(!completed_payment.has_failed());
|
|
assert!(!completed_payment.is_refunded());
|
|
assert!(completed_payment.completed_at.is_some());
|
|
assert!(completed_payment.completed_at.unwrap() > 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_failure() {
|
|
let payment = Payment::new(
|
|
"pi_fail_test".to_string(),
|
|
1,
|
|
"yearly".to_string(),
|
|
300.0,
|
|
60.0,
|
|
1020.0,
|
|
);
|
|
|
|
let failed_payment = payment.fail_payment();
|
|
|
|
assert_eq!(failed_payment.status, PaymentStatus::Failed);
|
|
assert!(failed_payment.has_failed());
|
|
assert!(!failed_payment.is_completed());
|
|
assert!(!failed_payment.is_pending());
|
|
assert!(!failed_payment.is_refunded());
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_refund() {
|
|
let payment = Payment::new(
|
|
"pi_refund_test".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
150.0,
|
|
45.0,
|
|
195.0,
|
|
);
|
|
|
|
// First complete the payment
|
|
let completed_payment = payment.complete_payment(Some("cus_refund_test".to_string()));
|
|
assert!(completed_payment.is_completed());
|
|
|
|
// Then refund it
|
|
let refunded_payment = completed_payment.refund_payment();
|
|
|
|
assert_eq!(refunded_payment.status, PaymentStatus::Refunded);
|
|
assert!(refunded_payment.is_refunded());
|
|
assert!(!refunded_payment.is_completed());
|
|
assert!(!refunded_payment.is_pending());
|
|
assert!(!refunded_payment.has_failed());
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_builder_pattern() {
|
|
let custom_timestamp = 1640995200; // Jan 1, 2022
|
|
let payment = Payment::new(
|
|
"pi_builder_test".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
100.0,
|
|
50.0,
|
|
150.0,
|
|
)
|
|
.payment_plan("yearly".to_string())
|
|
.setup_fee(500.0)
|
|
.monthly_fee(99.0)
|
|
.total_amount(1688.0)
|
|
.currency("eur".to_string())
|
|
.stripe_customer_id(Some("cus_builder_test".to_string()))
|
|
.created_at(custom_timestamp)
|
|
.completed_at(Some(custom_timestamp + 3600));
|
|
|
|
assert_eq!(payment.payment_plan, "yearly");
|
|
assert_eq!(payment.setup_fee, 500.0);
|
|
assert_eq!(payment.monthly_fee, 99.0);
|
|
assert_eq!(payment.total_amount, 1688.0);
|
|
assert_eq!(payment.currency, "eur");
|
|
assert_eq!(
|
|
payment.stripe_customer_id,
|
|
Some("cus_builder_test".to_string())
|
|
);
|
|
assert_eq!(payment.created_at, custom_timestamp);
|
|
assert_eq!(payment.completed_at, Some(custom_timestamp + 3600));
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_database_persistence() {
|
|
let db = create_test_db();
|
|
|
|
let payment = Payment::new(
|
|
"pi_db_test".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
200.0,
|
|
60.0,
|
|
260.0,
|
|
);
|
|
|
|
// Save payment
|
|
let (payment_id, saved_payment) = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.set(&payment)
|
|
.expect("Failed to save payment");
|
|
assert!(payment_id > 0);
|
|
assert_eq!(saved_payment.payment_intent_id, "pi_db_test");
|
|
|
|
// Retrieve payment
|
|
let retrieved_payment: Payment = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.get_by_id(payment_id)
|
|
.expect("Failed to get payment")
|
|
.unwrap();
|
|
assert_eq!(retrieved_payment.payment_intent_id, "pi_db_test");
|
|
assert_eq!(retrieved_payment.company_id, 1);
|
|
assert_eq!(retrieved_payment.status, PaymentStatus::Pending);
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_status_transitions() {
|
|
let db = create_test_db();
|
|
|
|
let payment = Payment::new(
|
|
"pi_transition_test".to_string(),
|
|
1,
|
|
"yearly".to_string(),
|
|
400.0,
|
|
80.0,
|
|
1360.0,
|
|
);
|
|
|
|
let (payment_id, mut payment) = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.set(&payment)
|
|
.expect("Failed to save payment");
|
|
|
|
// Test pending -> completed
|
|
payment = payment.complete_payment(Some("cus_transition_test".to_string()));
|
|
let (_, mut payment) = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.set(&payment)
|
|
.expect("Failed to update payment");
|
|
assert!(payment.is_completed());
|
|
|
|
// Test completed -> refunded
|
|
payment = payment.refund_payment();
|
|
let (_, payment) = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.set(&payment)
|
|
.expect("Failed to update payment");
|
|
assert!(payment.is_refunded());
|
|
|
|
// Verify final state in database
|
|
let final_payment: Payment = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.get_by_id(payment_id)
|
|
.expect("Failed to get payment")
|
|
.unwrap();
|
|
assert_eq!(final_payment.status, PaymentStatus::Refunded);
|
|
}
|
|
|
|
#[test]
|
|
fn test_payment_timestamps() {
|
|
let payment = Payment::new(
|
|
"pi_timestamp_test".to_string(),
|
|
1,
|
|
"monthly".to_string(),
|
|
100.0,
|
|
50.0,
|
|
150.0,
|
|
);
|
|
|
|
let initial_created_at = payment.base_data.created_at;
|
|
let initial_modified_at = payment.base_data.modified_at;
|
|
|
|
// Complete payment (should update modified_at)
|
|
let completed_payment = payment.complete_payment(Some("cus_timestamp_test".to_string()));
|
|
|
|
assert_eq!(completed_payment.base_data.created_at, initial_created_at);
|
|
assert!(completed_payment.base_data.modified_at >= initial_modified_at);
|
|
}
|
|
|
|
#[test]
|
|
fn test_company_payment_integration() {
|
|
let db = create_test_db();
|
|
|
|
// Create company with default PendingPayment status
|
|
let company = Company::new()
|
|
.name("Integration Test Corp")
|
|
.registration_number("ITC-001")
|
|
.incorporation_date(chrono::Utc::now().timestamp())
|
|
.email("test@integration.com")
|
|
.business_type(BusinessType::Starter);
|
|
|
|
let (company_id, company) = db
|
|
.collection::<Company>()
|
|
.expect("open company collection")
|
|
.set(&company)
|
|
.expect("Failed to save company");
|
|
assert_eq!(company.status, CompanyStatus::PendingPayment);
|
|
|
|
// Create payment for the company
|
|
let payment = Payment::new(
|
|
"pi_integration_test".to_string(),
|
|
company_id,
|
|
"monthly".to_string(),
|
|
250.0,
|
|
55.0,
|
|
305.0,
|
|
);
|
|
|
|
let (_payment_id, payment) = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.set(&payment)
|
|
.expect("Failed to save payment");
|
|
assert_eq!(payment.company_id, company_id);
|
|
|
|
// Complete payment
|
|
let completed_payment = payment.complete_payment(Some("cus_integration_test".to_string()));
|
|
let (_, completed_payment) = db
|
|
.collection::<Payment>()
|
|
.expect("open payment collection")
|
|
.set(&completed_payment)
|
|
.expect("Failed to update payment");
|
|
|
|
// Update company status to Active
|
|
let active_company = company.status(CompanyStatus::Active);
|
|
let (_, active_company) = db
|
|
.collection::<Company>()
|
|
.expect("open company collection")
|
|
.set(&active_company)
|
|
.expect("Failed to update company");
|
|
|
|
// Verify final states
|
|
assert!(completed_payment.is_completed());
|
|
assert_eq!(active_company.status, CompanyStatus::Active);
|
|
|
|
// Verify relationship
|
|
assert_eq!(completed_payment.company_id, active_company.get_id());
|
|
}
|