aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-25 21:30:34 -0500
committerCraig Jennings <c@cjennings.net>2026-05-25 21:30:34 -0500
commit1de0623168d13c51fb9a7c8a87f89030642ae4d0 (patch)
treea4edf8b91ebafe8590145488f88b47a887ca716c /tests
parent60b51e1c996f1c8f152696946b9440f26ea9a956 (diff)
downloadpearl-1de0623168d13c51fb9a7c8a87f89030642ae4d0.tar.gz
pearl-1de0623168d13c51fb9a7c8a87f89030642ae4d0.zip
refactor(save): remove the immediate-state-push org-sync machinery
The opt-in org-sync mode pushed Linear state immediately — on every TODO-keyword cycle (the org-after-todo-state-change hook) and on every buffer save (the after-save hook scanning the whole file). That's the last immediate-push path, and it contradicts the one-write-path model the rest of v2 builds, so I removed it: pearl-enable-org-sync / -disable-org-sync, pearl-org-hook-function, pearl-sync-org-to-linear, pearl-sync-current-heading-to-linear, and the now-orphaned pearl--process-heading-at-point, pearl--map-org-state-to-linear, and pearl--update-issue-state-async. This finishes the "make org-sync private" task — there was nothing left to make private. Cycling a TODO keyword now marks state dirty (reconciled at the next save) instead of pushing. Saving the buffer pushes nothing. Re-introducing an auto-push on save is the separate "Automatic sync on save" task, which would call the save engine rather than this whole-file scanner. I kept pearl--extract-org-heading-properties and pearl--get-todo-states-pattern. They're general heading/keyword readers, not org-sync-specific, and the keyword-cycle save (the keyword-derivation follow-up) needs exactly them. The obsolete sync tests are replaced by two that assert the contract: the sync commands are gone, and a keyword cycle pushes nothing.
Diffstat (limited to 'tests')
-rw-r--r--tests/test-pearl-mapping.el20
-rw-r--r--tests/test-pearl-org-parse.el35
-rw-r--r--tests/test-pearl-states.el36
-rw-r--r--tests/test-pearl-sync-hooks.el168
4 files changed, 38 insertions, 221 deletions
diff --git a/tests/test-pearl-mapping.el b/tests/test-pearl-mapping.el
index 17eff82..2a96163 100644
--- a/tests/test-pearl-mapping.el
+++ b/tests/test-pearl-mapping.el
@@ -54,22 +54,10 @@
(test-pearl--with-mapping '(("Todo" . "TODO"))
(should (string-equal "TODO" (pearl--map-linear-state-to-org nil)))))
-;;; pearl--map-org-state-to-linear
-
-(ert-deftest test-pearl-map-org-state-to-linear-mapped-returns-state ()
- "An org keyword present in the mapping returns its Linear state."
- (test-pearl--with-mapping '(("Todo" . "TODO") ("In Progress" . "DOING"))
- (should (string-equal "In Progress" (pearl--map-org-state-to-linear "DOING")))))
-
-(ert-deftest test-pearl-map-org-state-to-linear-unmapped-returns-nil ()
- "An org keyword not in the mapping returns nil."
- (test-pearl--with-mapping '(("Todo" . "TODO") ("Done" . "DONE"))
- (should (null (pearl--map-org-state-to-linear "WAITING")))))
-
-(ert-deftest test-pearl-map-org-state-to-linear-nil-returns-nil ()
- "A nil org keyword returns nil."
- (test-pearl--with-mapping '(("Todo" . "TODO"))
- (should (null (pearl--map-org-state-to-linear nil)))))
+;; pearl--map-org-state-to-linear (the keyword->state reverse map) was removed
+;; with the org-sync push path in save-model-v2; the keyword-cycle save will
+;; resolve through the team's states (the keyword-derivation task), not a flat
+;; reverse mapping.
;;; pearl--get-todo-states-pattern
diff --git a/tests/test-pearl-org-parse.el b/tests/test-pearl-org-parse.el
index 87f4a02..e0bb967 100644
--- a/tests/test-pearl-org-parse.el
+++ b/tests/test-pearl-org-parse.el
@@ -19,12 +19,10 @@
;;; Commentary:
-;; Tests for the org readers `pearl--extract-org-heading-properties' and
-;; `pearl--process-heading-at-point'. The reader uses org APIs over the
-;; LINEAR-* property drawer, so it works from anywhere in the entry, at any
-;; heading depth, and is unbothered by body text or nested sub-entries. The one
-;; network boundary reached during processing (`--update-issue-state-async') is
-;; stubbed.
+;; Tests for the org reader `pearl--extract-org-heading-properties'. It uses
+;; org APIs over the LINEAR-* property drawer, so it works from anywhere in the
+;; entry, at any heading depth, and is unbothered by body text or nested
+;; sub-entries.
;;; Code:
@@ -100,29 +98,8 @@
(goto-char (point-min))
(should (null (pearl--extract-org-heading-properties)))))
-;;; process-heading-at-point
-
-(ert-deftest test-pearl-process-heading-updates-when-complete ()
- "A complete entry triggers an async state update with the mapped state."
- (let ((captured nil))
- (cl-letf (((symbol-function 'pearl--update-issue-state-async)
- (lambda (id state team) (setq captured (list id state team)))))
- (test-pearl--in-org
- "*** IN-PROGRESS x\n:PROPERTIES:\n:LINEAR-ID: i-1\n:LINEAR-IDENTIFIER: ENG-2\n:LINEAR-TEAM-ID: t-1\n:END:\n"
- (re-search-forward "x")
- (pearl--process-heading-at-point)
- (should (equal '("i-1" "In Progress" "t-1") captured))))))
-
-(ert-deftest test-pearl-process-heading-skips-without-team ()
- "An entry missing its team id makes no API call."
- (let ((called nil))
- (cl-letf (((symbol-function 'pearl--update-issue-state-async)
- (lambda (&rest _) (setq called t))))
- (test-pearl--in-org
- "*** TODO x\n:PROPERTIES:\n:LINEAR-ID: i-1\n:LINEAR-IDENTIFIER: ENG-2\n:END:\n"
- (re-search-forward "x")
- (pearl--process-heading-at-point)
- (should-not called)))))
+;; process-heading-at-point (the org-sync per-heading state push) was removed
+;; in save-model-v2; the extractor above is retained for the keyword-cycle save.
;;; render -> parse round trip
diff --git a/tests/test-pearl-states.el b/tests/test-pearl-states.el
index 713cb78..ae8ef63 100644
--- a/tests/test-pearl-states.el
+++ b/tests/test-pearl-states.el
@@ -97,38 +97,10 @@
(pearl-update-issue-state "i-1" "Done" "team-1")
(should requested))))
-;;; pearl--update-issue-state-async -- nil-state-id guard
-
-(ert-deftest test-pearl-update-issue-state-async-nil-state-id-skips-request ()
- "The async update also short-circuits when the state name doesn't resolve."
- (let ((requested nil)
- (pearl-api-key "test-key"))
- (cl-letf (((symbol-function 'pearl--get-state-id-by-name) (lambda (_s _t) nil))
- ((symbol-function 'request) (lambda (&rest _) (setq requested t))))
- (pearl--update-issue-state-async "i-1" "Nonexistent" "team-1")
- (should-not requested))))
-
-(ert-deftest test-pearl-update-issue-state-async-success-runs ()
- "The async success handler runs on a successful update."
- (let ((pearl-api-key "test-key"))
- (cl-letf (((symbol-function 'pearl--get-state-id-by-name) (lambda (_s _t) "s2"))
- ((symbol-function 'request)
- (lambda (_url &rest args)
- (funcall (plist-get args :success)
- :data '((data (issueUpdate (success . t))))))))
- (should (progn (pearl--update-issue-state-async "i-1" "Done" "team-1") t)))))
-
-(ert-deftest test-pearl-update-issue-state-async-error-runs ()
- "The async error handler runs on a transport error."
- (let ((pearl-api-key "test-key"))
- (cl-letf (((symbol-function 'pearl--get-state-id-by-name) (lambda (_s _t) "s2"))
- ((symbol-function 'request)
- (lambda (_url &rest args)
- (funcall (plist-get args :error)
- :error-thrown "boom"
- :response (make-request-response :status-code 500)
- :data nil))))
- (should (progn (pearl--update-issue-state-async "i-1" "Done" "team-1") t)))))
+;; pearl--update-issue-state-async (the by-name state push) was removed with
+;; the org-sync path in save-model-v2. State now pushes through the save
+;; engine's pearl--save-state-field -> pearl--update-issue-async (stateId),
+;; covered in test-pearl-save.
(provide 'test-pearl-states)
;;; test-pearl-states.el ends here
diff --git a/tests/test-pearl-sync-hooks.el b/tests/test-pearl-sync-hooks.el
index 05864e7..0ee7d82 100644
--- a/tests/test-pearl-sync-hooks.el
+++ b/tests/test-pearl-sync-hooks.el
@@ -19,157 +19,37 @@
;;; Commentary:
-;; Tests for the org sync hook wiring: enable/disable add and remove
-;; buffer-local hooks; the after-save hook only fires for linear.org buffers;
-;; and per-heading sync degrades gracefully when point is before any heading.
+;; Save-model-v2 removed the opt-in org-sync mode. The after-save and
+;; TODO-cycle hooks that pushed Linear state immediately are gone -- every push
+;; now goes through the save engine (pearl-save-issue / pearl-save-all). These
+;; tests assert the removal: the sync commands no longer exist, and cycling a
+;; TODO keyword pushes nothing.
;;; Code:
(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))
(require 'cl-lib)
-;;; enable / disable
-
-(ert-deftest test-pearl-enable-org-sync-adds-buffer-local-hooks ()
- "Enabling sync adds both hook functions buffer-locally."
- (with-temp-buffer
- (pearl-enable-org-sync)
- (should (memq 'pearl-org-hook-function after-save-hook))
- (should (memq 'pearl-sync-org-to-linear org-after-todo-state-change-hook))))
-
-(ert-deftest test-pearl-disable-org-sync-removes-hooks ()
- "Disabling sync removes both hook functions."
- (with-temp-buffer
- (pearl-enable-org-sync)
- (pearl-disable-org-sync)
- (should-not (memq 'pearl-org-hook-function after-save-hook))
- (should-not (memq 'pearl-sync-org-to-linear org-after-todo-state-change-hook))))
-
-;;; org-hook-function buffer guard
-
-(ert-deftest test-pearl-org-hook-function-skips-other-buffer ()
- "The after-save hook does nothing in a buffer that isn't the configured file."
- (let ((called nil)
- (pearl-org-file-path "/tmp/linear.org"))
- (cl-letf (((symbol-function 'pearl-sync-org-to-linear) (lambda () (setq called t))))
- (with-temp-buffer
- (setq buffer-file-name "/tmp/scratch.org")
- (pearl-org-hook-function)
- (should-not called)))))
-
-(ert-deftest test-pearl-org-hook-function-syncs-configured-buffer ()
- "The after-save hook syncs when the buffer visits `pearl-org-file-path'."
- (let ((called nil)
- (pearl-org-file-path "/tmp/linear.org"))
- (cl-letf (((symbol-function 'pearl-sync-org-to-linear) (lambda () (setq called t))))
- (with-temp-buffer
- (setq buffer-file-name "/tmp/linear.org")
- (pearl-org-hook-function)
- (should called)))))
-
-(ert-deftest test-pearl-org-hook-function-honors-custom-path ()
- "A non-default `pearl-org-file-path' is what the hook matches on.
-Regression: the hook used to hardcode a \"linear.org$\" regex, so a buffer
-named linear.org fired even when the configured file was elsewhere, and a
-custom-named configured file never fired."
- (let ((called nil)
- (pearl-org-file-path "/tmp/my-linear-issues.org"))
- (cl-letf (((symbol-function 'pearl-sync-org-to-linear) (lambda () (setq called t))))
- ;; A buffer literally named linear.org must NOT fire when the configured
- ;; file is something else.
- (with-temp-buffer
- (setq buffer-file-name "/tmp/linear.org")
- (pearl-org-hook-function)
- (should-not called))
- ;; The configured custom-named file DOES fire.
- (with-temp-buffer
- (setq buffer-file-name "/tmp/my-linear-issues.org")
- (pearl-org-hook-function)
- (should called)))))
-
-(ert-deftest test-pearl-org-hook-function-matches-through-symlink ()
- "A configured path and a visited symlink to the same file match via truename.
-The hook resolves both sides with `file-truename', so a symlink to the
-configured file still syncs -- this guards the choice of truename over a raw
-string compare."
- (let ((real (make-temp-file "linear-real-" nil ".org"))
- (link (make-temp-file "linear-link-" nil ".org"))
- (called nil))
- (unwind-protect
- (progn
- (delete-file link)
- (make-symbolic-link real link)
- (let ((pearl-org-file-path real))
- (cl-letf (((symbol-function 'pearl-sync-org-to-linear)
- (lambda () (setq called t))))
- (with-temp-buffer
- (setq buffer-file-name link)
- (pearl-org-hook-function)
- (should called)))))
- (when (file-exists-p link) (delete-file link))
- (when (file-exists-p real) (delete-file real)))))
-
-(ert-deftest test-pearl-org-hook-function-nil-path-no-op ()
- "With `pearl-org-file-path' nil, the hook is a no-op and does not error."
- (let ((called nil)
- (pearl-org-file-path nil))
- (cl-letf (((symbol-function 'pearl-sync-org-to-linear) (lambda () (setq called t))))
- (with-temp-buffer
- (setq buffer-file-name "/tmp/linear.org")
- (should (progn (pearl-org-hook-function) t))
- (should-not called)))))
-
-;;; sync-current-heading-to-linear
-
-(ert-deftest test-pearl-sync-current-heading-before-first-heading-no-error ()
- "Syncing with point before the first heading degrades gracefully.
-
-Regression: `org-back-to-heading' signals \"before first heading\" in the
-preamble, which must not propagate out of the sync entry point."
- (cl-letf (((symbol-function 'pearl--process-heading-at-point) (lambda () nil)))
- (with-temp-buffer
- (insert "#+TITLE: x\n\npreamble line\n")
- (org-mode)
- (goto-char (point-min))
- (should (progn (pearl-sync-current-heading-to-linear) t)))))
-
-(ert-deftest test-pearl-sync-current-heading-processes-on-heading ()
- "Syncing from within an entry processes that heading."
- (let ((called nil))
- (cl-letf (((symbol-function 'pearl--process-heading-at-point)
- (lambda () (setq called t))))
- (with-temp-buffer
- (insert "* Top\n*** TODO x\n")
- (org-mode)
- (goto-char (point-max))
- (pearl-sync-current-heading-to-linear)
- (should called)))))
-
-;;; sync-org-to-linear dispatcher
-
-(ert-deftest test-pearl-sync-org-to-linear-org-todo-syncs-current-heading ()
- "When invoked from `org-todo', only the current heading is synced."
- (let ((called nil)
- (this-command 'org-todo))
- (cl-letf (((symbol-function 'pearl-sync-current-heading-to-linear)
- (lambda () (setq called t))))
- (pearl-sync-org-to-linear)
- (should called))))
-
-(ert-deftest test-pearl-sync-org-to-linear-otherwise-scans-whole-file ()
- "Outside `org-todo', every matching heading in the buffer is processed."
- (let ((count 0)
- (this-command 'some-other-command)
- (pearl-state-to-todo-mapping '(("Todo" . "TODO") ("Done" . "DONE")))
- (pearl-todo-states-pattern nil)
- (pearl--todo-states-pattern-source nil))
- (cl-letf (((symbol-function 'pearl--process-heading-at-point)
- (lambda () (setq count (1+ count)))))
+(ert-deftest test-pearl-org-sync-commands-removed ()
+ "The opt-in org-sync mode and its push machinery no longer exist."
+ (should-not (fboundp 'pearl-enable-org-sync))
+ (should-not (fboundp 'pearl-disable-org-sync))
+ (should-not (fboundp 'pearl-org-hook-function))
+ (should-not (fboundp 'pearl-sync-org-to-linear))
+ (should-not (fboundp 'pearl-sync-current-heading-to-linear)))
+
+(ert-deftest test-pearl-keyword-cycle-pushes-nothing ()
+ "Cycling a TODO keyword pushes nothing -- pearl wires no TODO-cycle sync hook."
+ (let ((pushed nil))
+ (cl-letf (((symbol-function 'pearl--update-issue-async)
+ (lambda (&rest _) (setq pushed t))))
(with-temp-buffer
- (insert "*** TODO a\n*** DONE b\n")
- (org-mode)
- (pearl-sync-org-to-linear)
- (should (= 2 count))))))
+ (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
+ (insert "*** TODO Title\n:PROPERTIES:\n:LINEAR-ID: a\n:END:\n")
+ (org-mode)
+ (goto-char (point-min))
+ (org-todo "DONE"))
+ (should-not pushed)))))
(provide 'test-pearl-sync-hooks)
;;; test-pearl-sync-hooks.el ends here