aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pearl-normalize.el
blob: 78874abf632788058c45e51ef1d10fbca44fbc5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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