From 91c802f02899ec844b8b5a05934366d1d9840b33 Mon Sep 17 00:00:00 2001 From: azahjessica49-commits Date: Tue, 28 Jul 2026 23:34:26 +0100 Subject: [PATCH 1/3] Add tests for the lock-time-limit guard --- quicklendx-contracts/src/lib.rs | 2 + .../src/test_lock_time_limit_guard.rs | 105 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 quicklendx-contracts/src/test_lock_time_limit_guard.rs diff --git a/quicklendx-contracts/src/lib.rs b/quicklendx-contracts/src/lib.rs index 448d8a3fe..d61b24bc8 100644 --- a/quicklendx-contracts/src/lib.rs +++ b/quicklendx-contracts/src/lib.rs @@ -138,6 +138,8 @@ mod test_dispute_timeline_props; #[cfg(test)] mod test_due_date_guard; #[cfg(test)] +mod test_lock_time_limit_guard; +#[cfg(test)] // mod test_dispute_event_invariant; #[cfg(test)] mod test_dust_transfer; diff --git a/quicklendx-contracts/src/test_lock_time_limit_guard.rs b/quicklendx-contracts/src/test_lock_time_limit_guard.rs new file mode 100644 index 000000000..ad91f8805 --- /dev/null +++ b/quicklendx-contracts/src/test_lock_time_limit_guard.rs @@ -0,0 +1,105 @@ +//! Lock-time-limit guard — "Within, at limit, past". +//! +//! Pins the time-window validation in +//! `ProtocolLimitsContract::validate_invoice` across three zones: +//! +//! | Zone | Lower bound | Upper bound | +//! |------------|------------------------------------|------------------------------------| +//! | **Within** | `due_date > current_time` | `due_date < max_due_date` | +//! | **At limit** | `due_date == current_time` | `due_date == max_due_date` | +//! | **Past** | `due_date < current_time` | `due_date > max_due_date` | +//! +//! Every test is gated only by `#[cfg(test)]` so it runs on every CI matrix +//! entry, not just when the `legacy-tests` feature is enabled. + +#![cfg(test)] + +use crate::errors::QuickLendXError; +use crate::protocol_limits::ProtocolLimitsContract; +use crate::QuickLendXContract; +use soroban_sdk::{ + testutils::{Address as _, Ledger}, + Address, Env, +}; + +const SECONDS_PER_DAY: u64 = 86_400; + +fn setup_env() -> (Env, Address) { + let env = Env::default(); + env.mock_all_auths(); + env.ledger().set_timestamp(1_000_000); + let contract_id = env.register(QuickLendXContract, ()); + (env, contract_id) +} + +fn valid_amount() -> i128 { + 100i128 // > DEFAULT_MIN_AMOUNT (10 in test builds) +} + +// --------------------------------------------------------------------------- +// Lower bound: due_date <= current_time → InvoiceDueDateInvalid +// --------------------------------------------------------------------------- + +#[test] +fn validate_invoice_rejects_due_date_past_lower_bound() { + let (env, contract_id) = setup_env(); + let result = env.as_contract(&contract_id, || { + ProtocolLimitsContract::validate_invoice(env.clone(), valid_amount(), 999_999u64) + }); + assert_eq!(result, Err(QuickLendXError::InvoiceDueDateInvalid)); +} + +#[test] +fn validate_invoice_rejects_due_date_at_lower_bound() { + let (env, contract_id) = setup_env(); + let result = env.as_contract(&contract_id, || { + ProtocolLimitsContract::validate_invoice(env.clone(), valid_amount(), 1_000_000u64) + }); + assert_eq!(result, Err(QuickLendXError::InvoiceDueDateInvalid)); +} + +#[test] +fn validate_invoice_accepts_due_date_within_lower_bound() { + let (env, contract_id) = setup_env(); + let result = env.as_contract(&contract_id, || { + // due_date = current_time + 1, the first valid second + ProtocolLimitsContract::validate_invoice(env.clone(), valid_amount(), 1_000_001u64) + }); + assert_eq!(result, Ok(())); +} + +// --------------------------------------------------------------------------- +// Upper bound: due_date > max_due_date → InvoiceDueDateInvalid +// --------------------------------------------------------------------------- + +#[test] +fn validate_invoice_accepts_due_date_within_upper_bound() { + let (env, contract_id) = setup_env(); + let one_day_before_max = env.ledger().timestamp() + + 365 * SECONDS_PER_DAY + - 1; + let result = env.as_contract(&contract_id, || { + ProtocolLimitsContract::validate_invoice(env.clone(), valid_amount(), one_day_before_max) + }); + assert_eq!(result, Ok(())); +} + +#[test] +fn validate_invoice_accepts_due_date_at_upper_bound() { + let (env, contract_id) = setup_env(); + let max_due_date = env.ledger().timestamp() + 365 * SECONDS_PER_DAY; + let result = env.as_contract(&contract_id, || { + ProtocolLimitsContract::validate_invoice(env.clone(), valid_amount(), max_due_date) + }); + assert_eq!(result, Ok(())); +} + +#[test] +fn validate_invoice_rejects_due_date_past_upper_bound() { + let (env, contract_id) = setup_env(); + let past_max = env.ledger().timestamp() + 365 * SECONDS_PER_DAY + 1; + let result = env.as_contract(&contract_id, || { + ProtocolLimitsContract::validate_invoice(env.clone(), valid_amount(), past_max) + }); + assert_eq!(result, Err(QuickLendXError::InvoiceDueDateInvalid)); +} From 8f1c220a9a02f830210c2bc7b37ae3a5aa2bf31b Mon Sep 17 00:00:00 2001 From: azahjessica49-commits Date: Tue, 28 Jul 2026 23:36:38 +0100 Subject: [PATCH 2/3] quick fix [ci-skip] From 4ceb6599e68ef0db515ab94d39df7858dc13b3b7 Mon Sep 17 00:00:00 2001 From: azahjessica49-commits Date: Tue, 28 Jul 2026 23:37:08 +0100 Subject: [PATCH 3/3] quick fix [ci-skip]