use crate::{Options, repair_to_string};
use serde_json::Value;
fn opts() -> Options {
Options::default()
}
fn assert_repair_eq(input: &str, expected: &str) {
let result = repair_to_string(input, &opts()).unwrap();
let result_val: Value = serde_json::from_str(&result).unwrap();
let expected_val: Value = serde_json::from_str(expected).unwrap();
assert_eq!(
result_val, expected_val,
"\nInput: {}\nGot: {}\nExpected: {}",
input, result, expected
);
}
#[test]
fn test_empty_string() {
let result = repair_to_string("", &opts()).unwrap();
assert_eq!(result, "");
}
#[test]
fn test_fenced_code_block_at_start() {
let input = r#"```json
{
"name": "John",
"age": 30
}
```"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30}"#);
}
#[test]
fn test_fenced_code_block_in_middle() {
let input = r#"Based on the information extracted, here is the filled JSON output: ```json { "a": "b" } ```"#;
assert_repair_eq(input, r#"{"a": "b"}"#);
}
#[test]
fn test_leading_text_without_fence() {
let input = r#"Here is the JSON: {"name": "John", "age": 30}"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30}"#);
}
#[test]
fn test_trailing_text() {
let input = r#"{"name": "John", "age": 30} and some more text"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30}"#);
}
#[test]
fn test_both_sides_text() {
let input = r#"leading text {"key": "value"} trailing text"#;
assert_repair_eq(input, r#"{"key": "value"}"#);
}
#[test]
fn test_unquoted_value_with_spaces() {
let input = r#"{"name": "John", "age": 30, "city": New York}"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30, "city": "New York"}"#);
}
#[test]
fn test_unquoted_single_word_value() {
let input = r#"{key: value}"#;
assert_repair_eq(input, r#"{"key": "value"}"#);
}
#[test]
fn test_incomplete_array_in_object() {
let input = r#"{foo: [}"#;
assert_repair_eq(input, r#"{"foo": []}"#);
}
#[test]
fn test_missing_value_after_colon() {
let input = r#"{a:}"#;
assert_repair_eq(input, r#"{"a": ""}"#);
}
#[test]
fn test_incomplete_object_in_array() {
let input = r#"[{]"#;
assert_repair_eq(input, r#"[]"#);
}
#[test]
fn test_mixed_quotes() {
let input = r#"{'key': 'string', 'key2': false, "key3": null, "key4": unquoted}"#;
assert_repair_eq(
input,
r#"{"key": "string", "key2": false, "key3": null, "key4": "unquoted"}"#,
);
}
#[test]
fn test_inline_block_comment() {
let input = r#"{ "key": { "key2": "value2" /* comment */ }, "key3": "value3" }"#;
assert_repair_eq(input, r#"{"key": {"key2": "value2"}, "key3": "value3"}"#);
}
#[test]
fn test_line_comments() {
let input = r#"{ "key": { "key2": "value2" // comment }, "key3": "value3" }"#;
assert_repair_eq(input, r#"{"key": {"key2": "value2"}, "key3": "value3"}"#);
}
#[test]
fn test_multiple_fenced_blocks() {
let input = r#"lorem ```json {"key":"value"} ``` ipsum ```json [1,2,3,true] ``` 42"#;
assert_repair_eq(input, r#"[{"key": "value"}, [1, 2, 3, true]]"#);
}
#[test]
fn test_ellipsis_in_arrays() {
let input = r#"[1, 2, ..., 10]"#;
assert_repair_eq(input, r#"[1, 2, 10]"#);
}
#[test]
fn test_python_keywords() {
let input = r#"{active: True, value: None, flag: False}"#;
assert_repair_eq(input, r#"{"active": true, "value": null, "flag": false}"#);
}
#[test]
fn test_trailing_commas() {
let input = r#"{"name": "John", "age": 30,}"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30}"#);
}
#[test]
fn test_missing_commas() {
let input = r#"["a" "b" "c" 1]"#;
assert_repair_eq(input, r#"["a", "b", "c", 1]"#);
}
#[test]
fn test_incomplete_arrays() {
let input = r#"[1, 2, 3,"#;
assert_repair_eq(input, r#"[1, 2, 3]"#);
}
#[test]
fn test_incomplete_objects() {
let input = r#"{"name": "John", "age": 30"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30}"#);
}
#[test]
fn test_nested_incomplete() {
let input = r#"{"key1": {"key2": [1, 2, 3"#;
assert_repair_eq(input, r#"{"key1": {"key2": [1, 2, 3]}}"#);
}
#[test]
fn test_leading_dot_numbers() {
let input = r#"{"key": .25}"#;
assert_repair_eq(input, r#"{"key": 0.25}"#);
}
#[test]
fn test_trailing_dot_numbers() {
let input = r#"{"key": 1. }"#;
assert_repair_eq(input, r#"{"key": 1.0}"#);
}
#[test]
fn test_ensure_ascii() {
let opts = Options {
ensure_ascii: true,
..Default::default()
};
let input = r#"{'test_中国人_ascii':'统一码'}"#;
let result = repair_to_string(input, &opts).unwrap();
assert!(result.contains("\\u"), "Should contain Unicode escapes");
}
#[test]
fn test_unicode_preservation() {
let input = r#"{'test_中国人':'统一码'}"#;
let result = repair_to_string(input, &opts()).unwrap();
assert!(result.contains("中国人"), "Should preserve Unicode");
assert!(result.contains("统一码"), "Should preserve Unicode");
}
#[test]
fn test_llm_explanatory_text() {
let _input = r#"Here's the JSON: {"name": "John", "age": 30}"#;
}
#[test]
fn test_llm_markdown_fenced() {
let input = r#"```json
{
"name": "John",
"age": 30
}
```"#;
assert_repair_eq(input, r#"{"name": "John", "age": 30}"#);
}
#[test]
fn test_embedded_quotes_in_strings() {
let input = r#"{"key": "v"alu"e"} key:"#;
assert_repair_eq(input, r#"{"key": "v\"alu\"e"}"#);
}
#[test]
fn test_missing_closing_quote() {
let input = r#"{"name": "John", "age": 30, "city": "New York, "gender": "male"}"#;
assert_repair_eq(
input,
r#"{"name": "John", "age": 30, "city": "New York", "gender": "male"}"#,
);
}
#[test]
fn test_markdown_links_with_urls() {
let input = r#"{"content": "[link](https://google.com)"}"#;
assert_repair_eq(input, r#"{"content": "[link](https://google.com)"}"#);
}
#[test]
fn test_html_in_json() {
let input = r#"{"html": "<h3>Passie voor techniek"?</h3>"}"#;
assert_repair_eq(input, r#"{"html": "<h3>Passie voor techniek\"?</h3>"}"#);
}