Skip to content

Commit 9ced39b

Browse files
committed
test(checks): add negative cases and fix tautological assertions
Closes: #528 Closes: #529 Closes: #530 Closes: #531 - unchecked-token-amount: assert exactly one finding for an unguarded transfer and add a negative case (amount > 0 guard -> no finding). - missing-input-length-bound: add a negative case for a Bytes param with a data.len() guard, and wire the input-length fixture pair into fixture_scans.rs. - large-loop: add an ignored regression test documenting the #449 gap (bounded `for i in 0..n` is currently a false positive). - analyzer: add a regression comment for #531 on the scanned-file-count tests; the referenced compile/collect_rust_paths bugs are already fixed on main and the assertions pass unchanged.
1 parent cef1c35 commit 9ced39b

5 files changed

Lines changed: 74 additions & 1 deletion

File tree

crates/analyzer/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,10 @@ mod tests {
599599
);
600600
}
601601

602+
// Regression guard for #531: `collect_rust_paths` must actually push discovered
603+
// `.rs` files onto `paths`, so `scan_directory` / `scan_files` report a non-zero
604+
// `files_scanned` for a directory that contains source. The assertions below are
605+
// deliberately left unchanged.
602606
#[test]
603607
fn reports_scanned_rust_file_count_after_filters() {
604608
let root = std::env::temp_dir().join(format!(

crates/checks/src/large_loop.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,28 @@ impl C {
145145
assert_eq!(findings.len(), 1);
146146
Ok(())
147147
}
148+
149+
/// Documents the gap tracked by #449: the check has no bounds analysis and flags
150+
/// *every* loop in a `pub` contractimpl method, so a genuinely bounded
151+
/// `for i in 0..n { .. }` is a false positive. Ignored until #449 adds bounds
152+
/// analysis, at which point this should pass unchanged.
153+
#[test]
154+
#[ignore = "false positive until #449 adds loop-bounds analysis"]
155+
fn does_not_flag_bounded_for() -> Result<(), syn::Error> {
156+
let src = r#"
157+
#[contractimpl]
158+
impl C {
159+
pub fn process(env: Env, n: u32) {
160+
for i in 0..n {
161+
let x = i;
162+
}
163+
}
164+
}
165+
"#;
166+
let file = parse_file(src)?;
167+
let check = LargeLoopCheck;
168+
let findings = check.run(&file, src);
169+
assert_eq!(findings.len(), 0);
170+
Ok(())
171+
}
148172
}

crates/checks/src/missing_input_length_bound.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,26 @@ impl C {
152152
Ok(())
153153
}
154154

155+
#[test]
156+
fn does_not_flag_when_length_checked() -> Result<(), syn::Error> {
157+
let src = r#"
158+
#[contractimpl]
159+
impl C {
160+
pub fn process(env: Env, data: Bytes) {
161+
if data.len() > 1000 {
162+
panic!("input too long");
163+
}
164+
let x = data;
165+
}
166+
}
167+
"#;
168+
let file = parse_file(src)?;
169+
let check = MissingInputLengthBoundCheck;
170+
let findings = check.run(&file, src);
171+
assert!(findings.is_empty());
172+
Ok(())
173+
}
174+
155175
#[test]
156176
fn ignores_fixed_size_bytes_n() -> Result<(), syn::Error> {
157177
let src = r#"

crates/checks/src/unchecked_token_amount.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,27 @@ impl C {
130130
let file = parse_file(src)?;
131131
let check = UncheckedTokenAmountCheck;
132132
let findings = check.run(&file, src);
133-
assert!(findings.len() > 0);
133+
assert_eq!(findings.len(), 1);
134+
Ok(())
135+
}
136+
137+
#[test]
138+
fn does_not_flag_guarded_transfer() -> Result<(), syn::Error> {
139+
let src = r#"
140+
#[contractimpl]
141+
impl C {
142+
pub fn send_tokens(token: Address, to: Address, amount: u128) {
143+
if amount > 0 {
144+
let client = token::Client::new(&env, &token);
145+
client.transfer(&to, &amount);
146+
}
147+
}
148+
}
149+
"#;
150+
let file = parse_file(src)?;
151+
let check = UncheckedTokenAmountCheck;
152+
let findings = check.run(&file, src);
153+
assert!(findings.is_empty());
134154
Ok(())
135155
}
136156
}

crates/cli/tests/fixture_scans.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ fn ttl_fixtures() {
139139
assert_fixture_pair("ttl", "missing-ttl-extension");
140140
}
141141

142+
#[test]
143+
fn input_length_fixtures() {
144+
assert_fixture_pair("input-length", "missing-input-length-bound");
145+
}
146+
142147
/// Regression test for issue #362: a function that writes two distinct persistent keys but
143148
/// only calls extend_ttl on one of them must still produce a finding for the unextended key.
144149
/// The old function-scoped `has_extend` flag would have suppressed both findings.

0 commit comments

Comments
 (0)