OpenPay (1 crate, 1801 LOC, 57 tests)
op-bin BIN/IIN database + range matching + card-network
classification. 8-digit normalized prefix Bin +
BinRange. Sorted-vec interval tree with O(log N) point
lookup + overlap rejection. CardNetwork classify()
with structural rules (Visa 4, MC 51-55/2221-2720,
Amex 34/37, Discover, JCB, Diners, UnionPay, RuPay,
Mir, Troy, Maestro, Elo). Static prefix tables +
build_default_tree(). ISO/IEC 7812-1 Annex B Luhn
(validate / is_valid / check_digit). Card-type +
issuer-country + Durbin regulated-debit. PCI-safe:
8-digit cap, no full-PAN APIs.
Smart Byte (1 crate, ~1500 LOC, 20 tests)
smart-byte-tendermint BFT consensus. Step enum + Round + propose/prevote/
precommit/commit FSM with voting-power tallies.
ValidatorSet with 2/3+1 quorum, 1/3+1 trust
threshold, round-robin proposer. Block / Header /
Commit / CommitSig with Blake3 hashing. Light client
(sequential + skipping). ABCI Application trait +
EchoApp. DuplicateVoteEvidence + LightClientAttackEvidence.
IBC ClientState + ConsensusState + light-client
update.
EOC (1 crate, 2084 LOC, 23 tests)
eoc-safety AI safety primitives. RegexSet over 16 prompt-injection
signatures. 13-family jailbreak detector (DAN/DUDE/
Grandma/STAN/AIM/dev-mode/etc.). Presidio-style PII
redactor with Luhn-validated CC. Toxicity / bias /
NSFW classifier traits + heuristic baselines (5-axis
bias, 4-category NSFW with hard-reject for minors).
Constitutional AI critique→revise loop. Composable
InputGuard / OutputGuard pipelines. JSON-Schema-subset
output validator. Token-bucket rate limiter +
abuse-window detection. Red-team replay harness.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
82 lines
2.6 KiB
Rust
82 lines
2.6 KiB
Rust
//! Range-tree integration tests:
|
|
//! - the default tree built from `network_ranges` is internally
|
|
//! consistent (no overlaps),
|
|
//! - representative BINs across every supported network resolve
|
|
//! to the expected network,
|
|
//! - point lookups outside any range return `None`.
|
|
|
|
use op_bin::network_ranges::build_default_tree;
|
|
use op_bin::{Bin, CardNetwork};
|
|
|
|
#[test]
|
|
fn default_tree_constructs_disjoint() {
|
|
let tree = build_default_tree().expect("disjoint by construction");
|
|
assert!(tree.len() >= 25);
|
|
}
|
|
|
|
#[test]
|
|
fn known_bins_resolve_per_network() {
|
|
let tree = build_default_tree().expect("ok");
|
|
let cases: &[(&str, CardNetwork)] = &[
|
|
("411111", CardNetwork::Visa),
|
|
("510000", CardNetwork::Mastercard),
|
|
("222100", CardNetwork::Mastercard),
|
|
("340000", CardNetwork::Amex),
|
|
("370000", CardNetwork::Amex),
|
|
("601100", CardNetwork::Discover),
|
|
("352800", CardNetwork::Jcb),
|
|
("360000", CardNetwork::DinersClub),
|
|
("621234", CardNetwork::UnionPay),
|
|
("810000", CardNetwork::RuPay),
|
|
("220000", CardNetwork::Mir),
|
|
("979200", CardNetwork::Troy),
|
|
];
|
|
for (bin_str, expected) in cases {
|
|
let b = Bin::parse(bin_str).expect("valid");
|
|
let got = tree
|
|
.lookup(&b)
|
|
.unwrap_or_else(|| panic!("no range for {bin_str}"));
|
|
assert_eq!(
|
|
got.network, *expected,
|
|
"BIN {bin_str} expected {expected:?}, got {:?}",
|
|
got.network,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unassigned_bin_returns_none() {
|
|
let tree = build_default_tree().expect("ok");
|
|
// 700000 is unassigned in the published BIN catalog.
|
|
let b = Bin::parse("700000").expect("ok");
|
|
assert!(tree.lookup(&b).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn boundary_half_open() {
|
|
let tree = build_default_tree().expect("ok");
|
|
// Mastercard classic range ends just before 56xxxxxx.
|
|
let last_mc = Bin::parse("559999").expect("ok");
|
|
assert_eq!(
|
|
tree.lookup(&last_mc).map(|r| r.network),
|
|
Some(CardNetwork::Mastercard),
|
|
);
|
|
// 56xxxxxx maps into the Maestro carve-out at 5612 only;
|
|
// 5600xx falls outside any registered range.
|
|
let outside_mc = Bin::parse("560000").expect("ok");
|
|
assert!(tree.lookup(&outside_mc).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn sorted_invariant_holds() {
|
|
let tree = build_default_tree().expect("ok");
|
|
let rs = tree.ranges();
|
|
for win in rs.windows(2) {
|
|
assert!(
|
|
win[0].high <= win[1].low,
|
|
"ranges out of order or overlapping: {:?} then {:?}",
|
|
win[0],
|
|
win[1],
|
|
);
|
|
}
|
|
}
|