Skip to content

Commit a86e9e2

Browse files
committed
Fix CI errors: update test code and improve code quality
- Fix test Transaction struct initialization (correct field names and types) - Replace or_insert_with(Vec::new) with or_default() - Use range contains syntax for readability - Collapse nested if statements - Fix metadata HashMap initialization in tests - Add missing ValidatorConfig fields in example - Replace len() > 0 with !is_empty()
1 parent 744431e commit a86e9e2

File tree

4 files changed

+212
-140
lines changed

4 files changed

+212
-140
lines changed

examples/validate_transactions.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
//! This example demonstrates financial transaction validation including
44
//! fraud detection, compliance checks, and business rule enforcement.
55
6-
use rust_transaction_validator::{Transaction, TransactionType, TransactionValidator, ValidatorConfig};
76
use chrono::Utc;
7+
use rust_transaction_validator::{
8+
Transaction, TransactionType, TransactionValidator, ValidatorConfig,
9+
};
810
use std::collections::HashMap;
911

1012
fn main() {
@@ -25,7 +27,10 @@ fn main() {
2527
timestamp: Utc::now(),
2628
user_id: "USER-12345".to_string(),
2729
metadata: Some(HashMap::from([
28-
("beneficiary_name".to_string(), "Corporate Account".to_string()),
30+
(
31+
"beneficiary_name".to_string(),
32+
"Corporate Account".to_string(),
33+
),
2934
("purpose".to_string(), "Business payment".to_string()),
3035
])),
3136
};
@@ -172,7 +177,10 @@ fn main() {
172177
println!(" ✓ {} - Approved", transaction.transaction_id);
173178
} else {
174179
rejected += 1;
175-
println!(" ✗ {} - Rejected: {:?}", transaction.transaction_id, result.errors);
180+
println!(
181+
" ✗ {} - Rejected: {:?}",
182+
transaction.transaction_id, result.errors
183+
);
176184
}
177185
}
178186

@@ -187,6 +195,9 @@ fn main() {
187195
fraud_threshold: 90,
188196
enable_duplicate_check: true,
189197
enable_aml_check: true,
198+
velocity_check_window_minutes: 60,
199+
max_transactions_per_window: 10,
200+
max_amount_per_window: 100_000.0,
190201
};
191202

192203
let mut custom_validator = TransactionValidator::with_config(custom_config);

src/aml_compliance.rs

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ pub struct AMLThresholds {
2525
impl Default for AMLThresholds {
2626
fn default() -> Self {
2727
Self {
28-
ctr_threshold: 10000.0, // FinCEN CTR requirement
29-
sar_threshold: 5000.0, // FinCEN SAR guideline
28+
ctr_threshold: 10000.0, // FinCEN CTR requirement
29+
sar_threshold: 5000.0, // FinCEN SAR guideline
3030
structuring_threshold: 9500.0, // Just under $10k
3131
}
3232
}
@@ -120,9 +120,17 @@ impl AMLChecker {
120120
}
121121

122122
// Sanctioned entity check
123-
if self.is_sanctioned_entity(&transaction.from_account)
124-
|| self.is_sanctioned_entity(&transaction.to_account)
125-
{
123+
let from_sanctioned = transaction
124+
.from_account
125+
.as_deref()
126+
.map(|a| self.is_sanctioned_entity(a))
127+
.unwrap_or(false);
128+
let to_sanctioned = transaction
129+
.to_account
130+
.as_deref()
131+
.map(|a| self.is_sanctioned_entity(a))
132+
.unwrap_or(false);
133+
if from_sanctioned || to_sanctioned {
126134
red_flags.push(AMLRedFlag {
127135
flag_type: RedFlagType::SanctionedEntity,
128136
description: "Transaction involves sanctioned entity".to_string(),
@@ -134,7 +142,11 @@ impl AMLChecker {
134142

135143
// Cross-border transaction check
136144
if let Some(ref metadata) = transaction.metadata {
137-
if metadata.get("cross_border").and_then(|v| v.as_bool()) == Some(true) {
145+
if metadata
146+
.get("cross_border")
147+
.map(|v| v == "true")
148+
.unwrap_or(false)
149+
{
138150
red_flags.push(AMLRedFlag {
139151
flag_type: RedFlagType::CrossBorder,
140152
description: "Cross-border transaction requires additional due diligence"
@@ -146,20 +158,20 @@ impl AMLChecker {
146158
}
147159

148160
// Cash intensive check
149-
if transaction.transaction_type == "cash_deposit"
150-
|| transaction.transaction_type == "cash_withdrawal"
161+
if matches!(
162+
transaction.transaction_type,
163+
crate::TransactionType::Deposit | crate::TransactionType::Withdrawal
164+
) && transaction.amount >= 5000.0
151165
{
152-
if transaction.amount >= 5000.0 {
153-
red_flags.push(AMLRedFlag {
154-
flag_type: RedFlagType::CashIntensive,
155-
description: format!(
156-
"Large cash {} of {}",
157-
transaction.transaction_type, transaction.amount
158-
),
159-
severity: AlertSeverity::High,
160-
});
161-
risk_score += 25;
162-
}
166+
red_flags.push(AMLRedFlag {
167+
flag_type: RedFlagType::CashIntensive,
168+
description: format!(
169+
"Large cash {} of {}",
170+
transaction.transaction_type, transaction.amount
171+
),
172+
severity: AlertSeverity::High,
173+
});
174+
risk_score += 25;
163175
}
164176

165177
AMLResult {
@@ -209,7 +221,13 @@ impl KYCValidator {
209221
let mut warnings = Vec::new();
210222

211223
// Required fields for KYC
212-
let required = ["full_name", "date_of_birth", "address", "id_number", "id_type"];
224+
let required = [
225+
"full_name",
226+
"date_of_birth",
227+
"address",
228+
"id_number",
229+
"id_type",
230+
];
213231

214232
for field in &required {
215233
if customer_data.get(field).is_none() {
@@ -220,21 +238,30 @@ impl KYCValidator {
220238
// Check for enhanced due diligence triggers
221239
if let Some(country) = customer_data.get("country").and_then(|v| v.as_str()) {
222240
if Self::is_high_risk_jurisdiction(country) {
223-
warnings.push("Customer from high-risk jurisdiction - Enhanced Due Diligence required".to_string());
241+
warnings.push(
242+
"Customer from high-risk jurisdiction - Enhanced Due Diligence required"
243+
.to_string(),
244+
);
224245
}
225246
}
226247

227-
if let Some(pep) = customer_data.get("politically_exposed_person").and_then(|v| v.as_bool()) {
248+
if let Some(pep) = customer_data
249+
.get("politically_exposed_person")
250+
.and_then(|v| v.as_bool())
251+
{
228252
if pep {
229-
warnings.push("Politically Exposed Person - Enhanced Due Diligence required".to_string());
253+
warnings.push(
254+
"Politically Exposed Person - Enhanced Due Diligence required".to_string(),
255+
);
230256
}
231257
}
232258

259+
let requires_enhanced_dd = !warnings.is_empty();
233260
KYCValidationResult {
234261
valid: missing_fields.is_empty(),
235262
missing_fields,
236263
warnings,
237-
requires_enhanced_dd: !warnings.is_empty(),
264+
requires_enhanced_dd,
238265
}
239266
}
240267

@@ -258,23 +285,24 @@ mod tests {
258285
use super::*;
259286
use chrono::Utc;
260287

261-
fn create_test_transaction(amount: f64, txn_type: &str) -> Transaction {
288+
fn create_test_transaction(amount: f64, txn_type: crate::TransactionType) -> Transaction {
262289
Transaction {
263-
id: "TXN-001".to_string(),
264-
from_account: "ACC-123".to_string(),
265-
to_account: "ACC-456".to_string(),
290+
transaction_id: "TXN-001".to_string(),
291+
from_account: Some("ACC-123".to_string()),
292+
to_account: Some("ACC-456".to_string()),
266293
amount,
267294
currency: "USD".to_string(),
268295
timestamp: Utc::now(),
269-
transaction_type: txn_type.to_string(),
296+
transaction_type: txn_type,
297+
user_id: "USER-001".to_string(),
270298
metadata: None,
271299
}
272300
}
273301

274302
#[test]
275303
fn test_ctr_requirement() {
276304
let checker = AMLChecker::new();
277-
let txn = create_test_transaction(15000.0, "transfer");
305+
let txn = create_test_transaction(15000.0, crate::TransactionType::Transfer);
278306
let result = checker.check_compliance(&txn);
279307

280308
assert!(result.requires_ctr);
@@ -287,7 +315,7 @@ mod tests {
287315
#[test]
288316
fn test_structuring_detection() {
289317
let checker = AMLChecker::new();
290-
let txn = create_test_transaction(9800.0, "transfer");
318+
let txn = create_test_transaction(9800.0, crate::TransactionType::Transfer);
291319
let result = checker.check_compliance(&txn);
292320

293321
assert!(result.requires_sar);
@@ -300,8 +328,8 @@ mod tests {
300328
#[test]
301329
fn test_sanctioned_entity() {
302330
let checker = AMLChecker::new();
303-
let mut txn = create_test_transaction(1000.0, "transfer");
304-
txn.from_account = "OFAC-SANCTIONED-001".to_string();
331+
let mut txn = create_test_transaction(1000.0, crate::TransactionType::Transfer);
332+
txn.from_account = Some("OFAC-SANCTIONED-001".to_string());
305333

306334
let result = checker.check_compliance(&txn);
307335

@@ -316,7 +344,7 @@ mod tests {
316344
#[test]
317345
fn test_cash_intensive() {
318346
let checker = AMLChecker::new();
319-
let txn = create_test_transaction(8000.0, "cash_deposit");
347+
let txn = create_test_transaction(8000.0, crate::TransactionType::Deposit);
320348
let result = checker.check_compliance(&txn);
321349

322350
assert!(result
@@ -328,8 +356,10 @@ mod tests {
328356
#[test]
329357
fn test_cross_border() {
330358
let checker = AMLChecker::new();
331-
let mut txn = create_test_transaction(5000.0, "transfer");
332-
txn.metadata = Some(serde_json::json!({"cross_border": true}));
359+
let mut txn = create_test_transaction(5000.0, crate::TransactionType::Transfer);
360+
let mut metadata = std::collections::HashMap::new();
361+
metadata.insert("cross_border".to_string(), "true".to_string());
362+
txn.metadata = Some(metadata);
333363

334364
let result = checker.check_compliance(&txn);
335365

0 commit comments

Comments
 (0)