summaryrefslogtreecommitdiff
path: root/tests/test-org-contacts-capture-finalize.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-03 16:15:24 -0600
committerCraig Jennings <c@cjennings.net>2025-11-03 16:15:24 -0600
commitd7cc8638562daceb48aa8895cde4e057d3c25f6b (patch)
treeb6bf1d3fde9085d90b226ef8c8f12639f86f11fe /tests/test-org-contacts-capture-finalize.el
parentaea61b34693f164ced731cf3b0f0e8ee798c342c (diff)
test: Add unit tests for music config and org-contacts functions
Add comprehensive unit tests for various Emacs Lisp functions, covering M3U file handling, music file validation, and contact capture template finalization: - Tests for appending tracks to M3U files Tests for recursive music - collection Tests for completion table creation Tests for - extracting M3U basenames and files Tests for M3U file parsing - Tests for filename sanitization Tests for directory and file - validity checks Tests for org-contacts capture template - finalization Each test validates normal, boundary, and error conditions. These tests improve code reliability by verifying expected behavior across a range of scenarios.
Diffstat (limited to 'tests/test-org-contacts-capture-finalize.el')
-rw-r--r--tests/test-org-contacts-capture-finalize.el217
1 files changed, 217 insertions, 0 deletions
diff --git a/tests/test-org-contacts-capture-finalize.el b/tests/test-org-contacts-capture-finalize.el
new file mode 100644
index 00000000..d379a912
--- /dev/null
+++ b/tests/test-org-contacts-capture-finalize.el
@@ -0,0 +1,217 @@
+;;; test-org-contacts-capture-finalize.el --- Tests for org-contacts capture template finalization -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 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.
+
+;;; Commentary:
+
+;; Unit tests for the org-contacts capture template finalization function
+;; that automatically inserts birthday timestamps.
+
+;;; Code:
+
+;; Initialize package system for batch mode
+(when noninteractive
+ (package-initialize))
+
+(require 'ert)
+(require 'org)
+
+;; Define the function to test (copied from org-contacts-config.el)
+(defun cj/org-contacts-finalize-birthday-timestamp ()
+ "Add yearly repeating timestamp after properties drawer if BIRTHDAY is set.
+This function is called during `org-capture' finalization to automatically
+insert a plain timestamp for birthdays, enabling them to appear in org-agenda
+without requiring org-contacts to be loaded in the async subprocess."
+ (when (string= (plist-get org-capture-plist :key) "C")
+ (save-excursion
+ (goto-char (point-min))
+ ;; Find the properties drawer
+ (when (re-search-forward "^:PROPERTIES:" nil t)
+ (let ((drawer-start (point))
+ (drawer-end (save-excursion
+ (when (re-search-forward "^:END:" nil t)
+ (point)))))
+ (when drawer-end
+ ;; Get BIRTHDAY property value
+ (goto-char drawer-start)
+ (when (re-search-forward "^:BIRTHDAY:[ \t]*\\(.+\\)$" drawer-end t)
+ (let ((birthday-value (string-trim (match-string 1))))
+ ;; Only process non-empty birthdays
+ (when (and birthday-value
+ (not (string-blank-p birthday-value)))
+ ;; Parse birthday and create timestamp
+ (let* ((parsed (cond
+ ;; Format: YYYY-MM-DD
+ ((string-match "^\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)$" birthday-value)
+ (list (string-to-number (match-string 1 birthday-value))
+ (string-to-number (match-string 2 birthday-value))
+ (string-to-number (match-string 3 birthday-value))))
+ ;; Format: MM-DD
+ ((string-match "^\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)$" birthday-value)
+ (list nil
+ (string-to-number (match-string 1 birthday-value))
+ (string-to-number (match-string 2 birthday-value))))
+ (t nil)))
+ (year (when parsed (or (nth 0 parsed) (nth 5 (decode-time)))))
+ (month (when parsed (nth 1 parsed)))
+ (day (when parsed (nth 2 parsed))))
+ (when (and year month day)
+ ;; Create timestamp
+ (let* ((time (encode-time 0 0 0 day month year))
+ (dow (format-time-string "%a" time))
+ (date-str (format "%04d-%02d-%02d" year month day))
+ (timestamp (format "<%s %s +1y>" date-str dow)))
+ ;; Insert after :END: if not already present
+ (goto-char drawer-end)
+ (let ((heading-end (save-excursion (outline-next-heading) (point))))
+ (unless (re-search-forward "<[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}[^>]*\\+1y>" heading-end t)
+ (goto-char drawer-end)
+ (end-of-line)
+ (insert "\n" timestamp)))))))))))))
+
+;;; Tests for birthday timestamp finalization
+
+(ert-deftest test-contacts-capture-finalize-with-full-birthday ()
+ "Test that finalize adds timestamp for YYYY-MM-DD birthday."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Alice Anderson\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":EMAIL: alice@example.com\n")
+ (insert ":BIRTHDAY: 1985-03-15\n")
+ (insert ":END:\n")
+ (insert "Added: [2025-11-01 Fri 20:30]\n")
+
+ ;; Simulate capture context
+ (let ((org-capture-plist '(:key "C")))
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ (let ((content (buffer-string)))
+ ;; Should have birthday timestamp
+ (should (string-match-p "<1985-03-15 [A-Za-z]\\{3\\} \\+1y>" content))
+ ;; Timestamp should be after :END:
+ (should (string-match-p ":END:\n<1985-03-15" content))))))
+
+(ert-deftest test-contacts-capture-finalize-with-partial-birthday ()
+ "Test that finalize adds timestamp for MM-DD birthday with current year."
+ (let ((current-year (nth 5 (decode-time))))
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Bob Baker\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":BIRTHDAY: 07-04\n")
+ (insert ":END:\n")
+
+ (let ((org-capture-plist '(:key "C")))
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ (let ((content (buffer-string)))
+ ;; Should have birthday timestamp with current year
+ (should (string-match-p (format "<%d-07-04 [A-Za-z]\\{3\\} \\+1y>" current-year) content)))))))
+
+(ert-deftest test-contacts-capture-finalize-without-birthday ()
+ "Test that finalize does nothing when no birthday property."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Carol Chen\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":EMAIL: carol@example.com\n")
+ (insert ":END:\n")
+
+ (let ((original-content (buffer-string))
+ (org-capture-plist '(:key "C")))
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ ;; Content should be unchanged
+ (should (string= (buffer-string) original-content))
+ ;; Should have no timestamp
+ (should-not (string-match-p "<[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" (buffer-string))))))
+
+(ert-deftest test-contacts-capture-finalize-with-empty-birthday ()
+ "Test that finalize skips empty birthday values."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* David Davis\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":BIRTHDAY: \n")
+ (insert ":END:\n")
+
+ (let ((original-content (buffer-string))
+ (org-capture-plist '(:key "C")))
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ ;; Content should be unchanged
+ (should (string= (buffer-string) original-content))
+ ;; Should have no timestamp
+ (should-not (string-match-p "<[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" (buffer-string))))))
+
+(ert-deftest test-contacts-capture-finalize-prevents-duplicates ()
+ "Test that finalize doesn't add duplicate timestamps."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Eve Evans\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":BIRTHDAY: 2000-01-01\n")
+ (insert ":END:\n")
+ (insert "<2000-01-01 Sat +1y>\n")
+
+ (let ((org-capture-plist '(:key "C")))
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ ;; Should have exactly one timestamp
+ (should (= 1 (how-many "<2000-01-01 [A-Za-z]\\{3\\} \\+1y>" (point-min) (point-max)))))))
+
+(ert-deftest test-contacts-capture-finalize-only-for-contact-template ()
+ "Test that finalize only runs for 'C' template key."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Task with birthday property\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":BIRTHDAY: 2000-01-01\n")
+ (insert ":END:\n")
+
+ (let ((original-content (buffer-string))
+ (org-capture-plist '(:key "t"))) ; Different template key
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ ;; Content should be unchanged
+ (should (string= (buffer-string) original-content)))))
+
+(ert-deftest test-contacts-capture-finalize-preserves-existing-content ()
+ "Test that finalize preserves all existing content."
+ (with-temp-buffer
+ (org-mode)
+ (insert "* Alice Anderson\n")
+ (insert ":PROPERTIES:\n")
+ (insert ":EMAIL: alice@example.com\n")
+ (insert ":PHONE: 555-1234\n")
+ (insert ":BIRTHDAY: 1985-03-15\n")
+ (insert ":NICKNAME: Ali\n")
+ (insert ":NOTE: Met at conference\n")
+ (insert ":END:\n")
+ (insert "Added: [2025-11-01 Fri 20:30]\n")
+
+ (let ((org-capture-plist '(:key "C")))
+ (cj/org-contacts-finalize-birthday-timestamp)
+
+ (let ((content (buffer-string)))
+ ;; All properties should still be present
+ (should (string-search ":EMAIL: alice@example.com" content))
+ (should (string-search ":PHONE: 555-1234" content))
+ (should (string-search ":BIRTHDAY: 1985-03-15" content))
+ (should (string-search ":NICKNAME: Ali" content))
+ (should (string-search ":NOTE: Met at conference" content))
+ ;; Added timestamp should still be there
+ (should (string-search "Added: [2025-11-01 Fri 20:30]" content))
+ ;; Birthday timestamp should be added
+ (should (string-match-p "<1985-03-15 [A-Za-z]\\{3\\} \\+1y>" content))))))
+
+(provide 'test-org-contacts-capture-finalize)
+;;; test-org-contacts-capture-finalize.el ends here