From 44b7e8e42383a0e2c7024f250c2d16de6b66c652 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 24 May 2026 14:56:47 -0500 Subject: test: cover field-setter command failure branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added ERT tests that the four field setters preserve local org state when the issueUpdate mutation fails (returns :success nil): set-priority keeps the cookie, set-state keeps the TODO keyword and LINEAR-STATE drawer, set-assignee keeps the assignee drawer, set-labels keeps the labels drawer. Each test also asserts the push was attempted with the intended input, so a regression that simply stopped calling the API would still fail rather than pass silently. The commands already behave correctly — this locks it. 357 tests green. --- tests/test-pearl-assignee-labels.el | 28 ++++++++++++++++++++++++++++ tests/test-pearl-fields.el | 31 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tests/test-pearl-assignee-labels.el b/tests/test-pearl-assignee-labels.el index c70f0ca..7a4a352 100644 --- a/tests/test-pearl-assignee-labels.el +++ b/tests/test-pearl-assignee-labels.el @@ -71,6 +71,20 @@ (test-pearl--in-org "* Plain heading\nno id\n" (should-error (pearl-set-assignee "Craig") :type 'user-error))) +(ert-deftest test-pearl-set-assignee-failure-preserves-drawer () + "A failed assignee push attempts the mutation but leaves the drawer unchanged." + (let (pushed) + (test-pearl--in-org + "*** TODO Title\n:PROPERTIES:\n:LINEAR-ID: a\n:LINEAR-TEAM-ID: team-1\n:LINEAR-ASSIGNEE-ID: old\n:LINEAR-ASSIGNEE-NAME: Someone\n:END:\n" + (cl-letf (((symbol-function 'pearl--resolve-team-id) + (lambda (_kind _name _team &optional _force) "u9")) + ((symbol-function 'pearl--update-issue-async) + (lambda (_id input cb) (setq pushed input) (funcall cb '(:success nil))))) + (pearl-set-assignee "Craig") + (should (string= "u9" (cdr (assoc "assigneeId" pushed)))) + (should (string= "Someone" (org-entry-get nil "LINEAR-ASSIGNEE-NAME"))) + (should (string= "old" (org-entry-get nil "LINEAR-ASSIGNEE-ID"))))))) + ;;; set-labels (ert-deftest test-pearl-set-labels-pushes-ids-and-updates-drawer () @@ -110,5 +124,19 @@ (should-error (pearl-set-labels '("ghost")) :type 'user-error) (should-not pushed))))) +(ert-deftest test-pearl-set-labels-failure-preserves-drawer () + "A failed labels push attempts the mutation but leaves the labels drawer unchanged." + (let (pushed) + (test-pearl--in-org + "*** TODO Title\n:PROPERTIES:\n:LINEAR-ID: a\n:LINEAR-TEAM-ID: team-1\n:LINEAR-LABELS: [bug]\n:END:\n" + (cl-letf (((symbol-function 'pearl--resolve-team-id) + (lambda (_kind name _team &optional _force) + (pcase name ("p1" "l2") (_ nil)))) + ((symbol-function 'pearl--update-issue-async) + (lambda (_id input cb) (setq pushed input) (funcall cb '(:success nil))))) + (pearl-set-labels '("p1")) + (should (equal '("l2") (cdr (assoc "labelIds" pushed)))) + (should (string= "[bug]" (org-entry-get nil "LINEAR-LABELS"))))))) + (provide 'test-pearl-assignee-labels) ;;; test-pearl-assignee-labels.el ends here diff --git a/tests/test-pearl-fields.el b/tests/test-pearl-fields.el index 7322e4f..3722486 100644 --- a/tests/test-pearl-fields.el +++ b/tests/test-pearl-fields.el @@ -105,6 +105,20 @@ (test-pearl--in-org "* Plain heading\nno id\n" (should-error (pearl-set-priority "High") :type 'user-error))) +(ert-deftest test-pearl-set-priority-failure-preserves-cookie () + "A failed priority push attempts the mutation but leaves the cookie unchanged." + (let (pushed) + (test-pearl--in-org "*** TODO [#C] Title\n:PROPERTIES:\n:LINEAR-ID: a\n:END:\n" + (cl-letf (((symbol-function 'pearl--update-issue-async) + (lambda (_id input cb) (setq pushed input) (funcall cb '(:success nil))))) + (re-search-forward "Title") + (pearl-set-priority "High") + ;; the push was attempted with the intended value ... + (should (equal 2 (cdr (assoc "priority" pushed)))) + ;; ... but the local cookie is still #C, not #B. + (goto-char (point-min)) + (should (string-match-p "^\\*\\*\\* TODO \\[#C\\] Title" (thing-at-point 'line t))))))) + ;;; --set-heading-state (ert-deftest test-pearl-set-heading-state-updates-keyword-and-drawer () @@ -149,5 +163,22 @@ (test-pearl--in-org "* Plain heading\nno id\n" (should-error (pearl-set-state "Done") :type 'user-error))) +(ert-deftest test-pearl-set-state-failure-preserves-keyword-and-drawer () + "A failed state push attempts the mutation but leaves the keyword and drawer unchanged." + (let (pushed) + (test-pearl--in-org + "*** TODO [#B] Title\n:PROPERTIES:\n:LINEAR-ID: a\n:LINEAR-TEAM-ID: team-1\n:LINEAR-STATE-ID: s1\n:LINEAR-STATE-NAME: Todo\n:END:\n" + (cl-letf (((symbol-function 'pearl--team-states) + (lambda (_t) '(((id . "s1") (name . "Todo")) + ((id . "s2") (name . "In Progress"))))) + ((symbol-function 'pearl--update-issue-async) + (lambda (_id input cb) (setq pushed input) (funcall cb '(:success nil))))) + (pearl-set-state "In Progress") + (should (string= "s2" (cdr (assoc "stateId" pushed)))) + (goto-char (point-min)) + (should (string-match-p "^\\*\\*\\* TODO " (thing-at-point 'line t))) + (should (string= "Todo" (org-entry-get nil "LINEAR-STATE-NAME"))) + (should (string= "s1" (org-entry-get nil "LINEAR-STATE-ID"))))))) + (provide 'test-pearl-fields) ;;; test-pearl-fields.el ends here -- cgit v1.2.3