diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-23 23:40:27 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-23 23:40:27 -0400 |
| commit | 8869a083c3cc28c20efa63e8932347329b2865f4 (patch) | |
| tree | 8959775eb2cae28a3418542ab8e2fc235fe465f2 /tests/test-pearl-single-issue.el | |
| parent | 92ae9d8b56a9fabadf6ea8beb2163655c7a8ffb8 (diff) | |
| download | pearl-8869a083c3cc28c20efa63e8932347329b2865f4.tar.gz pearl-8869a083c3cc28c20efa63e8932347329b2865f4.zip | |
feat(sources): single-issue source (favorites + open-issue-by-id)
I wired the issue kind into the same pipeline every other source uses, so a favorited issue renders in the buffer as its own subtree instead of the v1 browser punt. The new pearl-open-issue-by-id command does the same for an issue you type by identifier or id. It's handy when someone hands you ENG-123 and you'd rather read it in Pearl than a browser.
Linear's issue(id:) accepts both the UUID and the human identifier, so there's no resolution step. I fetch by whatever was given and build the :type issue source from the returned node, so the stored id and identifier stay authoritative. Refresh re-fetches the one issue and merges by LINEAR-ID like the filter and view sources.
Tests cover the source constructor, the header round-trip, and the fetch/render and command paths across Normal, Boundary, and Error.
Diffstat (limited to 'tests/test-pearl-single-issue.el')
| -rw-r--r-- | tests/test-pearl-single-issue.el | 117 |
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 |
