"""Hardening tests for scripts/faithful_restyle.py (#215).

restyle() extracts faculty-authored HTML and injects inline styles into
structural tags only, preserving text verbatim. Before this fix, it also
preserved ANY tag/attribute found by BeautifulSoup — including <script>
elements, on* event-handler attributes, and javascript:/vbscript: URLs —
because the only gate (verify_verbatim) compares tag-stripped *text*, which
never sees attribute values and (per BeautifulSoup's NavigableString type
filtering) never sees <script>/<style> element content either. If the
restyled fragment is ever rendered inline, that active content executes.

These tests assert active content is stripped from restyle() output while
the visible text guarantee (verify_verbatim) is untouched — both for
adversarial input and for ordinary faculty prose.
"""

import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))

import faithful_restyle as fr  # noqa: E402


class ActiveContentStrippedTest(unittest.TestCase):
    """Adversarial fragment: script tag + onclick handler + javascript: URL."""

    SOURCE = (
        "<p>Intro <script>evil()</script> text.</p>"
        '<p onclick="alert(1)">Real text</p>'
        '<a href="javascript:alert(1)">link</a>'
    )

    def test_script_tag_removed(self):
        out = fr.restyle(self.SOURCE)
        self.assertNotIn("<script", out.lower())
        self.assertNotIn("evil()", out)

    def test_onclick_attribute_removed(self):
        out = fr.restyle(self.SOURCE)
        self.assertNotIn("onclick", out.lower())

    def test_javascript_url_removed(self):
        out = fr.restyle(self.SOURCE)
        self.assertNotIn("javascript:", out.lower())

    def test_visible_text_preserved(self):
        out = fr.restyle(self.SOURCE)
        for txt in ("Intro", "text.", "Real text", "link"):
            self.assertIn(txt, out)

    def test_verify_verbatim_still_passes(self):
        """The fidelity gate must not regress: stripped nodes/attrs carry no
        visible text, so tag-stripped text equality is unaffected."""
        out = fr.restyle(self.SOURCE)
        self.assertTrue(fr.verify_verbatim(self.SOURCE, out))


class VbscriptAndStyleTagTest(unittest.TestCase):
    def test_vbscript_url_removed(self):
        source = '<a href="vbscript:msgbox(1)">click here</a>'
        out = fr.restyle(source)
        self.assertNotIn("vbscript:", out.lower())
        self.assertIn("click here", out)
        self.assertTrue(fr.verify_verbatim(source, out))

    def test_style_tag_removed(self):
        source = "<style>body{background:url(javascript:alert(1))}</style><p>Body text</p>"
        out = fr.restyle(source)
        self.assertNotIn("<style", out.lower())
        self.assertIn("Body text", out)
        self.assertTrue(fr.verify_verbatim(source, out))

    def test_uppercase_and_whitespace_javascript_url_removed(self):
        """Case/whitespace tricks (JavaScript:, java\\tscript:) must not bypass the filter."""
        source = '<a href="  JaVaScRiPt:alert(1)">evade</a>'
        out = fr.restyle(source)
        self.assertNotIn("javascript:", out.lower())
        self.assertIn("evade", out)


class BenignFragmentRoundTripTest(unittest.TestCase):
    """Normal faculty content must round-trip with visible text unchanged."""

    SOURCE = (
        "<h3>Assignment Overview</h3>"
        '<p>Please read <a href="https://example.com/reading?verifier=XYZ9">'
        "the assigned chapter</a> before Friday.</p>"
        "<ul><li>Bring your laptop</li><li>Review the rubric</li></ul>"
    )

    def test_benign_fragment_text_preserved(self):
        out = fr.restyle(self.SOURCE)
        for txt in (
            "Assignment Overview",
            "the assigned chapter",
            "before Friday.",
            "Bring your laptop",
            "Review the rubric",
        ):
            self.assertIn(txt, out)

    def test_benign_fragment_verify_verbatim_passes(self):
        out = fr.restyle(self.SOURCE)
        self.assertTrue(fr.verify_verbatim(self.SOURCE, out))

    def test_benign_href_and_verifier_token_preserved(self):
        out = fr.restyle(self.SOURCE)
        self.assertIn('href="https://example.com/reading?verifier=XYZ9"', out)
        self.assertIn("verifier=XYZ9", out)

    def test_benign_fragment_still_gets_structural_styles(self):
        out = fr.restyle(self.SOURCE)
        soup = fr.BeautifulSoup(out, "html.parser")
        for tag_name in ("h3", "p", "ul", "li", "a"):
            tag = soup.find(tag_name)
            self.assertIsNotNone(tag)
            self.assertTrue(tag.get("style"), f"{tag_name} missing style attr")


if __name__ == "__main__":
    unittest.main()
