talon-core 0.4.1

Core retrieval engine for Talon: hybrid search (BM25 + semantic + reranker), indexing, and graph-aware ranking over markdown corpora.
Documentation
use super::*;

#[test]
fn test_normalize_keyword() {
    assert_eq!(normalize_keyword("Hello World"), "helloworld");
    assert_eq!(normalize_keyword("  Test  "), "test");
    assert_eq!(normalize_keyword("CAFÉ"), "cafe\u{0301}");
}

#[test]
fn test_parse_frontmatter_simple() {
    let content = "---\ntitle: Hello\nauthor: World\n---\n\nBody text.";
    let result = parse_frontmatter(content);
    assert_eq!(result.body.trim(), "Body text.");
    assert!(result.frontmatter.contains_key("title"));
    assert!(result.frontmatter.contains_key("author"));
}

#[test]
fn frontmatter_tags_skip_non_string_yaml_scalars() {
    let content = "---\ntags: [rust, 4, true, \"5\"]\n---\n\n#inline";
    let result = parse_frontmatter(content);

    assert_eq!(result.tags, vec!["rust", "5", "inline"]);
}

#[test]
fn date_like_frontmatter_string_gets_date_type() {
    let content = "---\ndue: 2026-04-29\n---\n\nBody text.";
    let result = parse_frontmatter(content);

    assert_eq!(
        result.frontmatter.get("due"),
        Some(&FrontmatterValue::Date("2026-04-29".to_string()))
    );
}

#[test]
fn parse_frontmatter_accepts_obsidian_unquoted_colon_scalars() {
    let content = "---\ntitle: Artist: Song\nstatus: draft\n---\n\nBody text.";
    let result = parse_frontmatter(content);

    assert_eq!(result.body.trim(), "Body text.");
    assert_eq!(
        result.frontmatter.get("title"),
        Some(&FrontmatterValue::String("Artist: Song".to_string()))
    );
}

#[test]
fn test_parse_frontmatter_no_frontmatter() {
    let content = "No frontmatter here.";
    let result = parse_frontmatter(content);
    assert_eq!(result.body, content);
    assert!(result.frontmatter.is_empty());
}

#[test]
fn parse_frontmatter_ignores_horizontal_rule_after_body_text() {
    let content = "# Long Note\n\nOpening section.\n\n---\n\nClosing section.";
    let result = parse_frontmatter(content);

    assert_eq!(result.body, content);
    assert!(result.frontmatter.is_empty());
}

#[test]
fn parse_frontmatter_keeps_invalid_top_delimited_body() {
    let content = "---\n# Morning notes\n\n- prep\n- service\n---\n\nLater section.";
    let result = parse_frontmatter(content);

    assert_eq!(result.body, content);
    assert!(result.frontmatter.is_empty());
}

#[test]
fn test_extract_wikilinks() {
    let content =
        "Check [[My Note]] and [[Other#section]].\n```\n[[not a link]]\n```\nAnd [[Target|alias]].";
    let links = extract_wikilinks(content);
    assert_eq!(links.len(), 3);
    assert_eq!(links[0].target, "My Note");
    assert_eq!(links[1].target, "Other");
    assert_eq!(links[1].heading, Some("section".to_string()));
    assert_eq!(links[2].target, "Target");
    assert_eq!(links[2].alias, Some("alias".to_string()));
}

#[test]
fn extract_wikilinks_skips_inline_code_spans() {
    let content = "Show syntax as `[[wikilink]]` and ``[[wikilinks]]``.\nKeep [[Real Note]].";
    let links = extract_wikilinks(content);

    assert_eq!(links.len(), 1);
    assert_eq!(links[0].target, "Real Note");
}

#[test]
fn test_extract_inline_tags() {
    let content = "Some text #hello #world and #foo.\n```\n#not a tag\n```";
    let tags = links::extract_inline_tags(content);
    assert!(tags.contains(&"hello".to_string()));
    assert!(tags.contains(&"world".to_string()));
    assert!(tags.contains(&"foo".to_string()));
}

#[test]
fn test_reverse_index() {
    let mut idx = FrontmatterReverseIndex::default();
    idx.insert("path1.md", "tag", "rust");
    idx.insert("path2.md", "tag", "rust");
    idx.insert("path3.md", "tag", "python");

    let results = idx.lookup("tag", "rust");
    assert!(results.is_some());
    assert_eq!(results.unwrap().len(), 2);

    let results = idx.lookup("tag", "python");
    assert!(results.is_some());
    assert_eq!(results.unwrap().len(), 1);
}

#[test]
fn test_reverse_source_index() {
    let mut idx = ReverseSourceIndex::default();
    idx.insert("a.md".to_string(), "b.md".to_string());
    idx.insert("c.md".to_string(), "b.md".to_string());

    let sources = idx.get("b.md");
    assert!(sources.is_some());
    assert_eq!(sources.unwrap().len(), 2);
}