diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-03 00:11:25 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-03 00:11:25 -0500 |
| commit | 8acc99d88527a9dfecf7ebceb858f344819d10c3 (patch) | |
| tree | 025136d5f9dcce9641d28b19ec9113fb38e05a13 /tests | |
| parent | 68d51b341bccc72c7b01f547e43ce90a76729244 (diff) | |
| download | pearl-8acc99d88527a9dfecf7ebceb858f344819d10c3.tar.gz pearl-8acc99d88527a9dfecf7ebceb858f344819d10c3.zip | |
fix(views): rebuild Linear view URLs instead of querying the missing url field
Linear's CustomView type has no url field. Three GraphQL selections requested it (the custom-views list query and both the create and update mutation payloads), so each of those requests returned HTTP 400 and pearl got nil back. Running a Linear view came up with an empty picker, and publishing or updating a local view to Linear failed silently. The test suite stayed green because the view tests mocked a url field the real API never returns.
I dropped url from all three selections. The workspace urlKey is now fetched once and cached, and a view's web URL is rebuilt as https://linear.app/<urlKey>/view/<view-id>, the shape Linear's own favorite URLs use (the full view UUID, not the slug id). pearl-open-current-view-in-linear keeps working, and when the urlKey is unavailable it degrades to nil so the link builder can't throw. That also stops a urlKey hiccup right after a successful publish from aborting the record step and orphaning the new view.
I added regression locks asserting neither the list query nor the mutation payload selection requests url, since the field name is the schema contract that broke.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-pearl-saved-query-sync.el | 7 | ||||
| -rw-r--r-- | tests/test-pearl-views.el | 49 |
2 files changed, 53 insertions, 3 deletions
diff --git a/tests/test-pearl-saved-query-sync.el b/tests/test-pearl-saved-query-sync.el index 534993e..91a7061 100644 --- a/tests/test-pearl-saved-query-sync.el +++ b/tests/test-pearl-saved-query-sync.el @@ -199,6 +199,12 @@ label, plist team-id nil + team-name \"Personal\" + shared nil." ;;; pearl--customview-create-async +(ert-deftest test-pearl-customview-payload-fields-omit-url () + "The mutation payload selection must not request the unsupported `url' field. +Linear's `CustomView' has no `url'; selecting it 400s the create/update and +silently fails the publish (the 2026-06-02 regression)." + (should-not (string-match-p "\\burl\\b" pearl--customview-payload-fields))) + (ert-deftest test-pearl-customview-create-async-success () "A successful `customViewCreate' invokes the callback with `:success' t and the view." (testutil-linear-with-response @@ -459,6 +465,7 @@ erased on every re-sync." ((symbol-function 'pearl--progress) (lambda (&rest _) nil)) ((symbol-function 'message) (lambda (&rest _) nil)) ((symbol-function 'customize-save-variable) (lambda (_ _) nil)) + ((symbol-function 'pearl--org-url-key) (lambda (&optional _) "deepsat")) ((symbol-function 'pearl--customview-update-async) (lambda (_view-id input cb) (setq captured-input input) diff --git a/tests/test-pearl-views.el b/tests/test-pearl-views.el index a750ae0..e22a6d3 100644 --- a/tests/test-pearl-views.el +++ b/tests/test-pearl-views.el @@ -55,13 +55,52 @@ (cl-incf calls) (funcall (plist-get args :success) :data '((data (customViews - (nodes . [((id . "v1") (name . "My View") (url . "https://x"))]) + (nodes . [((id . "v1") (name . "My View"))]) (pageInfo (hasNextPage . :json-false))))))))) (let ((views (pearl--custom-views))) (should (= 1 (length views))) (pearl--custom-views) (should (= 1 calls)))))) +(ert-deftest test-pearl-custom-views-query-omits-url () + "The Custom Views list query must not select the unsupported `url' field. +Linear's `CustomView' type has no `url'; requesting it 400s the whole query +and leaves the run-view picker empty (the 2026-06-02 regression)." + (should-not (string-match-p "\\burl\\b" (pearl--custom-views-query)))) + +;;; --org-url-key (cached) and --view-url (reconstructed) + +(ert-deftest test-pearl-org-url-key-caches () + "The workspace url key is fetched once and served from cache." + (let ((pearl-api-key "test-key") + (pearl--cache-org-url-key nil) + (calls 0)) + (cl-letf (((symbol-function 'request) + (lambda (_url &rest args) + (cl-incf calls) + (funcall (plist-get args :success) :data + '((data (organization (urlKey . "deepsat")))))))) + (should (string= "deepsat" (pearl--org-url-key))) + (pearl--org-url-key) + (should (= 1 calls))))) + +(ert-deftest test-pearl-view-url-builds-from-urlkey () + "A view's web URL is reconstructed from the workspace url key and view id." + (cl-letf (((symbol-function 'pearl--org-url-key) (lambda (&optional _) "deepsat"))) + (should (string= "https://linear.app/deepsat/view/abc123" + (pearl--view-url "abc123"))))) + +(ert-deftest test-pearl-view-url-nil-when-no-urlkey () + "With no workspace url key, the view URL is nil so callers degrade gracefully." + (cl-letf (((symbol-function 'pearl--org-url-key) (lambda (&optional _) nil))) + (should-not (pearl--view-url "abc123")))) + +(ert-deftest test-pearl-view-url-nil-when-empty-view-id () + "A nil or empty view id yields no URL rather than a malformed link." + (cl-letf (((symbol-function 'pearl--org-url-key) (lambda (&optional _) "deepsat"))) + (should-not (pearl--view-url nil)) + (should-not (pearl--view-url "")))) + ;;; run-view (ert-deftest test-pearl-run-view-renders-with-view-source () @@ -69,7 +108,10 @@ (let ((ran-id nil) (rendered-source nil)) (cl-letf (((symbol-function 'pearl--custom-views) (lambda (&optional _force) - '(((id . "v1") (name . "My View") (url . "https://linear.app/view/v1"))))) + ;; Nodes carry no `url' -- Linear's CustomView has none; the + ;; source URL is reconstructed from the workspace url key. + '(((id . "v1") (name . "My View"))))) + ((symbol-function 'pearl--org-url-key) (lambda (&optional _) "deepsat")) ((symbol-function 'pearl--query-view-async) (lambda (id cb) (setq ran-id id) (funcall cb (pearl--make-query-result 'ok :issues nil)))) @@ -79,7 +121,8 @@ (should (string= "v1" ran-id)) (should (eq 'view (plist-get rendered-source :type))) (should (string= "v1" (plist-get rendered-source :id))) - (should (string= "https://linear.app/view/v1" (plist-get rendered-source :url)))))) + (should (string= "https://linear.app/deepsat/view/v1" + (plist-get rendered-source :url)))))) ;;; refresh-current-view, view branch |
