aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pearl.el32
-rw-r--r--tests/test-pearl-bugfixes.el25
2 files changed, 45 insertions, 12 deletions
diff --git a/pearl.el b/pearl.el
index 8adca82..8b4ebc7 100644
--- a/pearl.el
+++ b/pearl.el
@@ -2418,13 +2418,18 @@ proceeds and the subtree is replaced with Linear's version."
(message "Issue %s is no longer on Linear (deleted or no access); not refreshing"
issue-id))
(raw
- (save-excursion
- (goto-char marker)
- (pearl--replace-issue-subtree-at-point
- (pearl--normalize-issue raw)))
- (pearl-highlight-comments)
- (pearl--surface-buffer (marker-buffer marker))
- (message "Refreshed %s from Linear" issue-id)))))))))
+ ;; The fetch may complete after the buffer was killed; only
+ ;; rewrite it when it's still live.
+ (if (buffer-live-p (marker-buffer marker))
+ (progn
+ (save-excursion
+ (goto-char marker)
+ (pearl--replace-issue-subtree-at-point
+ (pearl--normalize-issue raw)))
+ (pearl-highlight-comments)
+ (pearl--surface-buffer (marker-buffer marker))
+ (message "Refreshed %s from Linear" issue-id))
+ (message "Refreshed %s; its buffer was closed" issue-id))))))))))
(defun pearl--create-comment-async (issue-id body callback)
"Create a comment with BODY on ISSUE-ID via commentCreate.
@@ -2657,12 +2662,15 @@ tail of the field-setter commands (priority/state/assignee/labels)."
issue-id fields
(lambda (result)
(if (plist-get result :success)
+ ;; The push may complete after the buffer was killed; only touch it
+ ;; when it's still live, but still report the (remote) success.
(progn
- (save-excursion
- (goto-char marker)
- (funcall on-success))
- (message "%s" success-message)
- (pearl--surface-buffer (marker-buffer marker)))
+ (when (buffer-live-p (marker-buffer marker))
+ (save-excursion
+ (goto-char marker)
+ (funcall on-success))
+ (pearl--surface-buffer (marker-buffer marker)))
+ (message "%s" success-message))
(message "%s" fail-message)))))
(defun pearl-set-priority (priority-name)
diff --git a/tests/test-pearl-bugfixes.el b/tests/test-pearl-bugfixes.el
index 7f5c635..caab884 100644
--- a/tests/test-pearl-bugfixes.el
+++ b/tests/test-pearl-bugfixes.el
@@ -151,5 +151,30 @@
(should-error (pearl-run-saved-query "bad") :type 'user-error)
(should-not fetched))))
+;;; Bug: async issue callbacks must be harmless if the buffer is killed
+
+(ert-deftest test-pearl-push-issue-field-killed-buffer-no-signal ()
+ "A field push whose buffer is killed before the success callback does not signal."
+ (let ((buf (generate-new-buffer "pearl-pif-kill")))
+ (unwind-protect
+ (let (marker)
+ (with-current-buffer buf
+ (insert "* h\n:PROPERTIES:\n:LINEAR-ID: a\n:END:\nbody\n")
+ (org-mode)
+ (goto-char (point-min))
+ (setq marker (point-marker)))
+ ;; The update stub kills the buffer, then reports success; the success
+ ;; callback must not run its buffer ops (or signal) on a dead buffer.
+ (cl-letf (((symbol-function 'pearl--update-issue-async)
+ (lambda (_id _fields cb) (kill-buffer buf) (funcall cb '(:success t)))))
+ ;; Must complete without signaling; the on-success thunk (which would
+ ;; error) must not run because the buffer is dead.
+ (pearl--push-issue-field
+ "a" marker '(("priority" . 2))
+ (lambda () (error "on-success must not run against a dead buffer"))
+ "ok" "fail")
+ (should-not (buffer-live-p buf))))
+ (when (buffer-live-p buf) (kill-buffer buf)))))
+
(provide 'test-pearl-bugfixes)
;;; test-pearl-bugfixes.el ends here