f4660639ca 2026-07-31 1: use smtp2tg::utils::{
f4660639ca 2026-07-31 2: validate,
f4660639ca 2026-07-31 3: RE_CLOSING,
f4660639ca 2026-07-31 4: RE_DOMAIN,
f4660639ca 2026-07-31 5: };
f4660639ca 2026-07-31 6:
f4660639ca 2026-07-31 7: use std::{
f4660639ca 2026-07-31 8: borrow::Cow,
f4660639ca 2026-07-31 9: mem::discriminant,
f4660639ca 2026-07-31 10: };
f4660639ca 2026-07-31 11:
f4660639ca 2026-07-31 12: use stacked_errors::Result;
f4660639ca 2026-07-31 13:
f4660639ca 2026-07-31 14: #[test]
f4660639ca 2026-07-31 15: fn test_validate_escaping_behavior () -> Result<()> {
f4660639ca 2026-07-31 16: let cases: &[(&str, Cow<str>)] = &[
f4660639ca 2026-07-31 17: // `validate` escapes HTML special characters.
f4660639ca 2026-07-31 18: ("<p>Some <b>valid</b> HTML</p>", Cow::Owned("<p>Some <b>valid</b> HTML</p>".into())),
f4660639ca 2026-07-31 19: // Empty input is returned unchanged.
f4660639ca 2026-07-31 20: ("", Cow::Borrowed("")),
f4660639ca 2026-07-31 21: // Whitespace-only input needs no escaping.
f4660639ca 2026-07-31 22: (" \t\n", Cow::Borrowed(" \t\n")),
f4660639ca 2026-07-31 23: // `validate` returns `Cow<'a, str>` borrowed from its input lifetime `'a`.
f4660639ca 2026-07-31 24: // These two cases exercise both branches of that `Cow` to make sure the
f4660639ca 2026-07-31 25: // explicit lifetime introduced on `validate` still lets callers observe a
f4660639ca 2026-07-31 26: // zero-copy borrow when no escaping is required.
f4660639ca 2026-07-31 27: ("plain text without special html characters", Cow::Borrowed("plain text without special html characters")),
f4660639ca 2026-07-31 28: ("5 > 3 & 2 < 4", Cow::Owned("5 > 3 & 2 < 4".into())),
f4660639ca 2026-07-31 29: ];
f4660639ca 2026-07-31 30: for (input, expected) in cases {
f4660639ca 2026-07-31 31: let result = validate(input)?;
f4660639ca 2026-07-31 32: assert_eq!(&result, expected, "unexpected output for input {input:?}");
f4660639ca 2026-07-31 33: assert_eq!(discriminant(&result), discriminant(expected), "wrong Cow variant for input {input:?}");
f4660639ca 2026-07-31 34: }
f4660639ca 2026-07-31 35: Ok(())
f4660639ca 2026-07-31 36: }
f4660639ca 2026-07-31 37:
f4660639ca 2026-07-31 38: #[test]
f4660639ca 2026-07-31 39: fn test_validate_closing_tag_behavior () {
f4660639ca 2026-07-31 40: let cases = [
f4660639ca 2026-07-31 41: ("</ pre >", true),
f4660639ca 2026-07-31 42: ("</\tcode\t>", true),
f4660639ca 2026-07-31 43: ("</b>", false),
f4660639ca 2026-07-31 44: ("</Code>", true),
f4660639ca 2026-07-31 45: ("</code>", true),
f4660639ca 2026-07-31 46: ("</code>\t", true),
f4660639ca 2026-07-31 47: ("</code>\t>", true),
f4660639ca 2026-07-31 48: ("</div>", false), // Not a pre/code tag
f4660639ca 2026-07-31 49: ("</PRE>", true),
f4660639ca 2026-07-31 50: ("</pre>", true),
f4660639ca 2026-07-31 51: ("</pre>\n", true),
f4660639ca 2026-07-31 52: ("<p>Some <b>valid</b> HTML</p></code><a href='http://somewere.com'>Link injection!</a>", true),
f4660639ca 2026-07-31 53: ("<pre>", false),
f4660639ca 2026-07-31 54: ];
f4660639ca 2026-07-31 55: for (input, expected) in cases {
f4660639ca 2026-07-31 56: assert_eq!(RE_CLOSING.is_match(input), expected, "unexpected match result for {input:?}");
f4660639ca 2026-07-31 57: }
f4660639ca 2026-07-31 58: }
f4660639ca 2026-07-31 59:
f4660639ca 2026-07-31 60: #[test]
f4660639ca 2026-07-31 61: fn test_regex_domain_behavior() {
f4660639ca 2026-07-31 62: let cases = [
f4660639ca 2026-07-31 63: ("", false),
f4660639ca 2026-07-31 64: ("-example.com", false),
f4660639ca 2026-07-31 65: (".example.com", false),
f4660639ca 2026-07-31 66: ("123.456", true),
f4660639ca 2026-07-31 67: ("EXAMPLE.COM", false),
f4660639ca 2026-07-31 68: ("a", true),
f4660639ca 2026-07-31 69: ("a.b", true),
f4660639ca 2026-07-31 70: ("example-.com", false),
f4660639ca 2026-07-31 71: ("example..com", false),
f4660639ca 2026-07-31 72: ("example.com", true),
f4660639ca 2026-07-31 73: ("example.com.", false),
f4660639ca 2026-07-31 74: ("invalid@domain.com", false),
f4660639ca 2026-07-31 75: ("my-host.example.com", true),
f4660639ca 2026-07-31 76: ("sub.example.co.uk", true),
f4660639ca 2026-07-31 77: ];
f4660639ca 2026-07-31 78: for (input, expected) in cases {
f4660639ca 2026-07-31 79: assert_eq!(RE_DOMAIN.is_match(input), expected, "unexpected match result for {input:?}");
f4660639ca 2026-07-31 80: }
f4660639ca 2026-07-31 81: }