blob: 44493145301f6ef3ac922c03c270916760626296 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
;;; test-pearl-fetch-all-comments.el --- Tests for fetching a full comment thread -*- lexical-binding: t; -*-
;; Copyright (C) 2026 Craig Jennings
;; Author: Craig Jennings <c@cjennings.net>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Tests for pearl-fetch-all-comments: the keybinding, the in-place
;; Comments-subtree replacement with an exact N/N count, and the unpushed-edit
;; guard that refuses to clobber a locally-edited comment.
;;; Code:
(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))
(require 'cl-lib)
(defmacro test-pearl-fac--in-org (content &rest body)
"Run BODY in an org-mode temp buffer holding CONTENT."
(declare (indent 1))
`(let ((org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE"))))
(with-temp-buffer
(insert ,content)
(org-mode)
(goto-char (point-min))
,@body)))
(defconst test-pearl-fac--with-comments
(concat
"* My open issues\n"
"** TODO SE-1: Alpha\n:PROPERTIES:\n:LINEAR-ID: i1\n:LINEAR-IDENTIFIER: SE-1\n:END:\n"
"The description body.\n"
"*** Comments 2/5+\n"
"**** Old Author — 2026-01-01T00:00:00.000Z\n"
":PROPERTIES:\n:LINEAR-COMMENT-ID: oc1\n:LINEAR-COMMENT-AUTHOR-ID: u1\n"
":LINEAR-COMMENT-SHA256: deadbeef\n:LINEAR-COMMENT-ORG-SHA256: deadbeef\n:END:\n"
"old comment one\n")
"An issue with a capped (overflowing) Comments subtree and a stale-hash comment.")
(defconst test-pearl-fac--no-comments
(concat
"* My open issues\n"
"** TODO SE-1: Alpha\n:PROPERTIES:\n:LINEAR-ID: i1\n:LINEAR-IDENTIFIER: SE-1\n:END:\n"
"The description body.\n")
"An issue with no Comments subtree.")
(defun test-pearl-fac--goto-issue ()
"Move point onto the issue heading."
(goto-char (point-min))
(re-search-forward "^\\*\\* TODO SE-1")
(beginning-of-line))
;;; keybinding
(ert-deftest test-pearl-fetch-all-comments-bound ()
"The command is bound under the fetch sub-keymap at `c'."
(should (eq 'pearl-fetch-all-comments (lookup-key pearl-fetch-map (kbd "c")))))
;;; subtree replacement
(ert-deftest test-pearl-replace-comments-subtree-replaces-and-counts ()
"Replacing swaps the capped thread for the full set with an exact N/N count."
(test-pearl-fac--in-org test-pearl-fac--with-comments
(test-pearl-fac--goto-issue)
(pearl--replace-comments-subtree
(list '(:id "c1" :author "Bob" :created-at "2026-02-01T00:00:00.000Z" :body "new one")
'(:id "c2" :author "Sue" :created-at "2026-02-02T00:00:00.000Z" :body "new two")))
(goto-char (point-min))
(should (search-forward "Comments 2/2" nil t))
(goto-char (point-min))
(should (search-forward "The description body." nil t))
(goto-char (point-min))
(should (search-forward "new one" nil t))
(goto-char (point-min))
(should-not (search-forward "old comment one" nil t))))
(ert-deftest test-pearl-replace-comments-subtree-creates-when-absent ()
"An issue with no Comments subtree gets one inserted."
(test-pearl-fac--in-org test-pearl-fac--no-comments
(test-pearl-fac--goto-issue)
(pearl--replace-comments-subtree
(list '(:id "c1" :author "Bob" :created-at "2026-02-01T00:00:00.000Z" :body "hello")))
(goto-char (point-min))
(should (search-forward "Comments 1/1" nil t))
(goto-char (point-min))
(should (search-forward "hello" nil t))
(goto-char (point-min))
(should (search-forward "The description body." nil t))))
(ert-deftest test-pearl-replace-comments-subtree-empty-clears ()
"An empty thread clears the Comments subtree."
(test-pearl-fac--in-org test-pearl-fac--with-comments
(test-pearl-fac--goto-issue)
(pearl--replace-comments-subtree nil)
(goto-char (point-min))
(should-not (search-forward "Comments" nil t))
(goto-char (point-min))
(should (search-forward "The description body." nil t))))
;;; unpushed-edit guard
(ert-deftest test-pearl-unpushed-comment-edits-p-detects-dirty ()
"A comment whose body no longer matches its stored hash is an unpushed edit."
(test-pearl-fac--in-org test-pearl-fac--with-comments
(test-pearl-fac--goto-issue)
(should (pearl--unpushed-comment-edits-p))))
(ert-deftest test-pearl-unpushed-comment-edits-p-clean-when-no-comments ()
"An issue with no comments has no unpushed comment edits."
(test-pearl-fac--in-org test-pearl-fac--no-comments
(test-pearl-fac--goto-issue)
(should-not (pearl--unpushed-comment-edits-p))))
;;; save-after-fetch helper
(ert-deftest test-pearl-save-if-visiting-noop-without-file ()
"In a non-file buffer the save helper is a no-op, not an error."
(with-temp-buffer
(insert "x")
(should-not (pearl--save-if-visiting))))
(ert-deftest test-pearl-save-if-visiting-saves-file-buffer ()
"In a file-visiting buffer the helper writes the buffer to disk."
(let ((f (make-temp-file "pearl-save-" nil ".org")))
(unwind-protect
(let ((buf (find-file-noselect f)))
(unwind-protect
(with-current-buffer buf
(insert "hello pearl")
(should (buffer-modified-p))
(pearl--save-if-visiting)
(should-not (buffer-modified-p))
(should (string-match-p
"hello pearl"
(with-temp-buffer (insert-file-contents f) (buffer-string)))))
(kill-buffer buf)))
(delete-file f))))
(provide 'test-pearl-fetch-all-comments)
;;; test-pearl-fetch-all-comments.el ends here
|