aboutsummaryrefslogtreecommitdiff
path: root/tests/test-ai-config-gptel-prompt-tab-width.el
blob: 6f857cdcbbc98813ca99ba82f9d74a63a4d9dbb5 (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
;;; test-ai-config-gptel-prompt-tab-width.el --- Tests for gptel prompt-buffer tab-width fix -*- lexical-binding: t; -*-

;;; Commentary:
;; Regression tests for the `org-element--list-struct: Tab width in Org
;; files must be 8, not 4' error that fires when `gptel-magit' is
;; invoked from a `COMMIT_EDITMSG' buffer where `git-commit-major-mode'
;; is `org-mode'.
;;
;; Root cause: gptel's `gptel--with-buffer-copy-internal'
;; (gptel-request.el:945) sets the prompt buffer's `major-mode' to the
;; source buffer's mode as a *symbol*, without running mode hooks.  So
;; an inherited `org-mode' prompt buffer keeps the global default
;; `tab-width' (4 in this config) — `org-mode-hook' never runs to set
;; 8.  When gptel later parses the prompt buffer with `org-element',
;; Org's `tab-width=8' guard raises.
;;
;; Fix: an `:around' advice on `gptel--with-buffer-copy-internal' in
;; ai-config.el sets `tab-width=8' inside the prompt buffer when its
;; inherited `major-mode' is `org-mode'.
;;
;; These tests verify the advice fires for org-mode source buffers and
;; stays out of the way for other modes.  Each test pins
;; `default-value' of `tab-width' to 4 — matching Craig's real config
;; — so the advice's effect is observable in batch tests (where the
;; default would otherwise be Emacs's built-in 8).

;;; Code:

(require 'ert)

(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory))
(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))

;; testutil-ai-config provides the gptel--with-buffer-copy-internal stub
;; and the gptel stubs ai-config needs to load.
(require 'testutil-ai-config)
(require 'ai-config)

(defmacro test-ai-config--with-default-tab-width (sentinel &rest body)
  "Bind `default-value' of `tab-width' to SENTINEL during BODY.
Restores the prior default on unwind.  New buffers created inside BODY
inherit SENTINEL as their `tab-width'."
  (declare (indent 1))
  `(let ((saved (default-value 'tab-width)))
     (unwind-protect
         (progn (setq-default tab-width ,sentinel)
                ,@body)
       (setq-default tab-width saved))))

;;; Normal Cases

(ert-deftest test-ai-config-gptel-prompt-tab-width-org-mode-source-sets-8 ()
  "Normal: source buffer in `org-mode' → prompt buffer has `tab-width=8'.
Regression net for the `org-element--list-struct: Tab width must be 8'
error when gptel-magit runs from COMMIT_EDITMSG with
`git-commit-major-mode=org-mode'."
  (test-ai-config--with-default-tab-width 4
    (let ((recorded nil))
      (with-temp-buffer
        (setq major-mode 'org-mode)
        (gptel--with-buffer-copy-internal
         (current-buffer) nil nil
         (lambda () (setq recorded tab-width))))
      (should (= recorded 8)))))

;;; Boundary Cases

(ert-deftest test-ai-config-gptel-prompt-tab-width-text-mode-source-not-overridden ()
  "Boundary: source buffer in `text-mode' → advice does NOT set `tab-width=8'.
Only `org-mode' source triggers the override; other modes keep the
inherited default."
  (test-ai-config--with-default-tab-width 4
    (let ((recorded nil))
      (with-temp-buffer
        (setq major-mode 'text-mode)
        (gptel--with-buffer-copy-internal
         (current-buffer) nil nil
         (lambda () (setq recorded tab-width))))
      (should (= recorded 4)))))

(ert-deftest test-ai-config-gptel-prompt-tab-width-fundamental-mode-source-not-overridden ()
  "Boundary: source buffer in `fundamental-mode' → no override.
Confirms the advice's `(eq major-mode 'org-mode)' gate is strict."
  (test-ai-config--with-default-tab-width 4
    (let ((recorded nil))
      (with-temp-buffer
        (setq major-mode 'fundamental-mode)
        (gptel--with-buffer-copy-internal
         (current-buffer) nil nil
         (lambda () (setq recorded tab-width))))
      (should (= recorded 4)))))

(ert-deftest test-ai-config-gptel-prompt-tab-width-advice-does-not-leak ()
  "Boundary: advice's `setq-local' stays buffer-local to the prompt buffer.
After body-thunk completes and the prompt buffer is killed, the source
buffer's `tab-width' is unchanged."
  (with-temp-buffer
    (setq major-mode 'org-mode)
    (setq-local tab-width 4)
    (gptel--with-buffer-copy-internal
     (current-buffer) nil nil
     (lambda () nil))
    (should (= tab-width 4))))

(provide 'test-ai-config-gptel-prompt-tab-width)
;;; test-ai-config-gptel-prompt-tab-width.el ends here