All checks were successful
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 8m28s
Every OpenPay crate now passes clippy with -D warnings, and the openpay entry of the workspace build sets clippy: true so it stays there. Most of the findings were mechanical (clippy's own fixes, write! for push_str(&format!), let-else); where a lint is wrong for the code it is allowed at the narrowest scope with its reason: code tables keep one arm per code, units_cents amounts keep their grouping, op-fraud-graph's index casts rest on a bound upsert_entity now enforces (it used to hand every vertex past 2^32 the same id). Two findings were bugs. op-payouts capped FedNow at USD 1,000,000, the limit before November 2025; the network maximum is USD 10,000,000 and a test now sends five million. op-crypto-extra's EIP-7702 low-s bound was N - 2^255, not N/2: it refused valid low-s signatures in a band just below N/2. It is N/2 now, with tests at both edges. Co-Authored-By: Claude Opus 5.5 <[email protected]>
68 lines
1.7 KiB
Rust
68 lines
1.7 KiB
Rust
//! Integration tests for [`op_fraud_graph::VelocityCounter`].
|
|
|
|
use op_fraud_graph::{EntityKey, EntityKind, VelocityCounter, VelocityWindow};
|
|
|
|
fn k(s: &str) -> EntityKey {
|
|
EntityKey::from_raw(EntityKind::Account, s)
|
|
}
|
|
|
|
#[test]
|
|
fn counts_within_window() {
|
|
let mut c = VelocityCounter::new(VelocityWindow::default()).expect("ok");
|
|
let key = k("acc-A");
|
|
for t in 0..20 {
|
|
c.record(key, t);
|
|
}
|
|
assert_eq!(c.count(key, 19), 20);
|
|
}
|
|
|
|
#[test]
|
|
fn drops_events_outside_window() {
|
|
let mut c = VelocityCounter::new(VelocityWindow {
|
|
window_secs: 10,
|
|
bucket_secs: 1,
|
|
})
|
|
.expect("ok");
|
|
let key = k("acc-B");
|
|
for t in 0..10 {
|
|
c.record(key, t);
|
|
}
|
|
assert_eq!(c.count(key, 9), 10);
|
|
// Advance halfway: ts=14 → buckets at t=0..4 should have rolled off
|
|
// (window covers ts-9..=ts).
|
|
let later = c.count(key, 14);
|
|
assert!((5..10).contains(&later), "got {later}");
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_key_is_zero() {
|
|
let mut c = VelocityCounter::new(VelocityWindow::default()).expect("ok");
|
|
assert_eq!(c.count(k("missing"), 0), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn evict_stale_removes_old_entries() {
|
|
let mut c = VelocityCounter::new(VelocityWindow {
|
|
window_secs: 5,
|
|
bucket_secs: 1,
|
|
})
|
|
.expect("ok");
|
|
let key = k("acc-C");
|
|
c.record(key, 0);
|
|
assert_eq!(c.count(key, 0), 1);
|
|
c.evict_stale(1000);
|
|
// After eviction, the key should report zero (no state).
|
|
assert_eq!(c.count(key, 1000), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_keys_isolated() {
|
|
let mut c = VelocityCounter::new(VelocityWindow::default()).expect("ok");
|
|
let a = k("acc-X");
|
|
let b = k("acc-Y");
|
|
c.record(a, 0);
|
|
c.record(a, 1);
|
|
c.record(b, 0);
|
|
assert_eq!(c.count(a, 1), 2);
|
|
assert_eq!(c.count(b, 1), 1);
|
|
}
|