blob: db3d6e7ed5b558194a3e83f32a90e9053b4648e8 (
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
;;; test-gptel-tools-read-text-file.el --- Tests for read_text_file gptel tool -*- lexical-binding: t; -*-
;;; Commentary:
;; Tests for the helpers in read_text_file.el.
;;; Code:
(require 'ert)
(require 'cl-lib)
(eval-and-compile
(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory))
(add-to-list 'load-path (expand-file-name "gptel-tools" user-emacs-directory))
(setq load-prefer-newer t)
(unless (featurep 'gptel)
(defvar gptel-tools nil)
(defun gptel-make-tool (&rest _args) nil)
(defun gptel-get-tool (&rest _args) nil)
(provide 'gptel)))
(require 'read_text_file)
;; -------------------------- helpers
(defun test-gptel-tools-read-text-file--in-home (suffix content fn)
"Run FN with a temp file (containing CONTENT) under HOME using SUFFIX."
(let* ((name (format ".test-gptel-tools-read-text-file-%s-%s.tmp"
suffix (format-time-string "%s%N")))
(path (expand-file-name name "~")))
(unwind-protect
(progn
(with-temp-file path (insert content))
(funcall fn path))
(when (file-exists-p path) (delete-file path)))))
;; -------------------------- validate-file-path
(ert-deftest test-gptel-tools-read-text-file-validate-path-normal ()
"Normal: an existing readable file under HOME passes."
(test-gptel-tools-read-text-file--in-home
"normal" "hi"
(lambda (path)
(should (equal (cj/validate-file-path path) (file-truename path))))))
(ert-deftest test-gptel-tools-read-text-file-validate-path-error-outside-home ()
"Error: path outside HOME signals."
(should-error (cj/validate-file-path "/etc/hostname")))
(ert-deftest test-gptel-tools-read-text-file-validate-path-error-missing ()
"Error: missing file signals."
(let ((path (expand-file-name
(format ".test-gptel-tools-read-text-file-missing-%s.tmp"
(format-time-string "%s%N"))
"~")))
(when (file-exists-p path) (delete-file path))
(should-error (cj/validate-file-path path))))
(ert-deftest test-gptel-tools-read-text-file-validate-path-error-directory ()
"Error: a directory signals."
(should-error (cj/validate-file-path "~")))
(ert-deftest test-gptel-tools-read-text-file-validate-path-error-unreadable ()
"Error: unreadable files signal."
(test-gptel-tools-read-text-file--in-home
"unreadable" "secret"
(lambda (path)
(cl-letf (((symbol-function 'file-readable-p) (lambda (_) nil)))
(should-error (cj/validate-file-path path))))))
(ert-deftest test-gptel-tools-read-text-file-validate-path-boundary-relative-home-path ()
"Boundary: relative paths resolve under HOME."
(test-gptel-tools-read-text-file--in-home
"relative" "hi"
(lambda (path)
(let ((relative (file-relative-name path (expand-file-name "~"))))
(should (equal (cj/validate-file-path relative)
(file-truename path)))))))
(ert-deftest test-gptel-tools-read-text-file-validate-path-boundary-symlink-inside-home ()
"Boundary: symlinks inside HOME resolving inside HOME are accepted."
(test-gptel-tools-read-text-file--in-home
"symlink-target" "hi"
(lambda (target)
(let ((link (expand-file-name
(format ".test-gptel-tools-read-text-file-link-%s.tmp"
(format-time-string "%s%N"))
"~")))
(unwind-protect
(progn
(make-symbolic-link target link t)
(should (equal (cj/validate-file-path link)
(file-truename target))))
(when (file-symlink-p link) (delete-file link)))))))
(ert-deftest test-gptel-tools-read-text-file-validate-path-error-symlink-outside-home ()
"Error: symlinks inside HOME pointing outside HOME are rejected."
(let ((outside (make-temp-file "test-gptel-tools-read-text-file-outside-"))
(link (expand-file-name
(format ".test-gptel-tools-read-text-file-outside-link-%s.tmp"
(format-time-string "%s%N"))
"~")))
(unwind-protect
(progn
(make-symbolic-link outside link t)
(should-error (cj/validate-file-path link)))
(when (file-exists-p outside) (delete-file outside))
(when (file-symlink-p link) (delete-file link)))))
;; -------------------------- get-file-metadata
(ert-deftest test-gptel-tools-read-text-file-get-metadata-shape ()
"Returns a plist with :size and :string keys."
(test-gptel-tools-read-text-file--in-home
"meta" "abc"
(lambda (path)
(let ((meta (cj/get-file-metadata path)))
(should (plist-get meta :size))
(should (= 3 (plist-get meta :size)))
(should (stringp (plist-get meta :string)))
(should (string-match-p "modified" (plist-get meta :string)))))))
;; -------------------------- check-file-size-limits
(ert-deftest test-gptel-tools-read-text-file-size-limits-normal ()
"Small size below warning limit is a no-op."
(should-not (cj/check-file-size-limits 1024 nil)))
(ert-deftest test-gptel-tools-read-text-file-size-limits-error-hard-cap ()
"Sizes above 100MB always signal."
(should-error (cj/check-file-size-limits (* 101 1024 1024) t))
(should-error (cj/check-file-size-limits (* 101 1024 1024) nil)))
(ert-deftest test-gptel-tools-read-text-file-size-limits-warning-with-no-confirm ()
"Above 10MB but below 100MB with no-confirm passes through silently."
(should-not (cj/check-file-size-limits (* 11 1024 1024) t)))
(ert-deftest test-gptel-tools-read-text-file-size-limits-warning-user-accepts ()
"Above warning limit proceeds when the user accepts."
(cl-letf (((symbol-function 'y-or-n-p) (lambda (_prompt) t)))
(should-not (cj/check-file-size-limits (* 11 1024 1024) nil))))
(ert-deftest test-gptel-tools-read-text-file-size-limits-warning-user-declines ()
"Above warning limit signals when the user declines."
(cl-letf (((symbol-function 'y-or-n-p) (lambda (_prompt) nil)))
(should-error (cj/check-file-size-limits (* 11 1024 1024) nil))))
;; -------------------------- detect-binary-file
(ert-deftest test-gptel-tools-read-text-file-detect-binary-text-file ()
"Text file: detect-binary returns nil."
(test-gptel-tools-read-text-file--in-home
"text" "plain ascii content"
(lambda (path)
(should-not (cj/detect-binary-file path)))))
(ert-deftest test-gptel-tools-read-text-file-detect-binary-with-null-byte ()
"File with NUL in first 1024 bytes returns truthy."
(test-gptel-tools-read-text-file--in-home
"bin" (concat "head\0tail")
(lambda (path)
(should (cj/detect-binary-file path)))))
;; -------------------------- handle-special-file-types
(ert-deftest test-gptel-tools-read-text-file-handle-special-epub-error ()
"EPUB special-type handler signals \"not yet implemented\"."
(should-error (cj/handle-special-file-types "/tmp/foo.epub" t)))
(ert-deftest test-gptel-tools-read-text-file-handle-special-epub-cancel ()
"EPUB special-type handler signals when user declines extraction."
(cl-letf (((symbol-function 'y-or-n-p) (lambda (_prompt) nil)))
(should-error (cj/handle-special-file-types "/tmp/foo.epub" nil))))
(ert-deftest test-gptel-tools-read-text-file-handle-special-pdf-cancel ()
"PDF special-type handler signals when user declines extraction."
(cl-letf (((symbol-function 'y-or-n-p) (lambda (_prompt) nil)))
(should-error (cj/handle-special-file-types "/tmp/foo.pdf" nil))))
(ert-deftest test-gptel-tools-read-text-file-handle-special-pdf-empty-extraction ()
"PDF special-type handler signals when extraction returns empty text."
(cl-letf (((symbol-function 'shell-command-to-string) (lambda (_cmd) "")))
(should-error (cj/handle-special-file-types "/tmp/foo.pdf" t))))
(ert-deftest test-gptel-tools-read-text-file-handle-special-pdf-text ()
"PDF special-type handler returns extracted text."
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) "pdf text\n")))
(should (equal (cj/handle-special-file-types "/tmp/foo.pdf" t)
"pdf text\n"))))
(ert-deftest test-gptel-tools-read-text-file-handle-special-binary-cancel ()
"Generic binary handler signals when user declines."
(cl-letf (((symbol-function 'y-or-n-p) (lambda (_prompt) nil)))
(should-error (cj/handle-special-file-types "/tmp/foo.bin" nil))))
(ert-deftest test-gptel-tools-read-text-file-handle-special-binary-returns-nil ()
"Generic binary file with no-confirm returns nil to indicate normal read."
(should-not (cj/handle-special-file-types "/tmp/foo.bin" t)))
(provide 'test-gptel-tools-read-text-file)
;;; test-gptel-tools-read-text-file.el ends here
|