aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pearl-single-issue.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-pearl-single-issue.el')
-rw-r--r--tests/test-pearl-single-issue.el117
1 files changed, 117 insertions, 0 deletions
diff --git a/tests/test-pearl-single-issue.el b/tests/test-pearl-single-issue.el
new file mode 100644
index 0000000..a686ea5
--- /dev/null
+++ b/tests/test-pearl-single-issue.el
@@ -0,0 +1,117 @@
+;;; test-pearl-single-issue.el --- Tests for the single-issue source -*- 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 the single-issue source: `pearl--issue-source' (id/identifier ->
+;; source plist), the `#+LINEAR-SOURCE' round-trip for that plist,
+;; `pearl--fetch-and-render-issue' (fetch one issue and render it like every
+;; other source), and `pearl-open-issue-by-id' (the jump-by-identifier command).
+;; The async fetch and the render boundary are stubbed at their seams.
+
+;;; Code:
+
+(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))
+
+;;; pearl--issue-source
+
+(ert-deftest test-pearl-issue-source-builds-plist ()
+ "An id + identifier builds a `:type issue' source named by the identifier."
+ (let ((s (pearl--issue-source "u1" "ENG-7")))
+ (should (eq 'issue (plist-get s :type)))
+ (should (string= "u1" (plist-get s :id)))
+ (should (string= "ENG-7" (plist-get s :identifier)))
+ (should (string= "ENG-7" (plist-get s :name)))))
+
+(ert-deftest test-pearl-issue-source-name-falls-back-to-id ()
+ "With no identifier the source name falls back to the id, never nil."
+ (let ((s (pearl--issue-source "u1" nil)))
+ (should (string= "u1" (plist-get s :name)))))
+
+(ert-deftest test-pearl-issue-source-round-trips-through-header ()
+ "The issue source survives `#+LINEAR-SOURCE' serialization and read-back."
+ (let* ((s (pearl--issue-source "u1" "ENG-7"))
+ (back (car (read-from-string (pearl--linear-source-string s)))))
+ (should (equal s back))))
+
+;;; pearl--fetch-and-render-issue
+
+(ert-deftest test-pearl-fetch-and-render-issue-renders-one ()
+ "A fetched node renders through the shared query-result boundary as one issue,
+tagged with an issue source built from the node's own id and identifier."
+ (let ((node '((id . "u1") (identifier . "ENG-7") (title . "A bug")))
+ captured)
+ (cl-letf (((symbol-function 'pearl--fetch-issue-async)
+ (lambda (_ref cb) (funcall cb node)))
+ ((symbol-function 'pearl--render-query-result)
+ (lambda (result source) (setq captured (list result source)))))
+ (pearl--fetch-and-render-issue "ENG-7")
+ (should captured)
+ (let ((result (nth 0 captured))
+ (source (nth 1 captured)))
+ (should (equal (list node) (pearl--query-result-issues result)))
+ (should (eq 'issue (plist-get source :type)))
+ (should (string= "u1" (plist-get source :id)))
+ (should (string= "ENG-7" (plist-get source :identifier)))))))
+
+(ert-deftest test-pearl-fetch-and-render-issue-missing-does-not-render ()
+ "A `:missing' result reports and renders nothing (no buffer rewrite)."
+ (let (rendered)
+ (cl-letf (((symbol-function 'pearl--fetch-issue-async)
+ (lambda (_ref cb) (funcall cb :missing)))
+ ((symbol-function 'pearl--render-query-result)
+ (lambda (&rest _) (setq rendered t)))
+ ((symbol-function 'message) (lambda (&rest _) nil)))
+ (pearl--fetch-and-render-issue "ENG-404")
+ (should-not rendered))))
+
+(ert-deftest test-pearl-fetch-and-render-issue-error-does-not-render ()
+ "An `:error' result reports and renders nothing."
+ (let (rendered)
+ (cl-letf (((symbol-function 'pearl--fetch-issue-async)
+ (lambda (_ref cb) (funcall cb :error)))
+ ((symbol-function 'pearl--render-query-result)
+ (lambda (&rest _) (setq rendered t)))
+ ((symbol-function 'message) (lambda (&rest _) nil)))
+ (pearl--fetch-and-render-issue "ENG-7")
+ (should-not rendered))))
+
+;;; pearl-open-issue-by-id
+
+(ert-deftest test-pearl-open-issue-by-id-forwards-trimmed-ref ()
+ "The command trims the typed ref and forwards it to the fetch/render path."
+ (let (got)
+ (cl-letf (((symbol-function 'pearl--require-account-context) #'ignore)
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--fetch-and-render-issue)
+ (lambda (ref) (setq got ref))))
+ (pearl-open-issue-by-id " ENG-7 ")
+ (should (string= "ENG-7" got)))))
+
+(ert-deftest test-pearl-open-issue-by-id-rejects-empty ()
+ "An empty/whitespace identifier is a `user-error', not a fetch."
+ (let (fetched)
+ (cl-letf (((symbol-function 'pearl--require-account-context) #'ignore)
+ ((symbol-function 'pearl--fetch-and-render-issue)
+ (lambda (&rest _) (setq fetched t))))
+ (should-error (pearl-open-issue-by-id " ") :type 'user-error)
+ (should-not fetched))))
+
+(provide 'test-pearl-single-issue)
+;;; test-pearl-single-issue.el ends here