aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-24 14:33:21 -0500
committerCraig Jennings <c@cjennings.net>2026-05-24 14:33:21 -0500
commit5c0fa15d1d5bbe48f85f21e98bb6e91e9b9bcd85 (patch)
tree684a3e9da41d1587e36732a6aee8502bd7aaa5a9 /tests
parentc858c74aa09667fbf899f587be816e8ad5e20d55 (diff)
downloaddotemacs-5c0fa15d1d5bbe48f85f21e98bb6e91e9b9bcd85.tar.gz
dotemacs-5c0fa15d1d5bbe48f85f21e98bb6e91e9b9bcd85.zip
fix(org-roam): guard move-branch-to-roam against data loss
cj/move-org-branch-to-roam cut the subtree from the source buffer before writing the new roam file, so a failure in the demote/format/write/db-sync steps left the subtree gone from the source and not persisted anywhere — a destructive operation with no rollback. Reordered so the node file is written and verified on disk before org-cut-subtree runs; a failed write now aborts with the source untouched. Added a no-clobber guard (refuse an existing target file) and a confirmation prompt for large subtrees (>= cj/move-org-branch-confirm-lines, 30) or buffers with unsaved changes. The source buffer is deliberately left modified and undoable rather than auto-saved, so the move stays reversible. New test drives the write-failure-preserves-source invariant via an unwritable roam dir; the existing creates-roam-file test gained the confirm mock.
Diffstat (limited to 'tests')
-rw-r--r--tests/test-org-roam-config-copy-and-move.el20
1 files changed, 20 insertions, 0 deletions
diff --git a/tests/test-org-roam-config-copy-and-move.el b/tests/test-org-roam-config-copy-and-move.el
index 729afaa8..8b7c963f 100644
--- a/tests/test-org-roam-config-copy-and-move.el
+++ b/tests/test-org-roam-config-copy-and-move.el
@@ -119,6 +119,7 @@ syncs the roam db."
(lambda () "11111111-2222-3333-4444-555555555555"))
((symbol-function 'org-roam-db-sync)
(lambda (&rest _) (setq synced t)))
+ ((symbol-function 'yes-or-no-p) (lambda (&rest _) t))
((symbol-function 'message) #'ignore))
(with-temp-buffer
(org-mode)
@@ -144,6 +145,25 @@ syncs the roam db."
(should (string-match-p "^\\*\\* Sub heading" text))))))
(delete-directory roam-dir t))))
+(ert-deftest test-org-roam-move-branch-write-failure-preserves-source ()
+ "Error: if writing the roam file fails, the source subtree is NOT cut.
+The destructive cut must come after a successful write, so a failed write
+(here: an unwritable roam directory) can't lose the subtree."
+ (let ((org-roam-directory "/no/such/cj-roam-move-dir/"))
+ (cl-letf (((symbol-function 'require) (lambda (&rest _) t))
+ ((symbol-function 'org-id-new) (lambda () "id-1"))
+ ((symbol-function 'org-roam-db-sync) #'ignore)
+ ((symbol-function 'yes-or-no-p) (lambda (&rest _) t))
+ ((symbol-function 'message) #'ignore))
+ (with-temp-buffer
+ (org-mode)
+ (insert "* My Heading\n** Sub heading\nbody text\n")
+ (goto-char (point-min))
+ (ignore-errors (cj/move-org-branch-to-roam))
+ ;; The write failed before the cut, so the subtree is intact.
+ (should (string-match-p "My Heading" (buffer-string)))
+ (should (string-match-p "body text" (buffer-string)))))))
+
(ert-deftest test-org-roam-move-branch-errors-outside-heading ()
"Error: move-branch outside an org heading signals `user-error'."
(cl-letf (((symbol-function 'require) (lambda (&rest _) t)))