The cure for AI slop is a 1986 aircraft manual/kit

asd-ste100/tests/test_ste_lint.py

13 KB·Raw on GitHub ↗

import importlib.util
import json
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
 
 
ROOT = Path(__file__).resolve().parents[1]
LINTER = ROOT / "scripts" / "ste-lint.py"
SPEC = importlib.util.spec_from_file_location("ste_lint", LINTER)
STE = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(STE)
 
WRAPPED = (
    "This library builds one finite state machine from the usage\n"
    "string, and it walks that machine to match the command line."
)
FRONT_MATTER = (
    "---\n"
    "title: A powerful guide\n"
    "description: Ensure the tools work\n"
    "author: The user's team\n"
    "status: published\n"
    "version: 2\n"
    "tags: [docs, guide]\n"
    "date: 2026-09-22\n"
    "---\n\n"
)
 
 
class ContractionTests(unittest.TestCase):
    def test_possessive_nouns_do_not_count_as_contractions(self):
        for text in (
            "Check the Spec's Before Hook.",
            "Read the user's guide.",
            "Read Alice’s notes.",
            "The team's plan works.",
            "Read the children's books.",
            "Check the users' files.",
            "The company's service works.",
        ):
            with self.subTest(text=text):
                self.assertEqual(STE.lint(text)["violations"]["contraction"], 0)
 
    def test_common_contractions_still_count(self):
        for word in (
            "it's", "he's", "she's", "that's", "what's", "who's", "where's",
            "when's", "why's", "how's", "there's", "here's", "let's",
            "don't", "can't", "won't", "we're", "they've", "you'll", "I'd", "I'm",
        ):
            for token in (word, word.upper(), word.replace("'", "’")):
                with self.subTest(token=token):
                    self.assertEqual(STE.lint(token)["violations"]["contraction"], 1)
 
 
class MarkdownTests(unittest.TestCase):
    def test_hard_wrap_does_not_change_the_report(self):
        expected = STE.lint(WRAPPED.replace("\n", " "))
        self.assertEqual(expected["words"], 21)
        self.assertEqual(expected["sentences"], 1)
        self.assertEqual(expected["violations"]["long_sentence(>20w)"], 1)
        for newline in ("\n", "\r\n"):
            with self.subTest(newline=newline):
                self.assertEqual(STE.lint(WRAPPED.replace("\n", newline)), expected)
 
    def test_wrapped_phrases_keep_the_same_counts(self):
        for phrase in ("in order\nto", "is\nbuilt", "has\nbeen made", "spin\nup"):
            with self.subTest(phrase=phrase):
                self.assertEqual(STE.lint(phrase), STE.lint(phrase.replace("\n", " ")))
 
    def test_blank_lines_keep_paragraphs_apart(self):
        self.assertEqual(STE.sentences("Read the guide\n\nCheck the result"),
                         ["Read the guide", "Check the result"])
 
    def test_headings_and_list_items_keep_their_boundaries(self):
        text = "## Steps\n1. Read the guide\n   before you start\n2. Check the result"
        self.assertEqual(STE.sentences(text),
                         ["Steps", "Read the guide before you start", "Check the result"])
        for marker in ("-", "*", "+", "1.", "1)"):
            with self.subTest(marker=marker):
                text = f"{marker} Read the guide\n{marker} Check the result"
                self.assertEqual(STE.sentences(text), ["Read the guide", "Check the result"])
 
    def test_table_rows_and_horizontal_rules_keep_boundaries(self):
        self.assertEqual(STE.sentences("Read this\n---\nCheck that"), ["Read this", "Check that"])
        self.assertEqual(STE.sentences("| First | Second |\n| Third | Fourth |"),
                         ["| First | Second |", "| Third | Fourth |"])
 
    def test_wrapped_blockquote_keeps_one_sentence(self):
        quoted = "> " + WRAPPED.replace("\n", "\n> ")
        self.assertEqual(STE.lint(quoted), STE.lint(WRAPPED.replace("\n", " ")))
 
    def test_real_sentence_ends_still_split(self):
        text = "Read this. Check that! Is it ready? Yes: Start here."
        self.assertEqual(len(STE.sentences(text)), 5)
 
    def test_emphasis_does_not_hide_sentence_ends(self):
        expected = STE.lint("Short. Then the next one follows.")
        for marker in ("*", "**", "***", "_", "__", "___"):
            with self.subTest(marker=marker):
                self.assertEqual(STE.lint(f"{marker}Short.{marker} Then the next one follows."), expected)
                self.assertEqual(STE.sentences(f"{marker}Short.{marker} Then the next one follows."),
                                 ["Short.", "Then the next one follows."])
 
    def test_emphasis_does_not_hide_a_long_sentence(self):
        plain = WRAPPED.replace("\n", " ") + " Read the guide."
        bold = "**" + WRAPPED + "** Read the guide."
        self.assertEqual(STE.lint(bold), STE.lint(plain))
 
    def test_nested_emphasis_keeps_sentence_ends(self):
        self.assertEqual(STE.lint("**Short. _Next._** Then read this."),
                         STE.lint("Short. Next. Then read this."))
 
    def test_emphasis_preserves_identifiers_and_unpaired_markers(self):
        self.assertEqual(STE.sentences("Read snake_case and file_name."),
                         ["Read snake_case and file_name."])
        self.assertEqual(STE.sentences("Read *this guide."), ["Read *this guide."])
 
    def test_leading_front_matter_does_not_change_either_score(self):
        for strict in (False, True):
            for ending in ("---", "..."):
                for prefix in ("", "\ufeff"):
                    with self.subTest(strict=strict, ending=ending, prefix=prefix):
                        front = FRONT_MATTER.rsplit("---", 1)[0] + ending + "\n\n"
                        text = prefix + front + "Read the guide."
                        self.assertEqual(STE.lint(text, strict=strict),
                                         STE.lint("Read the guide.", strict=strict))
 
    def test_front_matter_accepts_crlf(self):
        self.assertEqual(STE.lint((FRONT_MATTER + "Read the guide.").replace("\n", "\r\n")),
                         STE.lint("Read the guide."))
 
    def test_front_matter_only_applies_at_the_start(self):
        result = STE.lint("Read the guide.\n\n" + FRONT_MATTER + "Check the result.")
        self.assertGreater(result["violations"]["banned_word"], 0)
        self.assertGreater(result["words"], 6)
 
    def test_front_matter_is_removed_only_once(self):
        body = "---\nGreat question.\n---\nRead the guide."
        result = STE.lint("---\ntitle: Guide\n---\n" + body)
        self.assertEqual(result["words"], 5)
        self.assertEqual(result["shape"]["preamble_opener"], 1)
 
    def test_unclosed_front_matter_keeps_its_text(self):
        result = STE.lint("---\ntitle: Ensure the guide works\nRead the guide.")
        self.assertEqual(result["violations"]["banned_word"], 1)
 
    def test_fenced_code_keeps_prose_apart(self):
        source = "Read the guide\n```python\nensure = 1\n\nprovide = 2\n```\nCheck the result"
        self.assertEqual(STE.lint(source), STE.lint("Read the guide\n\nCheck the result"))
 
    def test_inline_code_stays_out_of_the_score(self):
        result = STE.lint("Read `ensure provide it's` the guide.")
        self.assertEqual(result["words"], 3)
        self.assertEqual(result["total"], 0)
 
    def test_long_paragraph_still_counts(self):
        result = STE.lint(" ".join(["Read the guide."] * 7))
        self.assertEqual(result["violations"]["long_paragraph(>6s)"], 1)
 
    def test_shape_counts_stay_separate(self):
        result = STE.lint("\n".join(f"{n}. Read the guide." for n in range(1, 7)))
        self.assertEqual(result["shape"]["action_list(>5)"], 1)
        self.assertEqual(result["total"], 0)
 
 
class RuleCompatibilityTests(unittest.TestCase):
    def test_existing_rule_counts_stay_intact(self):
        cases = (
            ("Read this; check that.", "semicolon", 1),
            ("The guide is written by Alice.", "passive_voice", 1),
            ("The valve is closed.", "passive_voice", 0),
            ("The valve is closed by Alice.", "passive_voice", 1),
            ("The team has made the guide.", "complex_tense", 1),
            ("The team is reading the guide.", "ing_main_verb", 1),
            ("Perform an analysis.", "nominalization", 1),
            ("Spin up the service.", "phrasal_verb", 1),
            ("Ensure the guide works.", "banned_word", 1),
            ("Use the powerful tool.", "marketing_adjective", 1),
            ("It is worth noting the result.", "modal_hedge", 1),
        )
        for text, rule, count in cases:
            with self.subTest(rule=rule, text=text):
                self.assertEqual(STE.lint(text)["violations"][rule], count)
 
    def test_strict_rules_and_month_exception_stay_intact(self):
        self.assertNotIn("strict_banned_word", STE.lint("You should read this.")["violations"])
        self.assertEqual(STE.lint("You should read this.", strict=True)["violations"]["strict_banned_word"], 1)
        self.assertEqual(STE.lint("Read the May guide.", strict=True)["violations"]["strict_banned_word"], 0)
 
    def test_empty_input_keeps_a_finite_score(self):
        result = STE.lint("")
        self.assertEqual(result["words"], 1)
        self.assertEqual(result["sentences"], 0)
        self.assertEqual(result["total_per100w"], 0)
 
 
class CommandLineTests(unittest.TestCase):
    def setUp(self):
        self.temp = tempfile.TemporaryDirectory(prefix="ste-input-tests-")
        self.addCleanup(self.temp.cleanup)
        self.directory = Path(self.temp.name)
 
    def write(self, name, text):
        path = self.directory / name
        path.write_text(text, encoding="utf-8")
        return path
 
    def run_lint(self, *args, text=None):
        return subprocess.run([sys.executable, str(LINTER), *map(str, args)],
                              input=text, encoding="utf-8", capture_output=True, timeout=10)
 
    def test_missing_file_does_not_stop_later_files(self):
        first = self.write("first.md", "Read the guide.")
        last = self.write("last.md", "Check the result.")
        result = self.run_lint(first, self.directory / "missing.md", last)
        self.assertEqual(result.returncode, 1)
        self.assertIn("first.md", result.stdout)
        self.assertIn("last.md", result.stdout)
        self.assertIn("missing.md", result.stderr)
        self.assertNotIn("Traceback", result.stderr)
 
    def test_directory_error_does_not_stop_later_files(self):
        last = self.write("last.md", "Check the result.")
        result = self.run_lint(self.directory, last)
        self.assertEqual(result.returncode, 1)
        self.assertIn("last.md", result.stdout)
        self.assertNotIn("Traceback", result.stderr)
 
    def test_invalid_utf8_does_not_stop_later_files(self):
        bad = self.directory / "invalid.md"
        bad.write_bytes(b"\xff\xfe")
        last = self.write("last.md", "Check the result.")
        result = self.run_lint(bad, last)
        self.assertEqual(result.returncode, 1)
        self.assertIn("invalid.md", result.stderr)
        self.assertIn("last.md", result.stdout)
        self.assertNotIn("Traceback", result.stderr)
 
    def test_unmatched_glob_fails_and_continues(self):
        last = self.write("last.md", "Check the result.")
        result = self.run_lint(self.directory / "missing-*.md", last)
        self.assertEqual(result.returncode, 1)
        self.assertIn("missing-*.md", result.stderr)
        self.assertIn("last.md", result.stdout)
 
    def test_json_output_stays_parseable_after_a_file_error(self):
        good = self.write("good.md", "Read the guide.")
        result = self.run_lint("--json", self.directory / "missing.md", good)
        self.assertEqual(result.returncode, 1)
        self.assertEqual(json.loads(result.stdout)["file"], str(good))
        self.assertIn("missing.md", result.stderr)
 
    def test_file_and_stdin_reports_match(self):
        text = FRONT_MATTER + "**Read the user’s guide.**\n\n" + WRAPPED
        path = self.write("unicode.md", text)
        file_result = self.run_lint("--json", path)
        stdin_result = self.run_lint(text=text)
        self.assertEqual(file_result.returncode, 0, file_result.stderr)
        self.assertEqual(stdin_result.returncode, 0, stdin_result.stderr)
        file_report = json.loads(file_result.stdout)
        del file_report["file"]
        self.assertEqual(file_report, json.loads(stdin_result.stdout))
 
    def test_score_and_shape_thresholds_keep_their_exit_codes(self):
        self.assertEqual(self.run_lint("--fail-over", 0, text="Read the guide.").returncode, 0)
        self.assertEqual(self.run_lint("--fail-over", 0, text=WRAPPED).returncode, 1)
        self.assertEqual(self.run_lint("--fail-shape", 0, text="Great question. Read the guide.").returncode, 1)
        result = self.run_lint("--strict", text="Read the guide—then check it.")
        self.assertEqual(json.loads(result.stdout)["violations"]["em_dash"], 1)
 
 
if __name__ == "__main__":
    unittest.main()
 

← Back to the episode