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