mod common;
use common::create_test_graph;
#[test]
fn test_enhanced_word_based_search() {
let (mut graph, _temp_dir) = create_test_graph();
let pod1_address = "test_pod_depth_0";
let pod2_address = "test_pod_depth_1";
let pod3_address = "test_pod_depth_2";
graph
.add_pod_entry(
"Pod at Depth 0",
pod1_address,
"scratchpad1",
"config1",
"config_scratchpad1",
0,
)
.unwrap();
graph
.add_pod_entry(
"Pod at Depth 1",
pod2_address,
"scratchpad2",
"config2",
"config_scratchpad2",
0,
)
.unwrap();
graph
.add_pod_entry(
"Pod at Depth 2",
pod3_address,
"scratchpad3",
"config3",
"config_scratchpad3",
0,
)
.unwrap();
graph.update_pod_depth(pod1_address, "config1", 0).unwrap();
graph.update_pod_depth(pod2_address, "config2", 1).unwrap();
graph.update_pod_depth(pod3_address, "config3", 2).unwrap();
let pod1_iri = format!("ant://{pod1_address}");
let pod2_iri = format!("ant://{pod2_address}");
let pod3_iri = format!("ant://{pod3_address}");
graph
.put_quad(
"ant://album1",
"ant://title",
"The Beatles Abbey Road",
Some(&pod1_iri),
)
.unwrap();
graph
.put_quad(
"ant://album2",
"ant://description",
"The Beatles recorded Abbey Road album",
Some(&pod2_iri),
)
.unwrap();
graph
.put_quad(
"ant://album3",
"ant://artist",
"The Beatles",
Some(&pod3_iri),
)
.unwrap();
let search_results = graph
.search_content("beatles abbey road", Some(10))
.unwrap();
assert!(
!search_results.is_empty(),
"Search results should not be empty"
);
let json_result: serde_json::Value = serde_json::from_str(&search_results).unwrap();
let bindings = json_result["results"]["bindings"].as_array().unwrap();
assert!(bindings.len() >= 3, "Should have at least 3 search results");
let first_match_count: i32 = bindings[0]
.get("match_count")
.and_then(|v| v.get("value"))
.and_then(|v| v.as_str())
.and_then(|s| s.parse().ok())
.unwrap_or(0);
let last_match_count: i32 = bindings[bindings.len() - 1]
.get("match_count")
.and_then(|v| v.get("value"))
.and_then(|v| v.as_str())
.and_then(|s| s.parse().ok())
.unwrap_or(0);
assert!(
first_match_count >= last_match_count,
"Results should be ordered by match count (descending): first={first_match_count}, last={last_match_count}"
);
let single_word_results = graph.search_content("beatles", Some(10)).unwrap();
let single_json: serde_json::Value = serde_json::from_str(&single_word_results).unwrap();
let single_bindings = single_json["results"]["bindings"].as_array().unwrap();
assert!(
single_bindings.len() >= 3,
"Single word search should find all Beatles references"
);
let empty_results = graph.search_content("", Some(10)).unwrap();
assert_eq!(
empty_results, "[]",
"Empty search should return empty array"
);
let no_match_results = graph.search_content("nonexistent", Some(10)).unwrap();
let no_match_json: serde_json::Value = serde_json::from_str(&no_match_results).unwrap();
let no_match_bindings = no_match_json["results"]["bindings"].as_array().unwrap();
assert_eq!(
no_match_bindings.len(),
0,
"Search with no matches should return empty results"
);
}
#[test]
fn test_word_splitting_and_or_logic() {
let (mut graph, _temp_dir) = create_test_graph();
let pod_address = "test_pod_or_logic";
graph
.add_pod_entry(
"Test Pod",
pod_address,
"scratchpad",
"config",
"config_scratchpad",
0,
)
.unwrap();
let pod_iri = format!("ant://{pod_address}");
graph
.put_quad(
"ant://doc1",
"ant://title",
"The Beatles are great",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc2",
"ant://title",
"Abbey Road is an album",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc3",
"ant://title",
"Road trip to nowhere",
Some(&pod_iri),
)
.unwrap();
let search_results = graph
.search_content("beatles abbey road", Some(10))
.unwrap();
let json_result: serde_json::Value = serde_json::from_str(&search_results).unwrap();
let bindings = json_result["results"]["bindings"].as_array().unwrap();
assert_eq!(
bindings.len(),
3,
"Should find all three documents with OR logic"
);
let first_result = bindings[0]["object"]["value"].as_str().unwrap_or("");
assert!(
first_result.contains("Abbey Road"),
"First result should have most matches"
);
}
#[test]
fn test_quoted_phrase_search() {
let (mut graph, _temp_dir) = create_test_graph();
let pod_address = "test_pod_quotes";
graph
.add_pod_entry(
"Test Pod",
pod_address,
"scratchpad",
"config",
"config_scratchpad",
0,
)
.unwrap();
let pod_iri = format!("ant://{pod_address}");
graph
.put_quad(
"ant://doc1",
"ant://title",
"The Beatles Abbey Road album",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc2",
"ant://title",
"Abbey Road is great",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc3",
"ant://title",
"The Beatles recorded many albums",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc4",
"ant://title",
"Road trip with the band",
Some(&pod_iri),
)
.unwrap();
let search_results = graph
.search_content(r#"the beatles "abbey road""#, Some(10))
.unwrap();
let json_result: serde_json::Value = serde_json::from_str(&search_results).unwrap();
let bindings = json_result["results"]["bindings"].as_array().unwrap();
assert_eq!(bindings.len(), 4, "Should find exactly 4 documents");
let first_result = bindings[0]["object"]["value"].as_str().unwrap_or("");
assert!(
first_result.contains("The Beatles Abbey Road"),
"First result should be the one with most matches"
);
for binding in bindings {
let text = binding["object"]["value"]
.as_str()
.unwrap_or("")
.to_lowercase();
let has_the = text.contains("the");
let has_beatles = text.contains("beatles");
let has_abbey_road_phrase = text.contains("abbey road");
assert!(
has_the || has_beatles || has_abbey_road_phrase,
"Result '{text}' should contain at least one search term"
);
}
}
#[test]
fn test_multiple_quoted_phrases() {
let (mut graph, _temp_dir) = create_test_graph();
let pod_address = "test_pod_multi_quotes";
graph
.add_pod_entry(
"Test Pod",
pod_address,
"scratchpad",
"config",
"config_scratchpad",
0,
)
.unwrap();
let pod_iri = format!("ant://{pod_address}");
graph
.put_quad(
"ant://doc1",
"ant://title",
"The Beatles Abbey Road is a great album",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc2",
"ant://title",
"Led Zeppelin IV is also great",
Some(&pod_iri),
)
.unwrap();
graph
.put_quad(
"ant://doc3",
"ant://title",
"Abbey Road by The Beatles",
Some(&pod_iri),
)
.unwrap();
let search_results = graph
.search_content(r#""the beatles" "abbey road""#, Some(10))
.unwrap();
let json_result: serde_json::Value = serde_json::from_str(&search_results).unwrap();
let bindings = json_result["results"]["bindings"].as_array().unwrap();
assert_eq!(
bindings.len(),
2,
"Should find exactly 2 documents with both phrases"
);
for binding in bindings {
let text = binding["object"]["value"]
.as_str()
.unwrap_or("")
.to_lowercase();
assert!(
text.contains("the beatles") && text.contains("abbey road"),
"Result should contain both 'the beatles' and 'abbey road' phrases"
);
}
}
#[test]
fn test_search_term_parsing() {
let (graph, _temp_dir) = create_test_graph();
let test_cases = vec![
("simple words", vec!["simple", "words"]),
("\"quoted phrase\"", vec!["quoted phrase"]),
(
"word \"quoted phrase\" word",
vec!["word", "quoted phrase", "word"],
),
(
"\"first phrase\" \"second phrase\"",
vec!["first phrase", "second phrase"],
),
(
"before \"middle phrase\" after",
vec!["before", "middle phrase", "after"],
),
("\"unclosed quote", vec!["unclosed quote"]), ("", vec![]), (" spaced words ", vec!["spaced", "words"]), ];
for (input, expected) in test_cases {
if expected.is_empty() {
let result = graph.search_content(input, Some(1)).unwrap();
assert_eq!(
result, "[]",
"Empty search '{input}' should return empty results"
);
} else {
let result = graph.search_content(input, Some(1)).unwrap();
let _: serde_json::Value = serde_json::from_str(&result)
.unwrap_or_else(|_| panic!("Search '{input}' should return valid JSON"));
}
}
}