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