diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-24 13:44:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-24 13:44:34 -0500 |
| commit | b081d62276378b3168c92c06153fd59db0589535 (patch) | |
| tree | 9be7f7d22e0c9b4a73432fe744c09bb456c671a9 /tests/test-pearl-query.el | |
| download | pearl-b081d62276378b3168c92c06153fd59db0589535.tar.gz pearl-b081d62276378b3168c92c06153fd59db0589535.zip | |
feat: pearl — manage Linear issues from org-mode
Pearl fetches Linear issues into an org file and syncs edits back. It covers list / custom views / saved queries, per-issue and bulk rendering with comments inline, conflict-aware sync of descriptions, titles, and comments, field commands for priority / state / assignee / labels, and a transient dispatch menu. The render folds to a scannable outline and nests issues under a sortable parent.
Based on and inspired by Gael Blanchemain's linear-emacs.
Diffstat (limited to 'tests/test-pearl-query.el')
| -rw-r--r-- | tests/test-pearl-query.el | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/tests/test-pearl-query.el b/tests/test-pearl-query.el new file mode 100644 index 0000000..87e48b9 --- /dev/null +++ b/tests/test-pearl-query.el @@ -0,0 +1,151 @@ +;;; test-pearl-query.el --- Tests for the general issue query -*- 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--query-issues-async' and the `--page-issues' pager, +;; with `--graphql-request-async' stubbed. Cover the query/variable +;; construction, pagination across pages, the page-cap truncation, and the +;; full set of result statuses (ok / empty / graphql-failed / request-failed). + +;;; Code: + +(require 'test-bootstrap (expand-file-name "test-bootstrap.el")) +(require 'cl-lib) + +(defun test-lq--page (nodes has-next &optional cursor) + "Build a raw issues-page response with NODES, HAS-NEXT, and CURSOR." + `((data (issues (nodes . ,(vconcat nodes)) + (pageInfo (hasNextPage . ,(if has-next t :json-false)) + (endCursor . ,(or cursor "c"))))))) + +;;; construction + +(ert-deftest test-pearl-query-issues-construction () + "The query targets issues(filter:) and passes the filter + default orderBy." + (let (captured result) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (query variables success-fn _error-fn) + (setq captured (list query variables)) + (funcall success-fn (test-lq--page '(((id . "i1"))) nil))))) + (pearl--query-issues-async '(("assignee" ("isMe" ("eq" . t)))) + (lambda (r) (setq result r))) + (should (string-match-p "issues(filter:" (car captured))) + (should (equal '(("assignee" ("isMe" ("eq" . t)))) (cdr (assoc "filter" (cadr captured))))) + (should (string= "updatedAt" (cdr (assoc "orderBy" (cadr captured))))) + (should (= 100 (cdr (assoc "first" (cadr captured)))))))) + +(ert-deftest test-pearl-query-issues-no-filter-omits-variable () + "With a nil filter, the filter variable is omitted (no filter applied)." + (let (vars) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q variables success-fn _e) + (setq vars variables) + (funcall success-fn (test-lq--page '() nil))))) + (pearl--query-issues-async nil #'ignore) + (should-not (assoc "filter" vars))))) + +(ert-deftest test-pearl-query-issues-order-by-override () + "An explicit ORDER-BY overrides the updatedAt default." + (let (vars) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q variables success-fn _e) + (setq vars variables) + (funcall success-fn (test-lq--page '() nil))))) + (pearl--query-issues-async nil #'ignore 'createdAt) + (should (string= "createdAt" (cdr (assoc "orderBy" vars))))))) + +;;; result statuses + +(ert-deftest test-pearl-query-issues-single-page-ok () + "A single page with issues yields an ok result carrying the raw nodes." + (let (result) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q _v success-fn _e) + (funcall success-fn (test-lq--page '(((id . "i1")) ((id . "i2"))) nil))))) + (pearl--query-issues-async nil (lambda (r) (setq result r))) + (should (eq 'ok (pearl--query-result-status result))) + (should (= 2 (length (pearl--query-result-issues result)))) + (should-not (pearl--query-result-truncated-p result))))) + +(ert-deftest test-pearl-query-issues-empty () + "A page with no nodes yields an empty result, not a failure." + (let (result) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q _v success-fn _e) + (funcall success-fn (test-lq--page '() nil))))) + (pearl--query-issues-async nil (lambda (r) (setq result r))) + (should (eq 'empty (pearl--query-result-status result)))))) + +(ert-deftest test-pearl-query-issues-graphql-error () + "A GraphQL error response surfaces as a graphql-failed result." + (let (result) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q _v success-fn _e) + (funcall success-fn '((errors . (((message . "bad filter")))))))) ) + (pearl--query-issues-async nil (lambda (r) (setq result r))) + (should (eq 'graphql-failed (pearl--query-result-status result))) + (should (string= "bad filter" (pearl--query-result-message result)))))) + +(ert-deftest test-pearl-query-issues-transport-error () + "A transport failure (error callback) surfaces as request-failed." + (let (result) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q _v _success-fn error-fn) + (funcall error-fn "boom" nil nil)))) + (pearl--query-issues-async nil (lambda (r) (setq result r))) + (should (eq 'request-failed (pearl--query-result-status result)))))) + +;;; pagination + +(ert-deftest test-pearl-query-issues-paginates () + "Multiple pages accumulate; the cursor drives the next fetch." + (let ((result nil) (calls 0)) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q variables success-fn _e) + (setq calls (1+ calls)) + (if (assoc "after" variables) + (funcall success-fn (test-lq--page '(((id . "i2"))) nil)) + (funcall success-fn (test-lq--page '(((id . "i1"))) t "cur")))))) + (pearl--query-issues-async nil (lambda (r) (setq result r))) + (should (= 2 calls)) + (should (eq 'ok (pearl--query-result-status result))) + (should (= 2 (length (pearl--query-result-issues result))))))) + +(ert-deftest test-pearl-query-issues-cap-truncates () + "Hitting the page cap stops paging and marks the result truncated." + (let ((result nil) (calls 0) + (pearl-max-issue-pages 3)) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q _v success-fn _e) + (setq calls (1+ calls)) + (funcall success-fn (test-lq--page '(((id . "x"))) t "cur"))))) + (pearl--query-issues-async nil (lambda (r) (setq result r))) + (should (pearl--query-result-truncated-p result)) + (should (= 3 calls)) + (should (= 3 (length (pearl--query-result-issues result))))))) + +;;; the bulk query fetches comments so the list can render them + +(ert-deftest test-pearl-issues-query-requests-comments () + "The bulk issues query selects comments, so a populated list shows them." + (should (string-match-p "comments[[:space:]]*{[[:space:]]*nodes" pearl--issues-query))) + +(provide 'test-pearl-query) +;;; test-pearl-query.el ends here |
