use serde_json::json;
use solwatch::chain::{is_pubkey, metadata_pda, parse_metaplex};
use solwatch::checks::bundles::mint_deltas;
use solwatch::types::{score_flags, Flag, Severity, Verdict};
const BONK: &str = "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263";
#[test]
fn score_clean_token() {
let flags = vec![
Flag::new("mint_authority", Severity::Good, "ok", "".into(), 0),
Flag::new("freeze_authority", Severity::Good, "ok", "".into(), 0),
];
let (score, verdict) = score_flags(&flags, true);
assert_eq!(score, 100);
assert_eq!(verdict, Verdict::LowRisk);
}
#[test]
fn score_honeypot_hard_caps_at_5() {
let flags = vec![Flag::new(
"non_transferable",
Severity::Critical,
"honeypot",
"".into(),
60,
)
.hard()];
let (score, verdict) = score_flags(&flags, true);
assert!(score <= 5, "critical hard fail must cap at 5, got {score}");
assert_eq!(verdict, Verdict::Avoid);
}
#[test]
fn score_non_critical_hard_caps_at_20() {
let flags = vec![Flag::new("no_liquidity", Severity::Danger, "x", "".into(), 25).hard()];
let (score, _) = score_flags(&flags, true);
assert!(score <= 20);
}
#[test]
fn score_unresolved_token_is_unknown() {
let (score, verdict) = score_flags(&[], false);
assert_eq!(score, 0);
assert_eq!(verdict, Verdict::Unknown);
}
#[test]
fn pubkey_validation() {
assert!(is_pubkey(BONK));
assert!(is_pubkey("So11111111111111111111111111111111111111112"));
assert!(!is_pubkey("0x8e62F281f282686fCa6dCB39288069a93fC23F1c")); assert!(!is_pubkey("notbase58!!!"));
assert!(!is_pubkey(""));
}
#[test]
fn metadata_pda_derivation() {
let pda = metadata_pda(BONK).expect("pda derives");
assert!(is_pubkey(&pda));
assert_eq!(pda, metadata_pda(BONK).unwrap());
}
#[test]
fn metaplex_parser_roundtrip() {
let mut b: Vec<u8> = vec![4];
b.extend([7u8; 32]); b.extend([9u8; 32]); for (s, cap) in [("Bonk", 32), ("BONK", 10), ("https://x/meta.json", 200)] {
b.extend((cap as u32).to_le_bytes());
let mut raw = s.as_bytes().to_vec();
raw.resize(cap, 0);
b.extend(raw);
}
b.extend(100u16.to_le_bytes()); b.push(0); b.push(0); b.push(1); let m = parse_metaplex(&b).expect("parses");
assert_eq!(m.name, "Bonk");
assert_eq!(m.symbol, "BONK");
assert_eq!(m.uri, "https://x/meta.json");
assert!(m.is_mutable);
assert_eq!(m.update_authority, bs58_of([7u8; 32]));
}
#[test]
fn metaplex_parser_rejects_garbage() {
assert!(parse_metaplex(&[]).is_none());
assert!(parse_metaplex(&[4u8; 40]).is_none());
let mut b: Vec<u8> = vec![4];
b.extend([0u8; 64]);
b.extend(u32::MAX.to_le_bytes());
assert!(parse_metaplex(&b).is_none());
}
fn bs58_of(b: [u8; 32]) -> String {
bs58::encode(b).into_string()
}
#[test]
fn mint_deltas_from_parsed_tx() {
let mint = "M1nt111111111111111111111111111111111111111";
let tx = json!({
"meta": {
"preTokenBalances": [
{"accountIndex": 3, "mint": mint, "owner": "curve", "uiTokenAmount": {"amount": "1000"}},
{"accountIndex": 4, "mint": "OTHER", "owner": "alice", "uiTokenAmount": {"amount": "5"}}
],
"postTokenBalances": [
{"accountIndex": 3, "mint": mint, "owner": "curve", "uiTokenAmount": {"amount": "400"}},
{"accountIndex": 5, "mint": mint, "owner": "alice", "uiTokenAmount": {"amount": "600"}},
{"accountIndex": 4, "mint": "OTHER", "owner": "alice", "uiTokenAmount": {"amount": "9"}}
]
}
});
let mut d = mint_deltas(&tx, mint);
d.sort();
assert_eq!(d, vec![("alice".to_string(), 600u128)]);
}
#[test]
fn tweet_is_bounded() {
use solwatch::report::render_tweet;
use solwatch::types::{AuditResult, TokenMeta};
let r = AuditResult {
address: BONK.into(),
chain: "solana",
scanned_at: "2026-07-13T00:00:00Z".into(),
token: TokenMeta {
symbol: Some("X".repeat(120)),
market_cap_usd: Some(123456789012345.0),
..Default::default()
},
score: 3,
verdict: Verdict::Avoid,
flags: vec![
Flag::new(
"a",
Severity::Critical,
&"very long flag title ".repeat(10),
"d".into(),
60,
),
Flag::new(
"b",
Severity::Danger,
&"another long one ".repeat(10),
"d".into(),
30,
),
],
sections: json!({}),
warnings: vec![],
rokha_url: None,
rokha_app: serde_json::Value::Null,
};
assert!(render_tweet(&r).chars().count() <= 280);
let app = solwatch::report::render_rokha_app(&r);
assert_eq!(app["verdict"], "avoid");
let html = solwatch::report::render_html(&r);
assert!(html.contains("__AUDIT"));
assert!(!html.contains("</script><script>alert"));
let dir = std::env::temp_dir().join(format!("solwatch-test-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let md = solwatch::report::write_report(&r, &dir, false).unwrap();
let base = md.strip_suffix(".md").unwrap();
assert!(dir.join(&md).exists());
assert!(dir.join(format!("{base}.json")).exists());
assert!(dir.join("index.json").exists());
assert!(
!dir.join(format!("{base}.html")).exists(),
".html written without --report-html"
);
let md = solwatch::report::write_report(&r, &dir, true).unwrap();
let base = md.strip_suffix(".md").unwrap();
assert!(dir.join(format!("{base}.html")).exists());
let _ = std::fs::remove_dir_all(&dir);
}