// SPDX-FileCopyrightText: 2026 The Frogue contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
//! Integration tests for ActivityPub mentions and hashtags
#[cfg(test)]
mod tests {
use frogue::markdown::render;
use frogue::mentions::{MentionRequest, ResolvedMention, parse};
#[test]
fn test_parse_mentions_simple() {
let text = "Hello @alice and @bob@example.com!";
let (mentions, hashtags) = parse(text);
assert_eq!(mentions.len(), 2);
assert_eq!(mentions[0].username, "alice");
assert!(mentions[0].host.is_none());
assert_eq!(mentions[1].username, "bob");
assert_eq!(mentions[1].host, Some("example.com".to_string()));
assert_eq!(hashtags.len(), 0);
}
#[test]
fn test_parse_hashtags_simple() {
let text = "Check out #rust and #ActivityPub!";
let (mentions, hashtags) = parse(text);
assert_eq!(mentions.len(), 0);
assert_eq!(hashtags.len(), 2);
assert_eq!(hashtags[0], "rust");
assert_eq!(hashtags[1], "ActivityPub");
}
#[test]
fn test_parse_mixed() {
let text = "Hello @alice! Check out #rust @bob@example.com #ActivityPub";
let (mentions, hashtags) = parse(text);
assert_eq!(mentions.len(), 2);
assert_eq!(hashtags.len(), 2);
}
#[test]
fn test_parse_mentions_in_code_block() {
let text = "Don't mention @alice in this code:\n```\n@bob\n```\nand `@charlie`";
let (mentions, _hashtags) = parse(text);
// Only the mention outside code should be found
assert_eq!(mentions.len(), 1);
assert_eq!(mentions[0].username, "alice");
}
#[test]
fn test_parse_hashtag_with_numbers() {
let text = "Issue #123 and tag #rust2024";
let (_mentions, hashtags) = parse(text);
// #123 is a number, not a hashtag
// #rust2024 is a valid hashtag
assert_eq!(hashtags.len(), 1);
assert_eq!(hashtags[0], "rust2024");
}
#[test]
fn test_mention_request_handle_format() {
let local = MentionRequest {
username: "alice".to_string(),
host: None,
};
assert_eq!(local.handle(), "@alice");
let remote = MentionRequest {
username: "bob".to_string(),
host: Some("example.com".to_string()),
};
assert_eq!(remote.handle(), "@bob@example.com");
}
#[test]
fn test_resolved_mention_properties() {
let mention = ResolvedMention {
handle: "@alice".to_string(),
href: "https://example.com/users/alice".to_string(),
};
assert_eq!(mention.handle, "@alice");
assert_eq!(mention.href, "https://example.com/users/alice");
}
#[test]
fn test_render_hashtags_in_html() {
let text = "Check out #rust and #ActivityPub!";
let html = render(text);
// Hashtags should be rendered as links
assert!(html.contains("#rust"));
assert!(html.contains("#ActivityPub"));
// Should contain anchor tags
assert!(html.contains("<a href="));
}
#[test]
fn test_render_mentions_in_html() {
let text = "Hello @alice and @bob@example.com!";
let html = render(text);
// Mentions should be rendered as text (actual resolution happens at AP
// level)
assert!(html.contains("@alice"));
assert!(html.contains("@bob@example.com"));
}
#[test]
fn test_render_mixed_mentions_and_hashtags() {
let text = "Hello @alice! Check out #rust @bob@example.com #ActivityPub";
let html = render(text);
assert!(html.contains("@alice"));
assert!(html.contains("@bob@example.com"));
assert!(html.contains("#rust"));
assert!(html.contains("#ActivityPub"));
}
#[test]
fn test_empty_text() {
let text = "";
let (mentions, hashtags) = parse(text);
assert_eq!(mentions.len(), 0);
assert_eq!(hashtags.len(), 0);
}
#[test]
fn test_no_mentions_or_hashtags() {
let text = "Just plain text without any mentions or hashtags.";
let (mentions, hashtags) = parse(text);
assert_eq!(mentions.len(), 0);
assert_eq!(hashtags.len(), 0);
}
}