aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test-org-roam-config-tag-and-find.el167
1 files changed, 167 insertions, 0 deletions
diff --git a/tests/test-org-roam-config-tag-and-find.el b/tests/test-org-roam-config-tag-and-find.el
new file mode 100644
index 00000000..f98d8af0
--- /dev/null
+++ b/tests/test-org-roam-config-tag-and-find.el
@@ -0,0 +1,167 @@
+;;; test-org-roam-config-tag-and-find.el --- Tests for org-roam tag predicate + find wrappers -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Sibling tests cover the slug helper, the link-description helper,
+;; the demote-subtree pure function, the format-roam-node helper, and
+;; the copy-todo-to-today hook. This file fills in the tag-filter
+;; predicate, the list-notes-by-tag wrapper, the find-node command +
+;; its convenience entries, the insert-immediate wrapper, and the
+;; capture-finalize hook.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'org-roam-config)
+
+;; Top-level defvars so `let' bindings reach the dynamic variable under
+;; lexical scope (the hooks reference these globals).
+(defvar org-note-abort nil
+ "Dynamic stand-in for `org-note-abort' (org-capture isn't loaded here).")
+(defvar org-roam-capture-templates nil
+ "Dynamic stand-in for `org-roam-capture-templates'.")
+(defvar org-agenda-files nil
+ "Dynamic stand-in for `org-agenda-files' (org-agenda not loaded here).")
+(defvar org-capture-after-finalize-hook nil
+ "Dynamic stand-in for `org-capture-after-finalize-hook'.")
+
+;;; cj/org-roam-filter-by-tag
+
+(ert-deftest test-org-roam-filter-by-tag-returns-predicate ()
+ "Normal: filter returns a function."
+ (should (functionp (cj/org-roam-filter-by-tag "Topic"))))
+
+(ert-deftest test-org-roam-filter-by-tag-keeps-nodes-with-matching-tag ()
+ "Normal: predicate returns non-nil for a node whose tags contain TAG-NAME."
+ (cl-letf (((symbol-function 'org-roam-node-tags)
+ (lambda (n) (plist-get n :tags))))
+ (let ((pred (cj/org-roam-filter-by-tag "Topic")))
+ (should (funcall pred (list :tags '("Topic" "Tech")))))))
+
+(ert-deftest test-org-roam-filter-by-tag-rejects-nodes-without-tag ()
+ "Boundary: predicate returns nil when the tag isn't in the node's tag list."
+ (cl-letf (((symbol-function 'org-roam-node-tags)
+ (lambda (n) (plist-get n :tags))))
+ (let ((pred (cj/org-roam-filter-by-tag "Topic")))
+ (should-not (funcall pred (list :tags '("Recipe")))))))
+
+;;; cj/org-roam-list-notes-by-tag
+
+(ert-deftest test-org-roam-list-notes-by-tag-returns-file-paths-for-matches ()
+ "Normal: filters the node list by tag and returns each node's file path."
+ (cl-letf (((symbol-function 'org-roam-node-list)
+ (lambda () (list (list :tags '("Topic") :file "/p/a.org")
+ (list :tags '("Recipe") :file "/p/b.org")
+ (list :tags '("Topic" "Tech") :file "/p/c.org"))))
+ ((symbol-function 'org-roam-node-tags)
+ (lambda (n) (plist-get n :tags)))
+ ((symbol-function 'org-roam-node-file)
+ (lambda (n) (plist-get n :file))))
+ (should (equal (cj/org-roam-list-notes-by-tag "Topic")
+ '("/p/a.org" "/p/c.org")))))
+
+(ert-deftest test-org-roam-list-notes-by-tag-empty-when-no-matches ()
+ "Boundary: a tag with no matches returns an empty list."
+ (cl-letf (((symbol-function 'org-roam-node-list)
+ (lambda () (list (list :tags '("Recipe") :file "/p/a.org"))))
+ ((symbol-function 'org-roam-node-tags)
+ (lambda (n) (plist-get n :tags)))
+ ((symbol-function 'org-roam-node-file)
+ (lambda (n) (plist-get n :file))))
+ (should (null (cj/org-roam-list-notes-by-tag "Topic")))))
+
+;;; cj/org-roam-add-node-to-agenda-files-finalize-hook
+
+(ert-deftest test-org-roam-finalize-hook-removes-itself ()
+ "Normal: the hook removes itself after running, regardless of abort."
+ (let ((org-note-abort nil)
+ (org-agenda-files nil)
+ (org-capture-after-finalize-hook
+ (list #'cj/org-roam-add-node-to-agenda-files-finalize-hook)))
+ (cl-letf (((symbol-function 'org-capture-get)
+ (lambda (_) (current-buffer))))
+ (with-temp-buffer
+ (setq buffer-file-name "/tmp/captured.org")
+ (cj/org-roam-add-node-to-agenda-files-finalize-hook)
+ (setq buffer-file-name nil)))
+ (should-not (memq #'cj/org-roam-add-node-to-agenda-files-finalize-hook
+ org-capture-after-finalize-hook))))
+
+(ert-deftest test-org-roam-finalize-hook-adds-file-when-not-aborted ()
+ "Normal: when the capture wasn't aborted, the new file lands in
+`org-agenda-files'."
+ (let ((org-note-abort nil)
+ (org-agenda-files nil)
+ (org-capture-after-finalize-hook
+ (list #'cj/org-roam-add-node-to-agenda-files-finalize-hook)))
+ (cl-letf (((symbol-function 'org-capture-get)
+ (lambda (_) (current-buffer))))
+ (with-temp-buffer
+ (setq buffer-file-name "/tmp/captured-a.org")
+ (cj/org-roam-add-node-to-agenda-files-finalize-hook)
+ (setq buffer-file-name nil)))
+ (should (member "/tmp/captured-a.org" org-agenda-files))))
+
+(ert-deftest test-org-roam-finalize-hook-skips-on-abort ()
+ "Boundary: when the capture was aborted, the file is NOT added."
+ (let ((org-note-abort t)
+ (org-agenda-files nil)
+ (org-capture-after-finalize-hook
+ (list #'cj/org-roam-add-node-to-agenda-files-finalize-hook)))
+ (cj/org-roam-add-node-to-agenda-files-finalize-hook)
+ (should-not org-agenda-files)))
+
+;;; cj/org-roam-find-node + the two convenience wrappers
+
+(ert-deftest test-org-roam-find-node-passes-templates-and-tag-filter ()
+ "Normal: find-node calls org-roam-node-find with a filter + templates."
+ (let ((called-with nil))
+ (cl-letf (((symbol-function 'org-roam-node-find)
+ (lambda (&rest args) (setq called-with args))))
+ (cj/org-roam-find-node "Topic" "t" "/tmp/template.org"))
+ (should called-with)
+ ;; Third positional arg is the filter predicate.
+ (should (functionp (nth 2 called-with)))
+ ;; Templates keyword arg present.
+ (should (memq :templates called-with))))
+
+(ert-deftest test-org-roam-find-node-topic-delegates-to-find-node ()
+ "Normal: `find-node-topic' calls `find-node' with Topic tag + template-key 't'."
+ (let ((args nil))
+ (cl-letf (((symbol-function 'cj/org-roam-find-node)
+ (lambda (&rest a) (setq args a))))
+ (cj/org-roam-find-node-topic))
+ (should (equal (car args) "Topic"))
+ (should (equal (cadr args) "t"))))
+
+(ert-deftest test-org-roam-find-node-recipe-delegates-to-find-node ()
+ "Normal: `find-node-recipe' uses Recipe tag, 'r' key, recipes/ subdir."
+ (let ((args nil))
+ (cl-letf (((symbol-function 'cj/org-roam-find-node)
+ (lambda (&rest a) (setq args a))))
+ (cj/org-roam-find-node-recipe))
+ (should (equal (car args) "Recipe"))
+ (should (equal (cadr args) "r"))
+ ;; The 4th arg is the subdir
+ (should (equal (nth 3 args) "recipes/"))))
+
+;;; cj/org-roam-node-insert-immediate
+
+(ert-deftest test-org-roam-node-insert-immediate-rebinds-templates-and-calls-insert ()
+ "Normal: the wrapper appends :immediate-finish t to the first capture
+template and calls org-roam-node-insert."
+ (let ((seen-templates nil)
+ (called nil))
+ (cl-letf (((symbol-function 'org-roam-node-insert)
+ (lambda (&rest _)
+ (setq called t
+ seen-templates org-roam-capture-templates))))
+ (let ((org-roam-capture-templates '(("d" "default" plain "%?"))))
+ (cj/org-roam-node-insert-immediate nil)))
+ (should called)
+ (should (member :immediate-finish (car seen-templates)))))
+
+(provide 'test-org-roam-config-tag-and-find)
+;;; test-org-roam-config-tag-and-find.el ends here