diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-01 17:33:09 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-01 17:33:09 -0500 |
| commit | c2ba1d8f25463bf6342ac635850500a9738b31e9 (patch) | |
| tree | 69518c846bc2e9b11559dc637b6ee81d9d3511c0 /tests/test-pearl-saved-query-sync.el | |
| parent | 4eab7a048ca18eeef31e4f8d0823182fc6e2368f (diff) | |
| download | pearl-c2ba1d8f25463bf6342ac635850500a9738b31e9.tar.gz pearl-c2ba1d8f25463bf6342ac635850500a9738b31e9.zip | |
refactor: rename saved-query surface to local-view vocabulary
I renamed Pearl's "saved query" surface to the local/Linear view vocabulary, with no obsolete aliases, since Pearl has no users to protect. The defcustom pearl-saved-queries is now pearl-local-views. The commands pearl-run-saved-query, pearl-delete-saved-query, pearl-sync-saved-query-to-linear, pearl-run-view, and pearl-publish-current-source become pearl-run-local-view, pearl-delete-local-view, pearl-publish-local-view, pearl-run-linear-view, and pearl-publish-current-view.
User-facing prompts, messages, and docstrings drop "saved query" for "local view", and the publish command reads "publish" instead of "sync". Internal GraphQL and helper names keep their "query" terms, which a user never sees. A naming-regression test asserts the new symbols exist, the old ones are gone rather than aliased, and no public command exposes "query".
Phase 1 of docs/local-and-linear-views-spec.org. No behavior change. Suite, compile, and lint are green.
Diffstat (limited to 'tests/test-pearl-saved-query-sync.el')
| -rw-r--r-- | tests/test-pearl-saved-query-sync.el | 110 |
1 files changed, 55 insertions, 55 deletions
diff --git a/tests/test-pearl-saved-query-sync.el b/tests/test-pearl-saved-query-sync.el index e237108..534993e 100644 --- a/tests/test-pearl-saved-query-sync.el +++ b/tests/test-pearl-saved-query-sync.el @@ -1,4 +1,4 @@ -;;; test-pearl-saved-query-sync.el --- Tests for syncing saved queries to Linear views -*- lexical-binding: t; -*- +;;; test-pearl-saved-query-sync.el --- Tests for syncing local views to Linear views -*- lexical-binding: t; -*- ;; Copyright (C) 2026 Craig Jennings @@ -19,7 +19,7 @@ ;;; Commentary: -;; Tests for `pearl-sync-saved-query-to-linear' and its helpers +;; Tests for `pearl-publish-local-view' and its helpers ;; (`pearl--sync-scope-candidates', `pearl--find-view-by-name-in-scope', ;; `pearl--customview-create-async', `pearl--customview-update-async', ;; `pearl--save-query-mark-synced', `pearl--sync-record-or-orphan-error'). @@ -254,11 +254,11 @@ label, plist team-id nil + team-name \"Personal\" + shared nil." (ert-deftest test-pearl-save-query-mark-synced-adds-the-four-keys () "After mark-synced, the entry carries `:linear-view-id', `-team-id', `-shared', `-synced-at'." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t)))))) (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) (pearl--save-query-mark-synced "Q" "team-uuid" t "view-uuid")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) (should (equal "view-uuid" (plist-get spec :linear-view-id))) (should (equal "team-uuid" (plist-get spec :linear-view-team-id))) (should (eq t (plist-get spec :linear-view-shared))) @@ -268,39 +268,39 @@ label, plist team-id nil + team-name \"Personal\" + shared nil." (ert-deftest test-pearl-save-query-mark-synced-preserves-sort-and-order () "Mark-synced doesn't drop `:sort' or `:order' from the entry." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :sort updated :order desc))))) (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) (pearl--save-query-mark-synced "Q" nil nil "view-uuid")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) (should (eq 'updated (plist-get spec :sort))) (should (eq 'desc (plist-get spec :order)))))) (ert-deftest test-pearl-save-query-mark-synced-personal-nil-team () "Personal scope persists `:linear-view-team-id' as nil and `:linear-view-shared' nil." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t)))))) (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) (pearl--save-query-mark-synced "Q" nil nil "view-uuid")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) (should (null (plist-get spec :linear-view-team-id))) (should (null (plist-get spec :linear-view-shared)))))) (ert-deftest test-pearl-save-query-mark-synced-unknown-name-signals-user-error () "Mark-synced on a name that doesn't exist signals a `user-error'." - (let ((pearl-saved-queries '())) + (let ((pearl-local-views '())) (should-error (pearl--save-query-mark-synced "Q" nil nil "view-uuid") :type 'user-error))) (ert-deftest test-pearl-save-query-mark-synced-stores-view-url-when-given () "The optional VIEW-URL arg lands in the entry as :linear-view-url." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t)))))) (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) (pearl--save-query-mark-synced "Q" nil nil "view-uuid" "https://linear.app/ws/view/abc")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) (should (equal "https://linear.app/ws/view/abc" (plist-get spec :linear-view-url)))))) @@ -308,7 +308,7 @@ label, plist team-id nil + team-name \"Personal\" + shared nil." "Omitting VIEW-URL on a re-sync preserves any previously stored URL. Re-sync paths whose API response doesn't carry a URL shouldn't erase a URL the create path already wrote." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "old-id" :linear-view-team-id nil @@ -317,7 +317,7 @@ URL the create path already wrote." (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) ;; No VIEW-URL passed -- the prior URL should survive. (pearl--save-query-mark-synced "Q" nil nil "new-id")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) (should (equal "https://linear.app/ws/view/old" (plist-get spec :linear-view-url)))))) @@ -327,7 +327,7 @@ The earlier implementation rebuilt the spec from a fixed key set and silently dropped anything outside it. Copy-then-plist-put preserves arbitrary keys so a future schema field or user annotation isn't erased on every re-sync." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :sort updated :order desc @@ -335,7 +335,7 @@ erased on every re-sync." :user-tag "experimental"))))) (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) (pearl--save-query-mark-synced "Q" "team-uuid" t "view-uuid" "https://x")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) ;; Original keys survive. (should (equal '(:open t) (plist-get spec :filter))) (should (eq 'updated (plist-get spec :sort))) @@ -351,7 +351,7 @@ erased on every re-sync." (ert-deftest test-pearl-save-query-mark-synced-overwrites-prior-sync-metadata () "Re-syncing an already-synced entry overwrites the four `:linear-view-*' keys." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "OLD-id" :linear-view-team-id "OLD-team" @@ -359,7 +359,7 @@ erased on every re-sync." :linear-view-synced-at "1970-01-01T00:00:00Z"))))) (cl-letf (((symbol-function 'customize-save-variable) (lambda (_ _) nil))) (pearl--save-query-mark-synced "Q" "NEW-team" t "NEW-id")) - (let ((spec (cdr (assoc "Q" pearl-saved-queries)))) + (let ((spec (cdr (assoc "Q" pearl-local-views)))) (should (equal "NEW-id" (plist-get spec :linear-view-id))) (should (equal "NEW-team" (plist-get spec :linear-view-team-id))) (should (eq t (plist-get spec :linear-view-shared))) @@ -445,7 +445,7 @@ erased on every re-sync." "Re-sync of an entry with `:linear-view-shared :json-false' must NOT flip it shared. `:json-false' is truthy in Elisp, so a stored `:json-false' would slip past `(if shared t :json-false)' in the encoder and silently make the view team-shared." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "view-id" :linear-view-team-id nil @@ -465,18 +465,18 @@ erased on every re-sync." (funcall cb (list :success t :view '((id . "view-id") (shared . :json-false))))))) - (pearl-sync-saved-query-to-linear "Q") + (pearl-publish-local-view "Q") (let ((shared-value (cdr (assoc "shared" captured-input)))) (should (eq :json-false shared-value)))))) -;;; pearl-sync-saved-query-to-linear — empty filter refusal +;;; pearl-publish-local-view — empty filter refusal (ert-deftest test-pearl-sync-empty-filter-refuses-before-scope-prompt () - "A first-time-sync saved query whose filter compiles to nil signals a clear + "A first-time-sync local view whose filter compiles to nil signals a clear user-error before any scope prompt fires. Without the guard, the sync would pass filterData=null to Linear, which rejects with an opaque error after the user has already clicked through the scope picker." - (let ((pearl-saved-queries (copy-tree '(("EmptyFilter" :filter nil)))) + (let ((pearl-local-views (copy-tree '(("EmptyFilter" :filter nil)))) (scope-prompt-fired nil) (api-called nil)) (cl-letf (((symbol-function 'pearl--sync-saved-query-pick-scope) @@ -488,7 +488,7 @@ user has already clicked through the scope picker." (list :team-id nil :team-name "Personal" :shared nil)))) ((symbol-function 'pearl--customview-create-async) (lambda (&rest _) (setq api-called t)))) - (should-error (pearl-sync-saved-query-to-linear "EmptyFilter") + (should-error (pearl-publish-local-view "EmptyFilter") :type 'user-error) (should-not scope-prompt-fired) (should-not api-called)))) @@ -498,7 +498,7 @@ user has already clicked through the scope picker." edited down to nil takes the same empty-filter guard, refusing without calling customViewUpdate. Guards against a future refactor accidentally hoisting the guard into the create-only branch." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("EmptyFilter" :filter nil :linear-view-id "existing-view-uuid" :linear-view-team-id nil @@ -506,11 +506,11 @@ hoisting the guard into the create-only branch." (update-called nil)) (cl-letf (((symbol-function 'pearl--customview-update-async) (lambda (&rest _) (setq update-called t)))) - (should-error (pearl-sync-saved-query-to-linear "EmptyFilter") + (should-error (pearl-publish-local-view "EmptyFilter") :type 'user-error) (should-not update-called)))) -;;; pearl-publish-current-source +;;; pearl-publish-current-view (defmacro test-pearl-publish--in-buffer (source-form &rest body) "Run BODY in an org-mode temp buffer carrying SOURCE-FORM as #+LINEAR-SOURCE. @@ -526,50 +526,50 @@ SOURCE-FORM is the source plist (or nil for no source header)." (ert-deftest test-pearl-publish-current-source-no-source-header-errors () "A buffer with no #+LINEAR-SOURCE signals a clear user-error." (test-pearl-publish--in-buffer nil - (should-error (pearl-publish-current-source) :type 'user-error))) + (should-error (pearl-publish-current-view) :type 'user-error))) (ert-deftest test-pearl-publish-current-source-view-source-errors () "A view source signals user-error -- views are already on Linear." (test-pearl-publish--in-buffer '(:type view :name "Eng dashboard" :id "v1") - (should-error (pearl-publish-current-source) :type 'user-error))) + (should-error (pearl-publish-current-view) :type 'user-error))) (ert-deftest test-pearl-publish-current-source-transient-filter-no-name-errors () "A :type filter with no :name (transient ad-hoc) signals user-error." (test-pearl-publish--in-buffer '(:type filter :filter (:open t)) - (should-error (pearl-publish-current-source) :type 'user-error))) + (should-error (pearl-publish-current-view) :type 'user-error))) (ert-deftest test-pearl-publish-current-source-no-matching-saved-query-errors () - "A :type filter source whose name doesn't match any pearl-saved-queries entry errors." - (let ((pearl-saved-queries '(("Other" :filter (:open t))))) + "A :type filter source whose name doesn't match any pearl-local-views entry errors." + (let ((pearl-local-views '(("Other" :filter (:open t))))) (test-pearl-publish--in-buffer '(:type filter :name "Gone" :filter (:open t)) - (should-error (pearl-publish-current-source) :type 'user-error)))) + (should-error (pearl-publish-current-view) :type 'user-error)))) (ert-deftest test-pearl-publish-current-source-dispatches-to-sync-with-name () - "A :type filter source whose name matches dispatches to pearl-sync-saved-query-to-linear." - (let ((pearl-saved-queries '(("Mine" :filter (:open t)))) + "A :type filter source whose name matches dispatches to pearl-publish-local-view." + (let ((pearl-local-views '(("Mine" :filter (:open t)))) (sync-called-with nil)) - (cl-letf (((symbol-function 'pearl-sync-saved-query-to-linear) + (cl-letf (((symbol-function 'pearl-publish-local-view) (lambda (n) (setq sync-called-with n)))) (test-pearl-publish--in-buffer '(:type filter :name "Mine" :filter (:open t)) - (pearl-publish-current-source)) + (pearl-publish-current-view)) (should (equal "Mine" sync-called-with))))) (ert-deftest test-pearl-publish-current-source-empty-name-errors () "A :type filter source with an empty-string :name (degenerate) errors cleanly." (test-pearl-publish--in-buffer '(:type filter :name "" :filter (:open t)) - (should-error (pearl-publish-current-source) :type 'user-error))) + (should-error (pearl-publish-current-view) :type 'user-error))) (ert-deftest test-pearl-publish-current-source-whitespace-name-errors () "A :type filter source with a whitespace-only :name takes the no-name branch rather than falling through to the no-matching-saved-query branch with a visibly-blank name in the error message." - (let ((pearl-saved-queries '())) + (let ((pearl-local-views '())) (test-pearl-publish--in-buffer '(:type filter :name " " :filter (:open t)) - (let ((err (should-error (pearl-publish-current-source) + (let ((err (should-error (pearl-publish-current-view) :type 'user-error))) ;; The message should name the source-has-no-name reason, not the ;; no-matching-query reason. @@ -609,7 +609,7 @@ team-key lookups." (pearl--customview-delete-async "view-uuid" (lambda (r) (setq result r))) (should-not (plist-get result :success))))) -;;; pearl-delete-saved-query — synced-entry second-stage prompt +;;; pearl-delete-local-view — synced-entry second-stage prompt (defun test-pearl--make-yes-no-stub (answers) "Return a `yes-or-no-p' replacement that returns each ANSWERS in order. @@ -623,7 +623,7 @@ more prompts fire than answers were prepared for." (ert-deftest test-pearl-delete-saved-query-synced-yes-yes-calls-customview-delete () "Synced entry, both prompts yes: customViewDelete fires and the local entry is removed." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "view-uuid" :linear-view-team-id nil @@ -638,13 +638,13 @@ more prompts fire than answers were prepared for." (lambda (vid cb) (setq delete-called-with vid) (funcall cb (list :success t))))) - (pearl-delete-saved-query "Q") + (pearl-delete-local-view "Q") (should (equal "view-uuid" delete-called-with)) - (should-not (assoc "Q" pearl-saved-queries))))) + (should-not (assoc "Q" pearl-local-views))))) (ert-deftest test-pearl-delete-saved-query-synced-yes-no-unlinks-only () "Synced entry, first prompt yes second prompt no: local entry removed, customViewDelete not called." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "view-uuid")))) (delete-called nil)) @@ -655,13 +655,13 @@ more prompts fire than answers were prepared for." ((symbol-function 'message) (lambda (&rest _) nil)) ((symbol-function 'pearl--customview-delete-async) (lambda (_ _) (setq delete-called t)))) - (pearl-delete-saved-query "Q") + (pearl-delete-local-view "Q") (should-not delete-called) - (should-not (assoc "Q" pearl-saved-queries))))) + (should-not (assoc "Q" pearl-local-views))))) (ert-deftest test-pearl-delete-saved-query-synced-api-failure-then-delete-anyway () "Synced entry, customViewDelete fails, user accepts delete-anyway prompt: local removed, message names the orphan id." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "ORPHAN-UUID")))) (last-message nil)) @@ -675,8 +675,8 @@ more prompts fire than answers were prepared for." ((symbol-function 'pearl--customview-delete-async) (lambda (_ cb) (funcall cb (list :success nil :error "permission denied"))))) - (pearl-delete-saved-query "Q") - (should-not (assoc "Q" pearl-saved-queries)) + (pearl-delete-local-view "Q") + (should-not (assoc "Q" pearl-local-views)) (should (string-match-p "ORPHAN-UUID" last-message)) (should (string-match-p "orphan" last-message))))) @@ -685,7 +685,7 @@ more prompts fire than answers were prepared for." Verifies the API call actually fires (so the keep branch only runs when the delete-anyway prompt explicitly declines), guarding against a future refactor that silently routes to the unlink branch and skips the API call." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t) :linear-view-id "view-uuid")))) (delete-called-with nil)) @@ -698,13 +698,13 @@ that silently routes to the unlink branch and skips the API call." (lambda (vid cb) (setq delete-called-with vid) (funcall cb (list :success nil :error "permission denied"))))) - (pearl-delete-saved-query "Q") + (pearl-delete-local-view "Q") (should (equal "view-uuid" delete-called-with)) - (should (assoc "Q" pearl-saved-queries))))) + (should (assoc "Q" pearl-local-views))))) (ert-deftest test-pearl-delete-saved-query-local-only-entry-unchanged-behavior () "Local-only entry (no `:linear-view-id'): one yes-or-no-p, no second prompt, no API call." - (let ((pearl-saved-queries + (let ((pearl-local-views (copy-tree '(("Q" :filter (:open t))))) (delete-called nil) (prompt-count 0)) @@ -714,10 +714,10 @@ that silently routes to the unlink branch and skips the API call." ((symbol-function 'message) (lambda (&rest _) nil)) ((symbol-function 'pearl--customview-delete-async) (lambda (_ _) (setq delete-called t)))) - (pearl-delete-saved-query "Q") + (pearl-delete-local-view "Q") (should (= 1 prompt-count)) (should-not delete-called) - (should-not (assoc "Q" pearl-saved-queries))))) + (should-not (assoc "Q" pearl-local-views))))) (provide 'test-pearl-saved-query-sync) ;;; test-pearl-saved-query-sync.el ends here |
