aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pearl-saved-query-sync.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-pearl-saved-query-sync.el')
-rw-r--r--tests/test-pearl-saved-query-sync.el83
1 files changed, 79 insertions, 4 deletions
diff --git a/tests/test-pearl-saved-query-sync.el b/tests/test-pearl-saved-query-sync.el
index 8c98539..e237108 100644
--- a/tests/test-pearl-saved-query-sync.el
+++ b/tests/test-pearl-saved-query-sync.el
@@ -110,6 +110,60 @@
(< apple-pos mango-pos)
(< mango-pos zebra-pos)))))
+;;; pearl--sync-scope-label
+
+(ert-deftest test-pearl-sync-scope-label-personal-ignores-shared ()
+ "Personal scope always renders the personal label regardless of SHARED.
+A `Personal + shared' combination is meaningless on Linear (there's no
+team to share with), so the label collapses to the only sensible
+phrasing."
+ (should (equal "[ Personal, only I see it ]"
+ (pearl--sync-scope-label "Personal" nil)))
+ (should (equal "[ Personal, only I see it ]"
+ (pearl--sync-scope-label "Personal" t))))
+
+(ert-deftest test-pearl-sync-scope-label-team-renders-shared-or-private ()
+ "A team scope renders the shared or private variant per SHARED."
+ (should (equal "[ Team: Engineering, visible to the team ]"
+ (pearl--sync-scope-label "Engineering" t)))
+ (should (equal "[ Team: Engineering, only I see it ]"
+ (pearl--sync-scope-label "Engineering" nil))))
+
+(ert-deftest test-pearl-sync-saved-query-pick-scope-returns-display-plist-pair ()
+ "`pick-scope' returns the chosen candidate's (DISPLAY . PLIST) pair, not
+just the plist. The caller uses the display string for the success
+message without rebuilding the candidate list to look it up."
+ (cl-letf (((symbol-function 'pearl--all-teams)
+ (lambda () '(((id . "t1") (key . "ENG") (name . "Engineering")))))
+ ((symbol-function 'completing-read)
+ (lambda (&rest _) "[ Team: Engineering, visible to the team ]")))
+ (let ((pair (pearl--sync-saved-query-pick-scope '(:team "ENG"))))
+ (should (consp pair))
+ (should (equal "[ Team: Engineering, visible to the team ]"
+ (car pair)))
+ (should (equal "t1" (plist-get (cdr pair) :team-id)))
+ (should (eq t (plist-get (cdr pair) :shared))))))
+
+(ert-deftest test-pearl-sync-saved-query-pick-scope-returns-personal-pair ()
+ "Picking the Personal row returns the personal pair: display = the personal
+label, plist team-id nil + team-name \"Personal\" + shared nil."
+ (cl-letf (((symbol-function 'pearl--all-teams) (lambda () '()))
+ ((symbol-function 'completing-read)
+ (lambda (&rest _) "[ Personal, only I see it ]")))
+ (let ((pair (pearl--sync-saved-query-pick-scope '())))
+ (should (consp pair))
+ (should (equal "[ Personal, only I see it ]" (car pair)))
+ (should (null (plist-get (cdr pair) :team-id)))
+ (should (equal "Personal" (plist-get (cdr pair) :team-name)))
+ (should (null (plist-get (cdr pair) :shared))))))
+
+(ert-deftest test-pearl-sync-saved-query-pick-scope-returns-nil-on-cancel ()
+ "Picking the cancel sentinel returns nil so the caller can short-circuit."
+ (cl-letf (((symbol-function 'pearl--all-teams) (lambda () '()))
+ ((symbol-function 'completing-read)
+ (lambda (&rest _) pearl--filter-cancel)))
+ (should (null (pearl--sync-saved-query-pick-scope '())))))
+
;;; pearl--find-view-by-name-in-scope
(ert-deftest test-pearl-find-view-by-name-in-scope-matches-team-shared ()
@@ -418,16 +472,20 @@ erased on every re-sync."
;;; pearl-sync-saved-query-to-linear — empty filter refusal
(ert-deftest test-pearl-sync-empty-filter-refuses-before-scope-prompt ()
- "A saved query 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
+ "A first-time-sync saved query 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))))
(scope-prompt-fired nil)
(api-called nil))
(cl-letf (((symbol-function 'pearl--sync-saved-query-pick-scope)
(lambda (&rest _) (setq scope-prompt-fired t)
- (list :team-id nil :team-name "Personal" :shared nil)))
+ ;; Pick-scope returns (DISPLAY . PLIST); the empty-filter
+ ;; guard fires before this is consumed, but the stub
+ ;; shape should still match production.
+ (cons "[ Personal, only I see it ]"
+ (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")
@@ -435,6 +493,23 @@ user has already clicked through the scope picker."
(should-not scope-prompt-fired)
(should-not api-called))))
+(ert-deftest test-pearl-sync-empty-filter-refuses-resync-too ()
+ "A re-sync (entry already has `:linear-view-id') whose filter has been
+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
+ (copy-tree '(("EmptyFilter" :filter nil
+ :linear-view-id "existing-view-uuid"
+ :linear-view-team-id nil
+ :linear-view-shared :json-false))))
+ (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")
+ :type 'user-error)
+ (should-not update-called))))
+
;;; pearl-publish-current-source
(defmacro test-pearl-publish--in-buffer (source-form &rest body)