aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-25 01:50:23 -0500
committerCraig Jennings <c@cjennings.net>2026-07-25 01:50:23 -0500
commit8822b0dab56ca50269771b4c9d06b78657c2b8b4 (patch)
treee318861637cdcf3281f123fe4ca01b9957d78c4e /.ai/scripts/tests
parentf7139d82173ccf9effdcdee50df22912f3f05fa0 (diff)
downloadrulesets-8822b0dab56ca50269771b4c9d06b78657c2b8b4.tar.gz
rulesets-8822b0dab56ca50269771b4c9d06b78657c2b8b4.zip
fix(lint-org): stop flagging correctly paired verbatim blocks
org-lint's invalid-block checker flags both delimiters of a paired example or src block whose body holds a heading-shaped line. Its parser reads the `** ` as a structural break and loses the open block. Every org file that documents org syntax inside a block collects two false findings. The checker is org-lint's own, not one lint-org implements, so I filtered its output instead of patching a local checker. `lo--matched-block-regions` pairs blocks by scanning lines under org's real rule (once open, only the block's own `#+end_TYPE` closes it). A finding inside a matched region gets dropped, delimiters included, since that's where org-lint reports. I scanned lines rather than asking org because org's parser is what's confused. An unterminated block never lands in a matched region, so it still reports. Three tests cover the false positives: a heading in an example block, a literal `#+end_example` inside a src block, and uppercase delimiters. Two more guard the real finding, including a file with one paired and one unterminated block that proves the suppression is per-block.
Diffstat (limited to '.ai/scripts/tests')
-rw-r--r--.ai/scripts/tests/test-lint-org.el108
1 files changed, 108 insertions, 0 deletions
diff --git a/.ai/scripts/tests/test-lint-org.el b/.ai/scripts/tests/test-lint-org.el
index d398c4c..ceee209 100644
--- a/.ai/scripts/tests/test-lint-org.el
+++ b/.ai/scripts/tests/test-lint-org.el
@@ -193,6 +193,65 @@ real suspicious-language warning here
#+end_src
")
+;; invalid-block, false-positive case — a correctly paired example block whose
+;; body holds a heading-shaped line. org's parser reads the `** ' inside the
+;; verbatim body as a structural break, loses the open block, and flags BOTH
+;; delimiters as "Possible incomplete block".
+(defconst lo-test--verbatim-heading-block "\
+* Heading
+
+#+begin_example
+** Feature Name or Topic
+Body line.
+#+end_example
+
+Trailing prose.
+")
+
+;; invalid-block, literal-delimiter case — a paired src block whose body holds
+;; a literal `#+end_example' plus a heading-shaped line. Only `#+end_src'
+;; closes a src block, so all three findings here are false.
+(defconst lo-test--literal-end-in-src "\
+* Heading
+
+#+begin_src text
+#+end_example
+** heading shaped
+#+end_src
+")
+
+;; invalid-block, uppercase-delimiter case — org accepts #+BEGIN_/#+END_ in
+;; either case, and the pre-fix script flagged both delimiters here too.
+(defconst lo-test--uppercase-verbatim-block "\
+* Heading
+
+#+BEGIN_EXAMPLE
+** heading shaped
+#+END_EXAMPLE
+")
+
+;; invalid-block, genuine case — a block that really is never closed. The
+;; suppression must not reach this one.
+(defconst lo-test--unterminated-block "\
+* Heading
+
+#+begin_example
+truly unterminated block body
+")
+
+;; A genuinely unterminated block *after* a correctly paired one — verifies the
+;; suppression is scoped per block rather than per file.
+(defconst lo-test--paired-then-unterminated "\
+* Heading
+
+#+begin_example
+** heading shaped
+#+end_example
+
+#+begin_example
+never closed
+")
+
;; Mixed fixture — each category once.
(defconst lo-test--mixed "\
* Mixed
@@ -392,6 +451,55 @@ suspicious-language judgment."
(should (= 1 suspicious))))
;;; ---------------------------------------------------------------------------
+;;; invalid-block — false positives on correctly paired verbatim blocks
+
+(ert-deftest lo-verbatim-heading-block-emits-no-invalid-block ()
+ "Normal: a paired example block containing a heading-shaped body line emits
+no invalid-block judgment. Both delimiters are flagged by org-lint because the
+parser treats the `** ' inside the verbatim body as a structural break."
+ (let* ((out (lo-test--run lo-test--verbatim-heading-block))
+ (res (plist-get out :result))
+ (judgments (lo-test--judgments (plist-get out :issues))))
+ ;; File untouched, no fixes applied — suppression only, never a rewrite.
+ (should (equal lo-test--verbatim-heading-block res))
+ (should (= 0 (plist-get out :fixes)))
+ (should-not (member 'invalid-block (lo-test--checkers judgments)))))
+
+(ert-deftest lo-literal-end-delimiter-in-src-emits-no-invalid-block ()
+ "Boundary: a paired src block whose body holds a literal `#+end_example' and
+a heading-shaped line emits no invalid-block judgment. Only `#+end_src' closes
+a src block, so the interior delimiter is body text."
+ (let* ((out (lo-test--run lo-test--literal-end-in-src))
+ (judgments (lo-test--judgments (plist-get out :issues))))
+ (should-not (member 'invalid-block (lo-test--checkers judgments)))))
+
+(ert-deftest lo-uppercase-verbatim-block-emits-no-invalid-block ()
+ "Boundary: block delimiters are case-insensitive in org, so an uppercase
+`#+BEGIN_EXAMPLE' pair is suppressed the same as a lowercase one."
+ (let* ((out (lo-test--run lo-test--uppercase-verbatim-block))
+ (judgments (lo-test--judgments (plist-get out :issues))))
+ (should-not (member 'invalid-block (lo-test--checkers judgments)))))
+
+(ert-deftest lo-unterminated-block-still-emits-invalid-block ()
+ "Error: a block that is never closed still emits its invalid-block judgment.
+This is the finding the checker exists for — the suppression must not mask it."
+ (let* ((out (lo-test--run lo-test--unterminated-block))
+ (judgments (lo-test--judgments (plist-get out :issues))))
+ (should (member 'invalid-block (lo-test--checkers judgments)))))
+
+(ert-deftest lo-invalid-block-suppression-is-scoped-per-block ()
+ "Boundary: a paired block and an unterminated block in the same file — the
+paired one is suppressed and the unterminated one still reports. Exactly one
+invalid-block judgment, and it points at the unterminated opener (line 7)."
+ (let* ((out (lo-test--run lo-test--paired-then-unterminated))
+ (judgments (lo-test--judgments (plist-get out :issues)))
+ (invalid (cl-remove-if-not
+ (lambda (i) (eq (plist-get i :checker) 'invalid-block))
+ judgments)))
+ (should (= 1 (length invalid)))
+ (should (= 7 (plist-get (car invalid) :line)))))
+
+;;; ---------------------------------------------------------------------------
;;; --check mode
(ert-deftest lo-check-mode-does-not-modify-file ()