From 4450efd364ce30f7abca3bcc1ffcda84bd229660 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 16 May 2026 01:39:57 -0500 Subject: test(gptel-tools): cover the helpers across the five remaining tools The gptel-tools files had zero direct coverage outside of `update_text_file`, which landed with its rewrite earlier this session. This commit adds 52 tests across the five other tools. For three of the tools the helpers were already top-level defuns (`read_text_file`, `list_directory_files`, `move_to_trash`). The other two had their main bodies inlined into the `gptel-make-tool` lambda -- I extracted them so the work is testable without mocking gptel itself: read_buffer.el -> `cj/read-buffer--get-content` write_text_file.el -> `cj/write-text-file--run` plus `--validate-path`, `--backup-name`, `--ensure-parent` Test files, by tool: - read_buffer.el (5 tests): normal, empty, buffer-object, text-property-stripping, missing buffer. - write_text_file.el (10 tests): validate-path, backup-name shape, ensure-parent (creates missing / rejects unwritable), run with normal / overwrite / existing-no-overwrite / empty content / outside-home. - read_text_file.el (12 tests): validate-file-path (normal + three error shapes), metadata plist shape, size limits (no-op / hard cap / warning bypass with no-confirm), binary detection (text vs null-byte), special-type EPUB and generic-binary paths. - list_directory_files.el (15 tests): mode-to-permissions (file / dir / executable), get-file-info (file / directory), extension filter (keep / drop / always-dir / nil-extension), format-file- entry, list-directory flat / recursive / error, format-output with and without files. - move_to_trash.el (10 tests): unique-name (no conflict / conflict with timestamp / no-extension), validate-path (HOME / /tmp / outside / critical-dir / missing), perform on file and directory. Each test file uses the same load-path / gptel-stub idiom (`eval-and-compile` block, gptel stub when the real package isn't available) so the byte-compile hook is happy. --- tests/test-gptel-tools-read-text-file.el | 117 +++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 tests/test-gptel-tools-read-text-file.el (limited to 'tests/test-gptel-tools-read-text-file.el') diff --git a/tests/test-gptel-tools-read-text-file.el b/tests/test-gptel-tools-read-text-file.el new file mode 100644 index 00000000..3a4f6662 --- /dev/null +++ b/tests/test-gptel-tools-read-text-file.el @@ -0,0 +1,117 @@ +;;; 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 "~"))) + +;; -------------------------- 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))) + +;; -------------------------- 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-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 -- cgit v1.2.3