use secreport::{Format, render, render_any};
use secfinding::{Finding, Reportable, Severity};
use std::sync::Arc;
use std::thread;
#[test]
fn concurrent_render_all_formats_20_threads() {
let finding = Arc::new(Finding::new("s", "t", Severity::Critical, "T", "D").unwrap());
let mut handles = vec![];
for _ in 0..20 {
let f = finding.clone();
handles.push(thread::spawn(move || {
for format in [
Format::Text,
Format::Json,
Format::Jsonl,
Format::Sarif,
Format::Markdown,
] {
let out = render(std::slice::from_ref(&*f), format, "tool").unwrap();
assert!(!out.is_empty());
}
}));
}
for h in handles {
h.join().unwrap();
}
}
#[test]
fn concurrent_render_any_with_custom_reportable() {
#[derive(Clone)]
struct R;
impl Reportable for R {
fn scanner(&self) -> &str { "s" }
fn target(&self) -> &str { "t" }
fn severity(&self) -> Severity { Severity::High }
fn title(&self) -> &str { "T" }
}
let items: Vec<R> = (0..50).map(|_| R).collect();
let mut handles = vec![];
for _ in 0..10 {
let items = items.clone();
handles.push(thread::spawn(move || {
let out = render_any(&items, Format::Json, "tool").unwrap();
let parsed: Vec<serde_json::Value> = serde_json::from_str(&out).unwrap();
assert_eq!(parsed.len(), 50);
}));
}
for h in handles {
h.join().unwrap();
}
}
#[test]
fn concurrent_mixed_formats_same_findings() {
let findings: Vec<Finding> = (0..100)
.map(|i| Finding::new("s", "t", Severity::Medium, &format!("T{}", i), "D").unwrap())
.collect();
let findings = Arc::new(findings);
let mut handles = vec![];
for format in [
Format::Text,
Format::Json,
Format::Jsonl,
Format::Sarif,
Format::Markdown,
] {
let f = findings.clone();
handles.push(thread::spawn(move || {
let out = render(&*f, format, "tool").unwrap();
assert!(!out.is_empty());
}));
}
for h in handles {
h.join().unwrap();
}
}
#[test]
fn concurrent_scale_jsonl() {
let findings: Vec<Finding> = (0..10_000)
.map(|i| Finding::new("s", "t", Severity::Info, &format!("T{}", i), "D").unwrap())
.collect();
let findings = Arc::new(findings);
let mut handles = vec![];
for _ in 0..8 {
let f = findings.clone();
handles.push(thread::spawn(move || {
let out = render(&*f, Format::Jsonl, "tool").unwrap();
assert_eq!(out.lines().count(), 10_000);
}));
}
for h in handles {
h.join().unwrap();
}
}
#[test]
fn concurrent_scale_sarif() {
let findings: Vec<Finding> = (0..1_000)
.map(|i| Finding::new("s", "t", Severity::High, &format!("T{}", i), "D").unwrap())
.collect();
let findings = Arc::new(findings);
let mut handles = vec![];
for _ in 0..8 {
let f = findings.clone();
handles.push(thread::spawn(move || {
let out = render(&*f, Format::Sarif, "tool").unwrap();
let parsed: serde_json::Value = serde_json::from_str(&out).unwrap();
let results = parsed["runs"][0]["results"].as_array().unwrap();
assert_eq!(results.len(), 1_000);
}));
}
for h in handles {
h.join().unwrap();
}
}