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-normalize.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-normalize.el')
| -rw-r--r-- | tests/test-pearl-normalize.el | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/tests/test-pearl-normalize.el b/tests/test-pearl-normalize.el new file mode 100644 index 0000000..78874ab --- /dev/null +++ b/tests/test-pearl-normalize.el @@ -0,0 +1,138 @@ +;;; test-pearl-normalize.el --- Tests for API model normalization -*- 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 normalizers that flatten raw json-read Linear responses into +;; internal plists. Driven by the shared fixtures so the renderer-facing +;; contract is locked: vectors become lists, absent/`:json-false' fields become +;; nil, and a null comment author falls back to the bot/external actor. + +;;; Code: + +(require 'test-bootstrap (expand-file-name "test-bootstrap.el")) +(require 'testutil-fixtures (expand-file-name "testutil-fixtures.el")) + +;;; node-list + +(ert-deftest test-pearl-normalize-node-list-vector-to-list () + "A connection's nodes vector is returned as a list." + (should (equal '(1 2 3) (pearl--node-list '((nodes . [1 2 3])))))) + +(ert-deftest test-pearl-normalize-node-list-empty-and-missing () + "An empty or missing nodes connection yields an empty list." + (should (null (pearl--node-list '((nodes . []))))) + (should (null (pearl--node-list '((pageInfo . nil)))))) + +;;; normalize-issue -- fully populated + +(ert-deftest test-pearl-normalize-issue-full () + "A full issue normalizes every field, flattening nested objects." + (let ((i (pearl--normalize-issue (testutil-linear-fixture-issue-full)))) + (should (string= "ENG-42" (plist-get i :identifier))) + (should (string= "Fix the thing" (plist-get i :title))) + (should (= 2 (plist-get i :priority))) + (should (string= "In Progress" (plist-get (plist-get i :state) :name))) + (should (string= "started" (plist-get (plist-get i :state) :type))) + (should (string= "Craig" (plist-get (plist-get i :assignee) :name))) + (should (string= "ENG" (plist-get (plist-get i :team) :key))) + (should (string= "Platform" (plist-get (plist-get i :project) :name))) + (should (string= "Cycle 12" (plist-get (plist-get i :cycle) :name))) + ;; labels: vector of nodes -> list of (:id :name) plists + (should (equal '("bug" "backend") + (mapcar (lambda (l) (plist-get l :name)) (plist-get i :labels)))))) + +;;; normalize-issue -- null / missing optional fields + +(ert-deftest test-pearl-normalize-issue-null-fields () + "Absent or null optional fields normalize to nil, not an error." + (let ((i (pearl--normalize-issue (testutil-linear-fixture-issue-null-fields)))) + (should (string= "ENG-7" (plist-get i :identifier))) + (should (null (plist-get i :description))) + (should (null (plist-get i :assignee))) + (should (null (plist-get i :project))) + (should (null (plist-get i :cycle))) + (should (null (plist-get i :labels))) + ;; state is still present + (should (string= "Todo" (plist-get (plist-get i :state) :name))))) + +(ert-deftest test-pearl-normalize-issue-nil-input () + "Normalizing nil yields nil." + (should (null (pearl--normalize-issue nil)))) + +(ert-deftest test-pearl-normalize-issue-omits-comments-when-absent () + "An issue fetched without comments has a nil :comments, not an empty list." + (let ((i (pearl--normalize-issue (testutil-linear-fixture-issue-full)))) + (should (null (plist-get i :comments))))) + +;;; normalize-comment -- author fallback + +(ert-deftest test-pearl-normalize-comment-user-author () + "A comment with a user takes the user's name as author." + (let* ((raw (car (pearl--node-list + (cdr (assoc 'comments (testutil-linear-fixture-issue-with-comments)))))) + (c (pearl--normalize-comment raw))) + (should (string= "Alice" (plist-get c :author))) + (should (string= "First comment" (plist-get c :body))))) + +(ert-deftest test-pearl-normalize-comment-null-user-falls-back-to-bot () + "A comment with a null user falls back to the bot actor's name. + +`Comment.user' is null for integration/bot comments, so the renderer must not +assume a user is present." + (let ((c (pearl--normalize-comment + '((id . "cm-bot") (body . "Deployed") (createdAt . "2026-05-20T00:00:00Z") + (user) (botActor . ((name . "GitHub"))))))) + (should (string= "GitHub" (plist-get c :author))))) + +(ert-deftest test-pearl-normalize-comment-null-user-no-actor-nil () + "A null user with no bot or external actor leaves :author nil. +The renderer is responsible for showing a placeholder; the normalizer reports +the absence honestly rather than inventing a name." + (let ((c (pearl--normalize-comment '((id . "cm-x") (body . "x") (user))))) + (should (null (plist-get c :author))))) + +(ert-deftest test-pearl-normalize-comment-bot-without-name-default () + "A bot actor with no name falls back to the literal \"automation\"." + (let ((c (pearl--normalize-comment + '((id . "cm-b") (body . "x") (user) (botActor . ((id . "b1"))))))) + (should (string= "automation" (plist-get c :author))))) + +;;; normalize-custom-view + +(ert-deftest test-pearl-normalize-custom-view-personal () + "A personal (shared=false) workspace-wide view: :shared nil, :team nil." + (let* ((views (cdr (assoc 'nodes (assoc 'customViews + (assoc 'data (testutil-linear-fixture-custom-views)))))) + (cv (pearl--normalize-custom-view (elt views 0)))) + (should (string= "My open work" (plist-get cv :name))) + (should (null (plist-get cv :shared))) + (should (null (plist-get cv :team))) + (should (string= "Craig" (plist-get (plist-get cv :owner) :name))))) + +(ert-deftest test-pearl-normalize-custom-view-shared-with-team () + "A shared team view: :shared t and a normalized :team plist." + (let* ((views (cdr (assoc 'nodes (assoc 'customViews + (assoc 'data (testutil-linear-fixture-custom-views)))))) + (cv (pearl--normalize-custom-view (elt views 1)))) + (should (eq t (plist-get cv :shared))) + (should (string= "ENG" (plist-get (plist-get cv :team) :key))))) + +(provide 'test-pearl-normalize) +;;; test-pearl-normalize.el ends here |
