const LINE_SEPARATOR: char = '\u{2028}';
const PARAGRAPH_SEPARATOR: char = '\u{2029}';
const NUL: char = '\u{0000}';
#[test]
fn serde_json_escapes_u_2028() {
let input = format!("hello{}world", LINE_SEPARATOR);
let json = serde_json::to_string(&input).expect("serde_json serialization should succeed");
let parsed: String = serde_json::from_str(&json).expect("JSON.parse should reconstruct");
assert_eq!(
parsed, input,
"Round-trip: original must equal parsed result"
);
eprintln!(
"U+2028 serde_json output: {:?} (has \\u escape: {})",
json,
json.contains("\\u2028")
);
}
#[test]
fn serde_json_escapes_u_2029() {
let input = format!("line1{}line2", PARAGRAPH_SEPARATOR);
let json = serde_json::to_string(&input).expect("serde_json serialization should succeed");
let parsed: String = serde_json::from_str(&json).expect("JSON.parse should reconstruct");
assert_eq!(
parsed, input,
"Round-trip: original must equal parsed result"
);
eprintln!(
"U+2029 serde_json output: {:?} (has \\u escape: {})",
json,
json.contains("\\u2029")
);
}
#[test]
fn serde_json_handles_both_separators() {
let input = format!(
"a{sep1}b{sep2}c",
sep1 = LINE_SEPARATOR,
sep2 = PARAGRAPH_SEPARATOR
);
let json = serde_json::to_string(&input).expect("serialize");
let parsed: String = serde_json::from_str(&json).expect("parse");
assert_eq!(parsed, input, "Round-trip must preserve both separators");
}
#[test]
fn serde_json_escapes_nul() {
let input = format!("null{0}char", NUL);
let json = serde_json::to_string(&input).expect("serialize");
assert!(
json.contains("\\u0000"),
"NUL must be escaped as \\u0000, got: {json:?}"
);
let parsed: String = serde_json::from_str(&json).expect("parse");
assert_eq!(parsed, input, "Round-trip must preserve NUL");
}
use bao_cdp_client::bridge::eval_synthesizer::build_iife_with_args;
use serde_json::{json, Value};
#[test]
fn iife_iife_with_u_2028_args_is_safe() {
let payload = format!("hello{}world", LINE_SEPARATOR);
let expr = build_iife_with_args("return __args[0];", &[json!(payload)])
.expect("build_iife_with_args should succeed");
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(
expr.contains(&json_payload),
"JSON-encoded payload must appear in __args literal\nexpr: {expr}\njson_payload: {json_payload}"
);
let body_start = expr.find("return (function(){").unwrap();
let body_end = expr.find("}).apply(null, __args);").unwrap();
let body = &expr[body_start..body_end];
assert!(
!body.contains(&payload),
"body must not contain raw payload\nexpr: {expr}"
);
}
#[test]
fn iife_with_u_2029_args_is_safe() {
let payload = format!("line1{}line2", PARAGRAPH_SEPARATOR);
let expr = build_iife_with_args("return __args[0];", &[json!(payload)])
.expect("build_iife_with_args should succeed");
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(
expr.contains(&json_payload),
"JSON-encoded payload must appear in __args literal"
);
}
#[test]
fn iife_u_2028_at_start_of_arg() {
let payload = format!("{}rest", LINE_SEPARATOR);
let expr = build_iife_with_args("return __args[0];", &[json!(payload)]).unwrap();
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(expr.contains(&json_payload), "U+2028 at start: {expr}");
}
#[test]
fn iife_u_2028_at_end_of_arg() {
let payload = format!("prefix{}", LINE_SEPARATOR);
let expr = build_iife_with_args("return __args[0];", &[json!(payload)]).unwrap();
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(expr.contains(&json_payload), "U+2028 at end: {expr}");
}
#[test]
fn iife_pure_u_2028_arg() {
let payload = LINE_SEPARATOR.to_string();
let expr = build_iife_with_args("return __args[0];", &[json!(payload)]).unwrap();
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(
expr.contains(&json_payload),
"pure U+2028: {expr}, json: {json_payload}"
);
let parsed: String = serde_json::from_str(&json_payload).expect("parse");
assert_eq!(parsed, "\u{2028}");
}
#[test]
fn iife_nested_object_with_separators() {
let payload = json!({
"title": format!("hello{}world", LINE_SEPARATOR),
"body": format!("line1{}line2", PARAGRAPH_SEPARATOR),
"nested": {
"data": [
format!("a{}b", LINE_SEPARATOR),
format!("c{}d", PARAGRAPH_SEPARATOR),
]
}
});
let expr = build_iife_with_args("return __args[0];", &[payload.clone()]).unwrap();
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(
expr.contains(&json_payload),
"nested object with separators"
);
let parsed: Value = serde_json::from_str(&json_payload).expect("parse");
assert_eq!(parsed, payload, "Round-trip must preserve nested structure");
}
#[test]
fn iife_array_with_separators() {
let payload = json!([
format!("a{}b", LINE_SEPARATOR),
format!("c{}d", PARAGRAPH_SEPARATOR),
LINE_SEPARATOR.to_string(),
PARAGRAPH_SEPARATOR.to_string(),
]);
let expr = build_iife_with_args("return __args[0];", &[payload.clone()]).unwrap();
let json_payload = serde_json::to_string(&payload).unwrap();
assert!(expr.contains(&json_payload), "array with separators");
}
#[test]
fn boundary_char_roundtrip() {
let test_cases = vec![
LINE_SEPARATOR.to_string(),
PARAGRAPH_SEPARATOR.to_string(),
format!("{}{}", LINE_SEPARATOR, PARAGRAPH_SEPARATOR),
format!("{a}{b}{a}{b}", a = LINE_SEPARATOR, b = PARAGRAPH_SEPARATOR),
"plain text with no special chars".to_string(),
"numbers 12345 in text".to_string(),
"unicode beyond BMP: 🔥🚀🌟".to_string(),
];
for tc in &test_cases {
let json = serde_json::to_string(tc).expect("serialize");
let parsed: String = serde_json::from_str(&json).expect("parse");
assert_eq!(
&parsed, tc,
"Round-trip failed for input: {tc:?} -> json: {json:?} -> parsed: {parsed:?}"
);
}
}
#[test]
fn utf8_length_preserved() {
let inputs = vec![
format!("a{}b", LINE_SEPARATOR),
format!("a{}b", PARAGRAPH_SEPARATOR),
format!("a{}{}b", LINE_SEPARATOR, PARAGRAPH_SEPARATOR),
"plain".to_string(),
format!("{}{}{}", LINE_SEPARATOR, LINE_SEPARATOR, LINE_SEPARATOR),
];
for input in &inputs {
let json = serde_json::to_string(input).expect("serialize");
let parsed: String = serde_json::from_str(&json).expect("parse");
assert_eq!(
parsed.len(),
input.len(),
"UTF-8 byte length mismatch: input={input:?} ({} bytes) -> parsed={parsed:?} ({} bytes) via json={json:?}",
input.len(),
parsed.len(),
);
}
}