f4660639ca 2026-07-31 arcade: use smtp2tg::utils::{
f4660639ca 2026-07-31 arcade: validate,
f4660639ca 2026-07-31 arcade: RE_CLOSING,
f4660639ca 2026-07-31 arcade: RE_DOMAIN,
f4660639ca 2026-07-31 arcade: };
f4660639ca 2026-07-31 arcade:
f4660639ca 2026-07-31 arcade: use std::{
f4660639ca 2026-07-31 arcade: borrow::Cow,
f4660639ca 2026-07-31 arcade: mem::discriminant,
f4660639ca 2026-07-31 arcade: };
f4660639ca 2026-07-31 arcade:
78b11b0319 2026-08-01 arcade: use stacked_errors::{
78b11b0319 2026-08-01 arcade: Result,
78b11b0319 2026-08-01 arcade: ensure_eq,
78b11b0319 2026-08-01 arcade: };
f4660639ca 2026-07-31 arcade:
f4660639ca 2026-07-31 arcade: #[test]
f4660639ca 2026-07-31 arcade: fn test_validate_escaping_behavior () -> Result<()> {
f4660639ca 2026-07-31 arcade: let cases: &[(&str, Cow<str>)] = &[
f4660639ca 2026-07-31 arcade: // `validate` escapes HTML special characters.
f4660639ca 2026-07-31 arcade: ("<p>Some <b>valid</b> HTML</p>", Cow::Owned("<p>Some <b>valid</b> HTML</p>".into())),
f4660639ca 2026-07-31 arcade: // Empty input is returned unchanged.
f4660639ca 2026-07-31 arcade: ("", Cow::Borrowed("")),
f4660639ca 2026-07-31 arcade: // Whitespace-only input needs no escaping.
f4660639ca 2026-07-31 arcade: (" \t\n", Cow::Borrowed(" \t\n")),
f4660639ca 2026-07-31 arcade: // `validate` returns `Cow<'a, str>` borrowed from its input lifetime `'a`.
f4660639ca 2026-07-31 arcade: // These two cases exercise both branches of that `Cow` to make sure the
f4660639ca 2026-07-31 arcade: // explicit lifetime introduced on `validate` still lets callers observe a
f4660639ca 2026-07-31 arcade: // zero-copy borrow when no escaping is required.
f4660639ca 2026-07-31 arcade: ("plain text without special html characters", Cow::Borrowed("plain text without special html characters")),
f4660639ca 2026-07-31 arcade: ("5 > 3 & 2 < 4", Cow::Owned("5 > 3 & 2 < 4".into())),
f4660639ca 2026-07-31 arcade: ];
f4660639ca 2026-07-31 arcade: for (input, expected) in cases {
f4660639ca 2026-07-31 arcade: let result = validate(input)?;
78b11b0319 2026-08-01 arcade: ensure_eq!(&result, expected, format!("unexpected output for input {input:?}"));
78b11b0319 2026-08-01 arcade: ensure_eq!(discriminant(&result), discriminant(expected), format!("wrong Cow variant for input {input:?}"));
f4660639ca 2026-07-31 arcade: }
f4660639ca 2026-07-31 arcade: Ok(())
f4660639ca 2026-07-31 arcade: }
f4660639ca 2026-07-31 arcade:
f4660639ca 2026-07-31 arcade: #[test]
78b11b0319 2026-08-01 arcade: fn test_validate_closing_tag_behavior () -> Result<()> {
f4660639ca 2026-07-31 arcade: let cases = [
f4660639ca 2026-07-31 arcade: ("</ pre >", true),
f4660639ca 2026-07-31 arcade: ("</\tcode\t>", true),
f4660639ca 2026-07-31 arcade: ("</b>", false),
f4660639ca 2026-07-31 arcade: ("</Code>", true),
f4660639ca 2026-07-31 arcade: ("</code>", true),
f4660639ca 2026-07-31 arcade: ("</code>\t", true),
f4660639ca 2026-07-31 arcade: ("</code>\t>", true),
f4660639ca 2026-07-31 arcade: ("</div>", false), // Not a pre/code tag
f4660639ca 2026-07-31 arcade: ("</PRE>", true),
f4660639ca 2026-07-31 arcade: ("</pre>", true),
f4660639ca 2026-07-31 arcade: ("</pre>\n", true),
f4660639ca 2026-07-31 arcade: ("<p>Some <b>valid</b> HTML</p></code><a href='http://somewere.com'>Link injection!</a>", true),
f4660639ca 2026-07-31 arcade: ("<pre>", false),
f4660639ca 2026-07-31 arcade: ];
f4660639ca 2026-07-31 arcade: for (input, expected) in cases {
78b11b0319 2026-08-01 arcade: ensure_eq!(RE_CLOSING.is_match(input), expected, format!("unexpected match result for {input:?}"));
f4660639ca 2026-07-31 arcade: }
78b11b0319 2026-08-01 arcade: Ok(())
f4660639ca 2026-07-31 arcade: }
f4660639ca 2026-07-31 arcade:
f4660639ca 2026-07-31 arcade: #[test]
78b11b0319 2026-08-01 arcade: fn test_regex_domain_behavior() -> Result<()> {
f4660639ca 2026-07-31 arcade: let cases = [
f4660639ca 2026-07-31 arcade: ("", false),
f4660639ca 2026-07-31 arcade: ("-example.com", false),
f4660639ca 2026-07-31 arcade: (".example.com", false),
f4660639ca 2026-07-31 arcade: ("123.456", true),
f4660639ca 2026-07-31 arcade: ("EXAMPLE.COM", false),
f4660639ca 2026-07-31 arcade: ("a", true),
f4660639ca 2026-07-31 arcade: ("a.b", true),
f4660639ca 2026-07-31 arcade: ("example-.com", false),
f4660639ca 2026-07-31 arcade: ("example..com", false),
f4660639ca 2026-07-31 arcade: ("example.com", true),
f4660639ca 2026-07-31 arcade: ("example.com.", false),
f4660639ca 2026-07-31 arcade: ("invalid@domain.com", false),
f4660639ca 2026-07-31 arcade: ("my-host.example.com", true),
f4660639ca 2026-07-31 arcade: ("sub.example.co.uk", true),
f4660639ca 2026-07-31 arcade: ];
f4660639ca 2026-07-31 arcade: for (input, expected) in cases {
78b11b0319 2026-08-01 arcade: ensure_eq!(RE_DOMAIN.is_match(input), expected, format!("unexpected match result for {input:?}"));
f4660639ca 2026-07-31 arcade: }
78b11b0319 2026-08-01 arcade: Ok(())
f4660639ca 2026-07-31 arcade: }