aboutsummaryrefslogtreecommitdiff
path: root/tests/test-chime-sanitize-title.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-18 11:13:39 -0600
committerCraig Jennings <c@cjennings.net>2025-11-18 11:13:39 -0600
commit4835fadabf243b33fb78557e45428055675e7300 (patch)
tree2e8ccd7995ffa6f6dd99943d829fb8b7e3112874 /tests/test-chime-sanitize-title.el
downloadchime-4835fadabf243b33fb78557e45428055675e7300.tar.gz
chime-4835fadabf243b33fb78557e45428055675e7300.zip
changed repositories
Diffstat (limited to 'tests/test-chime-sanitize-title.el')
-rw-r--r--tests/test-chime-sanitize-title.el402
1 files changed, 402 insertions, 0 deletions
diff --git a/tests/test-chime-sanitize-title.el b/tests/test-chime-sanitize-title.el
new file mode 100644
index 0000000..8329abd
--- /dev/null
+++ b/tests/test-chime-sanitize-title.el
@@ -0,0 +1,402 @@
+;;; test-chime-sanitize-title.el --- Tests for chime--sanitize-title -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024 Craig Jennings
+
+;; Author: Craig Jennings <c@cjennings.net>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for chime--sanitize-title function.
+;; Tests cover:
+;; - Unmatched opening delimiters (parentheses, brackets, braces)
+;; - Unmatched closing delimiters
+;; - Mixed unmatched delimiters
+;; - Already balanced delimiters (no-op)
+;; - Nil and empty strings
+;; - Real-world bug cases that triggered the issue
+
+;;; Code:
+
+;; Initialize package system for batch mode
+(when noninteractive
+ (package-initialize))
+
+(require 'ert)
+
+;; Load dependencies required by chime
+(require 'dash)
+(require 'alert)
+(require 'async)
+(require 'org-agenda)
+
+;; Load chime from parent directory
+(load (expand-file-name "../chime.el") nil t)
+
+;; Load test utilities
+(require 'testutil-general (expand-file-name "testutil-general.el"))
+(require 'testutil-time (expand-file-name "testutil-time.el"))
+
+;;; Setup and Teardown
+
+(defun test-chime-sanitize-title-setup ()
+ "Setup function run before each test."
+ (chime-create-test-base-dir))
+
+(defun test-chime-sanitize-title-teardown ()
+ "Teardown function run after each test."
+ (chime-delete-test-base-dir))
+
+;;; Normal Cases - Already Balanced
+
+(ert-deftest test-chime-sanitize-title-balanced-parens-unchanged ()
+ "Test that balanced parentheses are unchanged.
+
+REFACTORED: No timestamps used"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting (Team Sync)")
+ (result (chime--sanitize-title title)))
+ (should (string-equal title result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-balanced-brackets-unchanged ()
+ "Test that balanced brackets are unchanged.
+
+REFACTORED: No timestamps used"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Review [PR #123]")
+ (result (chime--sanitize-title title)))
+ (should (string-equal title result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-balanced-braces-unchanged ()
+ "Test that balanced braces are unchanged.
+
+REFACTORED: No timestamps used"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Code Review {urgent}")
+ (result (chime--sanitize-title title)))
+ (should (string-equal title result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-mixed-balanced-unchanged ()
+ "Test that mixed balanced delimiters are unchanged.
+
+REFACTORED: No timestamps used"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting [Team] (Sync) {Urgent}")
+ (result (chime--sanitize-title title)))
+ (should (string-equal title result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-nested-balanced-unchanged ()
+ "Test that nested balanced delimiters are unchanged.
+
+REFACTORED: No timestamps used"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Review (PR [#123] {urgent})")
+ (result (chime--sanitize-title title)))
+ (should (string-equal title result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-no-delimiters-unchanged ()
+ "Test that titles without delimiters are unchanged.
+
+REFACTORED: No timestamps used"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Simple Meeting Title")
+ (result (chime--sanitize-title title)))
+ (should (string-equal title result)))
+ (test-chime-sanitize-title-teardown)))
+
+;;; Unmatched Opening Delimiters
+
+(ert-deftest test-chime-sanitize-title-unmatched-opening-paren ()
+ "Test that unmatched opening parenthesis is closed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "CTO/COO XLT (Extended Leadership")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "CTO/COO XLT (Extended Leadership)" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-unmatched-opening-paren-at-end ()
+ "Test that unmatched opening parenthesis at end is closed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Spice Cake (")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Spice Cake ()" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-multiple-unmatched-opening-parens ()
+ "Test that multiple unmatched opening parentheses are closed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting (Team (Sync")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Meeting (Team (Sync))" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-unmatched-opening-bracket ()
+ "Test that unmatched opening bracket is closed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Review [PR #123")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Review [PR #123]" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-unmatched-opening-brace ()
+ "Test that unmatched opening brace is closed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Code Review {urgent")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Code Review {urgent}" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-mixed-unmatched-opening-delimiters ()
+ "Test that mixed unmatched opening delimiters are all closed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting [Team (Sync {Urgent")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Meeting [Team (Sync {Urgent})]" result)))
+ (test-chime-sanitize-title-teardown)))
+
+;;; Unmatched Closing Delimiters
+
+(ert-deftest test-chime-sanitize-title-unmatched-closing-paren ()
+ "Test that unmatched closing parenthesis is removed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting Title)")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Meeting Title" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-multiple-unmatched-closing-parens ()
+ "Test that multiple unmatched closing parentheses are removed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting Title))")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Meeting Title" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-unmatched-closing-bracket ()
+ "Test that unmatched closing bracket is removed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Review PR]")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Review PR" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-unmatched-closing-brace ()
+ "Test that unmatched closing brace is removed."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Code Review}")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Code Review" result)))
+ (test-chime-sanitize-title-teardown)))
+
+;;; Complex Mixed Cases
+
+(ert-deftest test-chime-sanitize-title-opening-and-closing-mixed ()
+ "Test title with both unmatched opening and closing delimiters."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting (Team) Extra)")
+ (result (chime--sanitize-title title)))
+ ;; Should remove the extra closing paren
+ (should (string-equal "Meeting (Team) Extra" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-complex-nesting-with-unmatched ()
+ "Test complex nested delimiters with some unmatched."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting [Team (Sync] Extra")
+ (result (chime--sanitize-title title)))
+ ;; The ']' doesn't match the '[' (because '(' is in between)
+ ;; So it's removed, and we close the '(' and '[' properly: ')' and ']'
+ (should (string-equal "Meeting [Team (Sync Extra)]" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-all-types-unmatched ()
+ "Test with all three delimiter types unmatched."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting (Team [Project {Status")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Meeting (Team [Project {Status}])" result)))
+ (test-chime-sanitize-title-teardown)))
+
+;;; Edge Cases
+
+(ert-deftest test-chime-sanitize-title-nil-returns-empty-string ()
+ "Test that nil title returns empty string."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((result (chime--sanitize-title nil)))
+ (should (string-equal "" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-empty-string-unchanged ()
+ "Test that empty string is unchanged."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-only-opening-delimiters ()
+ "Test title with only opening delimiters."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "([{")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "([{}])" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-only-closing-delimiters ()
+ "Test title with only closing delimiters."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title ")]}")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-very-long-title-with-unmatched ()
+ "Test very long title with unmatched delimiter."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "This is a very long meeting title that contains many words and might wrap in the notification display (Extended Info")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "This is a very long meeting title that contains many words and might wrap in the notification display (Extended Info)" result)))
+ (test-chime-sanitize-title-teardown)))
+
+;;; Real-World Bug Cases
+
+(ert-deftest test-chime-sanitize-title-bug-case-extended-leadership ()
+ "Test the actual bug case from vineti.meetings.org."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "1:01pm CTO/COO XLT (Extended Leadership")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "1:01pm CTO/COO XLT (Extended Leadership)" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-bug-case-spice-cake ()
+ "Test the actual bug case from journal/2023-11-22.org."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Spice Cake (")
+ (result (chime--sanitize-title title)))
+ (should (string-equal "Spice Cake ()" result)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-lisp-serialization-safety ()
+ "Test that sanitized title can be safely read by Lisp reader."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((title "Meeting (Team Sync")
+ (sanitized (chime--sanitize-title title))
+ ;; Simulate what happens in async serialization
+ (serialized (format "'((title . \"%s\"))" sanitized)))
+ ;; This should not signal an error
+ (should (listp (read serialized)))
+ (should (string-equal "Meeting (Team Sync)" sanitized)))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-sanitize-title-async-serialization-with-unmatched-parens ()
+ "Test that titles with unmatched parens won't break async serialization."
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((problematic-titles '("Meeting (Team"
+ "Review [PR"
+ "Code {Status"
+ "Event (("
+ "Task ))")))
+ (dolist (title problematic-titles)
+ (let* ((sanitized (chime--sanitize-title title))
+ (serialized (format "'((title . \"%s\"))" sanitized)))
+ ;; Should not signal 'invalid-read-syntax error
+ (should (listp (read serialized))))))
+ (test-chime-sanitize-title-teardown)))
+
+;;; Integration with chime--extract-title
+
+(ert-deftest test-chime-extract-title-sanitizes-output ()
+ "Test that chime--extract-title applies sanitization.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 0))
+ (timestamp (test-timestamp-string time))
+ (test-file (chime-create-temp-test-file-with-content
+ (format "* TODO Meeting (Team Sync\n%s\n" timestamp)))
+ (test-buffer (find-file-noselect test-file)))
+ (with-current-buffer test-buffer
+ (org-mode) ; Enable org-mode
+ (goto-char (point-min))
+ ;; Search for the heading
+ (re-search-forward "^\\* TODO" nil t)
+ (beginning-of-line)
+ (let* ((marker (point-marker))
+ (title (chime--extract-title marker)))
+ ;; Should be sanitized with closing paren added
+ (should (string-equal "Meeting (Team Sync)" title))))
+ (kill-buffer test-buffer))
+ (test-chime-sanitize-title-teardown)))
+
+(ert-deftest test-chime-extract-title-handles-nil ()
+ "Test that chime--extract-title handles nil gracefully.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-sanitize-title-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 0))
+ (timestamp (test-timestamp-string time))
+ (test-file (chime-create-temp-test-file-with-content
+ (format "* TODO\n%s\n" timestamp)))
+ (test-buffer (find-file-noselect test-file)))
+ (with-current-buffer test-buffer
+ (org-mode) ; Enable org-mode
+ (goto-char (point-min))
+ ;; Search for the heading
+ (re-search-forward "^\\* TODO" nil t)
+ (beginning-of-line)
+ (let* ((marker (point-marker))
+ (title (chime--extract-title marker)))
+ ;; Should return empty string for nil title
+ (should (string-equal "" title))))
+ (kill-buffer test-buffer))
+ (test-chime-sanitize-title-teardown)))
+
+(provide 'test-chime-sanitize-title)
+;;; test-chime-sanitize-title.el ends here