From a9a9e56148e9ab83f9abc4e4c531be86974364d1 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 14 May 2026 01:26:28 -0500 Subject: test(ai-config): cover gptel switch/add/toggle commands + small helpers Second pass on ai-config. First batch covered the auth-source, API-key caching, file-context, clear-buffer, context-clear, and model-heading helpers. This batch covers the gptel command wrappers and a couple of small helpers: - `cj/gptel--refresh-org-prefix`: refreshes the org-mode entry in `gptel-prompt-prefix-alist`. - `cj/gptel-backend-and-model`: formats a vector-backend's name + the current model + a timestamp; falls back to "AI:" when no backend is bound. - `cj/gptel-switch-backend`: picks a backend and one of its models, updates both gptel vars; errors on an invalid backend choice. - `cj/gptel-add-buffer-file`: hands the visited file off to `cj/gptel--add-file-to-context`, messages otherwise. - `cj/gptel-add-this-buffer`: calls `gptel-add` with the (4) prefix-arg. - `cj/toggle-gptel`: deletes the window when *AI-Assistant* is already shown. gptel and projectile primitives are stubbed throughout. --- tests/test-ai-config-gptel-commands.el | 152 +++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 tests/test-ai-config-gptel-commands.el (limited to 'tests') diff --git a/tests/test-ai-config-gptel-commands.el b/tests/test-ai-config-gptel-commands.el new file mode 100644 index 00000000..b87c4975 --- /dev/null +++ b/tests/test-ai-config-gptel-commands.el @@ -0,0 +1,152 @@ +;;; test-ai-config-gptel-commands.el --- Tests for ai-config gptel command wrappers -*- lexical-binding: t; -*- + +;;; Commentary: +;; Second pass on ai-config. The first batch covered the helpers +;; (auth-source, api-key caching, add-file, clear-buffer, context- +;; clear, insert-model-heading). This file covers the gptel command +;; wrappers and a few small pure helpers: +;; +;; cj/gptel--refresh-org-prefix +;; cj/gptel-backend-and-model +;; cj/gptel-switch-backend +;; cj/gptel-add-buffer-file +;; cj/gptel-add-this-buffer +;; cj/toggle-gptel +;; +;; The gptel/projectile primitives are stubbed throughout. + +;;; Code: + +(require 'ert) +(require 'cl-lib) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'ai-config) + +;; Dynamic vars gptel would normally own. +(defvar gptel-backend nil) +(defvar gptel-model nil) +(defvar gptel-prompt-prefix-alist nil) + +;;; cj/gptel--refresh-org-prefix + +(ert-deftest test-ai-config-refresh-org-prefix-updates-alist-entry () + "Normal: the advice refreshes the org-mode entry in the prefix alist." + (let ((gptel-prompt-prefix-alist '((org-mode . "stale\n")))) + (cj/gptel--refresh-org-prefix) + (let ((entry (alist-get 'org-mode gptel-prompt-prefix-alist))) + (should (stringp entry)) + ;; Fresh prefix includes the user-login-name + a timestamp bracket. + (should (string-match-p "\\[" entry))))) + +;;; cj/gptel-backend-and-model + +(ert-deftest test-ai-config-backend-and-model-formats-with-vector-backend () + "Normal: a vector backend's name element comes through formatted." + (let ((gptel-backend [unused-slot "Claude" other]) + (gptel-model 'claude-opus-4-6)) + (let ((s (cj/gptel-backend-and-model))) + (should (string-match-p "Claude" s)) + (should (string-match-p "claude-opus-4-6" s))))) + +(ert-deftest test-ai-config-backend-and-model-falls-back-to-ai-when-no-backend () + "Boundary: with no backend bound, the string starts with the AI fallback." + (let ((gptel-backend nil) + (gptel-model nil)) + (should (string-prefix-p "AI:" (cj/gptel-backend-and-model))))) + +;;; cj/gptel-switch-backend + +(ert-deftest test-ai-config-switch-backend-sets-backend-and-model () + "Normal: switch picks a backend + model, then updates the gptel vars." + (let ((gptel-backend nil) + (gptel-model nil) + (msg nil)) + (cl-letf (((symbol-function 'cj/gptel--available-backends) + (lambda () + '(("Anthropic - Claude" . anthropic-backend)))) + ((symbol-function 'gptel-backend-models) + (lambda (_b) '(claude-opus claude-sonnet))) + ((symbol-function 'completing-read) + (lambda (prompt collection &rest _) + ;; First call -> backend choice; second -> model. + (cond + ((string-match-p "backend" prompt) "Anthropic - Claude") + (t "claude-opus")))) + ((symbol-function 'message) + (lambda (fmt &rest args) (setq msg (apply #'format fmt args))))) + (cj/gptel-switch-backend)) + (should (eq gptel-backend 'anthropic-backend)) + (should (equal gptel-model "claude-opus")) + (should (string-match-p "Anthropic - Claude" msg)))) + +(ert-deftest test-ai-config-switch-backend-error-invalid-choice () + "Error: an unrecognized backend name signals user-error." + (cl-letf (((symbol-function 'cj/gptel--available-backends) + (lambda () '(("Anthropic - Claude" . backend-a)))) + ((symbol-function 'completing-read) + (lambda (&rest _) "Something Else"))) + (should-error (cj/gptel-switch-backend) :type 'user-error))) + +;;; cj/gptel-add-buffer-file + +(ert-deftest test-ai-config-add-buffer-file-adds-when-buffer-has-file () + "Normal: a buffer that visits a file -> the file is added to context." + (let ((added nil)) + (with-temp-buffer + (setq buffer-file-name "/tmp/sample.org") + (cl-letf (((symbol-function 'completing-read) + (lambda (&rest _) (buffer-name))) + ((symbol-function 'cj/gptel--add-file-to-context) + (lambda (f) (setq added f) t)) + ((symbol-function 'message) #'ignore)) + (cj/gptel-add-buffer-file)) + (setq buffer-file-name nil)) + (should (equal added "/tmp/sample.org")))) + +(ert-deftest test-ai-config-add-buffer-file-messages-when-no-file () + "Boundary: a buffer not visiting a file -> message, no add call." + (let ((added nil) + (msg nil)) + (with-temp-buffer + (cl-letf (((symbol-function 'completing-read) + (lambda (&rest _) (buffer-name))) + ((symbol-function 'cj/gptel--add-file-to-context) + (lambda (f) (setq added f) t)) + ((symbol-function 'message) + (lambda (fmt &rest args) + (setq msg (apply #'format fmt args))))) + (cj/gptel-add-buffer-file))) + (should-not added) + (should (string-match-p "not visiting" msg)))) + +;;; cj/gptel-add-this-buffer + +(ert-deftest test-ai-config-add-this-buffer-calls-gptel-add-with-prefix () + "Normal: `cj/gptel-add-this-buffer' calls `gptel-add' with the (4) prefix arg." + (let ((arg nil)) + (cl-letf (((symbol-function 'featurep) (lambda (_) t)) + ((symbol-function 'gptel-add) + (lambda (a) (setq arg a))) + ((symbol-function 'message) #'ignore)) + (with-temp-buffer + (cj/gptel-add-this-buffer))) + (should (equal arg '(4))))) + +;;; cj/toggle-gptel + +(ert-deftest test-ai-config-toggle-gptel-closes-when-window-shown () + "Normal: with a window already displaying *AI-Assistant*, toggle deletes it." + (let* ((buf (generate-new-buffer "*AI-Assistant*")) + (deleted nil)) + (unwind-protect + (cl-letf (((symbol-function 'get-buffer-window) + (lambda (_b) 'fake-window)) + ((symbol-function 'delete-window) + (lambda (w) (setq deleted w)))) + (cj/toggle-gptel)) + (when (buffer-live-p buf) (kill-buffer buf))) + (should (eq deleted 'fake-window)))) + +(provide 'test-ai-config-gptel-commands) +;;; test-ai-config-gptel-commands.el ends here -- cgit v1.2.3