branch: externals/ellama
commit 81e17db0f8a6c9a20d395b71a280329710acd8b2
Author: Sergey Kostyaev <[email protected]>
Commit: Sergey Kostyaev <[email protected]>
Add skills tests and update Makefile
Implemented a comprehensive test suite for Ellama skills, covering
front‑matter
parsing, directory scanning, project directory resolution, skill
aggregation,
and prompt generation. Added the new test file
`tests/test-ellama-skills.el` and
updated the Makefile to include it in the test target. Removed duplicate
skill
test definitions from `tests/test-ellama.el` to avoid redundancy.
---
Makefile | 1 +
tests/test-ellama-skills.el | 175 ++++++++++++++++++++++++++++++++++++++++++++
tests/test-ellama.el | 141 -----------------------------------
3 files changed, 176 insertions(+), 141 deletions(-)
diff --git a/Makefile b/Makefile
index 9e1a5fabc8..55f30465e4 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ test:
-l tests/test-ellama.el \
-l tests/test-ellama-context.el \
-l tests/test-ellama-tools.el \
+ -l tests/test-ellama-skills.el \
-l tests/test-ellama-transient.el \
-l tests/test-ellama-blueprint.el \
-l tests/test-ellama-manual.el \
diff --git a/tests/test-ellama-skills.el b/tests/test-ellama-skills.el
new file mode 100644
index 0000000000..08f1a5763c
--- /dev/null
+++ b/tests/test-ellama-skills.el
@@ -0,0 +1,175 @@
+;;; test-ellama-skills.el --- Ellama skills tests -*- lexical-binding: t;
package-lint-main-file: "../ellama.el"; -*-
+
+;; Copyright (C) 2023-2026 Free Software Foundation, Inc.
+
+;; Author: Sergey Kostyaev <[email protected]>
+
+;; This file 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, or (at your option)
+;; any later version.
+
+;; This file 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Ellama skills tests.
+;;
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'ellama)
+(require 'ert)
+
+(ert-deftest test-ellama-skills-parse-frontmatter-valid ()
+ (let ((file (make-temp-file "ellama-skill-frontmatter-" nil ".md")))
+ (unwind-protect
+ (progn
+ (with-temp-file file
+ (insert "---\n")
+ (insert "name: Build Skill\n")
+ (insert "description: Build projects with make.\n")
+ (insert "---\n")
+ (insert "# SKILL\n"))
+ (let ((meta (ellama-skills--parse-frontmatter file)))
+ (should (equal (alist-get 'name meta) "Build Skill"))
+ (should
+ (equal (alist-get 'description meta)
+ "Build projects with make."))))
+ (when (file-exists-p file)
+ (delete-file file)))))
+
+(ert-deftest test-ellama-skills-parse-frontmatter-invalid-or-missing ()
+ (let ((invalid-file (make-temp-file "ellama-skill-invalid-" nil ".md"))
+ (plain-file (make-temp-file "ellama-skill-plain-" nil ".md")))
+ (unwind-protect
+ (progn
+ (with-temp-file invalid-file
+ (insert "---\n")
+ (insert "name: [broken\n")
+ (insert "---\n")
+ (insert "# SKILL\n"))
+ (with-temp-file plain-file
+ (insert "# SKILL\n")
+ (insert "No frontmatter.\n"))
+ (should-not (ellama-skills--parse-frontmatter invalid-file))
+ (should-not (ellama-skills--parse-frontmatter plain-file)))
+ (when (file-exists-p invalid-file)
+ (delete-file invalid-file))
+ (when (file-exists-p plain-file)
+ (delete-file plain-file)))))
+
+(ert-deftest test-ellama-skills-scan-directory-filters-invalid-skills ()
+ (let* ((root (make-temp-file "ellama-skills-root-" t))
+ (valid-dir (expand-file-name "valid-skill" root))
+ (missing-desc-dir (expand-file-name "missing-desc" root))
+ (no-file-dir (expand-file-name "no-skill-file" root))
+ (hidden-dir (expand-file-name ".hidden-skill" root))
+ (valid-file (expand-file-name "SKILL.md" valid-dir))
+ (missing-desc-file (expand-file-name "SKILL.md" missing-desc-dir))
+ (hidden-file (expand-file-name "SKILL.md" hidden-dir)))
+ (unwind-protect
+ (progn
+ (make-directory valid-dir t)
+ (make-directory missing-desc-dir t)
+ (make-directory no-file-dir t)
+ (make-directory hidden-dir t)
+ (with-temp-file valid-file
+ (insert "---\n")
+ (insert "name: Valid Skill\n")
+ (insert "description: Works.\n")
+ (insert "---\n")
+ (insert "# SKILL\n"))
+ (with-temp-file missing-desc-file
+ (insert "---\n")
+ (insert "name: No Description\n")
+ (insert "---\n")
+ (insert "# SKILL\n"))
+ (with-temp-file hidden-file
+ (insert "---\n")
+ (insert "name: Hidden Skill\n")
+ (insert "description: Should be ignored.\n")
+ (insert "---\n")
+ (insert "# SKILL\n"))
+ (let ((skills (ellama-skills--scan-directory root)))
+ (should (= (length skills) 1))
+ (should (equal (ellama-skill-id (car skills)) "valid-skill"))
+ (should
+ (equal (ellama-skill-name (car skills))
+ "Valid Skill"))
+ (should
+ (equal (ellama-skill-description (car skills))
+ "Works."))
+ (should (equal (ellama-skill-path (car skills)) valid-dir))
+ (should (equal (ellama-skill-file-path (car skills))
+ valid-file))))
+ (when (file-exists-p root)
+ (delete-directory root t)))))
+
+(ert-deftest test-ellama-skills-get-project-dir ()
+ (let ((ellama-skills-local-path "my-skills"))
+ (cl-letf (((symbol-function 'ellama-tools-project-root-tool)
+ (lambda () "/tmp/my-project")))
+ (should (equal (ellama-skills-get-project-dir)
+ "/tmp/my-project/my-skills")))
+ (cl-letf (((symbol-function 'ellama-tools-project-root-tool)
+ (lambda () nil)))
+ (should-not (ellama-skills-get-project-dir)))))
+
+(ert-deftest test-ellama-get-skills-appends-global-then-local ()
+ (let ((ellama-skills-global-path "/tmp/global-skills"))
+ (cl-letf (((symbol-function 'ellama-skills-get-project-dir)
+ (lambda () "/tmp/project-skills"))
+ ((symbol-function 'ellama-skills--scan-directory)
+ (lambda (dir)
+ (if (equal dir "/tmp/global-skills")
+ '(global-skill)
+ '(local-skill)))))
+ (should (equal (ellama-get-skills)
+ '(global-skill local-skill))))))
+
+(ert-deftest test-ellama-skills-generate-prompt-empty-list ()
+ (cl-letf (((symbol-function 'ellama-get-skills)
+ (lambda () nil)))
+ (should (equal (ellama-skills-generate-prompt) ""))))
+
+(ert-deftest test-ellama-skills-generate-prompt-renders-skills ()
+ (let* ((skill-a (make-ellama-skill
+ :id "a"
+ :name "Skill A"
+ :description "Do A"
+ :path "/tmp/a"
+ :file-path "/tmp/a/SKILL.md"))
+ (skill-b (make-ellama-skill
+ :id "b"
+ :name "Skill B"
+ :description "Do B"
+ :path "/tmp/b"
+ :file-path "/tmp/b/SKILL.md"))
+ (prompt nil))
+ (cl-letf (((symbol-function 'ellama-get-skills)
+ (lambda () (list skill-a skill-b))))
+ (setq prompt (ellama-skills-generate-prompt)))
+ (should (string-match-p "<available_skills>" prompt))
+ (should (string-match-p "<name>Skill A</name>" prompt))
+ (should (string-match-p "<description>Do A</description>" prompt))
+ (should (string-match-p "<location>/tmp/a/SKILL.md</location>" prompt))
+ (should (string-match-p "<name>Skill B</name>" prompt))
+ (should (string-match-p "<description>Do B</description>" prompt))
+ (should (string-match-p "<location>/tmp/b/SKILL.md</location>" prompt))
+ (should
+ (string-match-p
+ "You have access to the skills listed above\\."
+ prompt))))
+
+
+(provide 'test-ellama-skills)
+
+;;; test-ellama-skills.el ends here
diff --git a/tests/test-ellama.el b/tests/test-ellama.el
index fe95ef9bd6..b6527ef0a2 100644
--- a/tests/test-ellama.el
+++ b/tests/test-ellama.el
@@ -945,147 +945,6 @@ region, season, or type)! 🍎🍊"))))
(should (equal local-callback-text "final")))))
-(ert-deftest test-ellama-skills-parse-frontmatter-valid ()
- (let ((file (make-temp-file "ellama-skill-frontmatter-" nil ".md")))
- (unwind-protect
- (progn
- (with-temp-file file
- (insert "---\n")
- (insert "name: Build Skill\n")
- (insert "description: Build projects with make.\n")
- (insert "---\n")
- (insert "# SKILL\n"))
- (let ((meta (ellama-skills--parse-frontmatter file)))
- (should (equal (alist-get 'name meta) "Build Skill"))
- (should
- (equal (alist-get 'description meta)
- "Build projects with make."))))
- (when (file-exists-p file)
- (delete-file file)))))
-
-(ert-deftest test-ellama-skills-parse-frontmatter-invalid-or-missing ()
- (let ((invalid-file (make-temp-file "ellama-skill-invalid-" nil ".md"))
- (plain-file (make-temp-file "ellama-skill-plain-" nil ".md")))
- (unwind-protect
- (progn
- (with-temp-file invalid-file
- (insert "---\n")
- (insert "name: [broken\n")
- (insert "---\n")
- (insert "# SKILL\n"))
- (with-temp-file plain-file
- (insert "# SKILL\n")
- (insert "No frontmatter.\n"))
- (should-not (ellama-skills--parse-frontmatter invalid-file))
- (should-not (ellama-skills--parse-frontmatter plain-file)))
- (when (file-exists-p invalid-file)
- (delete-file invalid-file))
- (when (file-exists-p plain-file)
- (delete-file plain-file)))))
-
-(ert-deftest test-ellama-skills-scan-directory-filters-invalid-skills ()
- (let* ((root (make-temp-file "ellama-skills-root-" t))
- (valid-dir (expand-file-name "valid-skill" root))
- (missing-desc-dir (expand-file-name "missing-desc" root))
- (no-file-dir (expand-file-name "no-skill-file" root))
- (hidden-dir (expand-file-name ".hidden-skill" root))
- (valid-file (expand-file-name "SKILL.md" valid-dir))
- (missing-desc-file (expand-file-name "SKILL.md" missing-desc-dir))
- (hidden-file (expand-file-name "SKILL.md" hidden-dir)))
- (unwind-protect
- (progn
- (make-directory valid-dir t)
- (make-directory missing-desc-dir t)
- (make-directory no-file-dir t)
- (make-directory hidden-dir t)
- (with-temp-file valid-file
- (insert "---\n")
- (insert "name: Valid Skill\n")
- (insert "description: Works.\n")
- (insert "---\n")
- (insert "# SKILL\n"))
- (with-temp-file missing-desc-file
- (insert "---\n")
- (insert "name: No Description\n")
- (insert "---\n")
- (insert "# SKILL\n"))
- (with-temp-file hidden-file
- (insert "---\n")
- (insert "name: Hidden Skill\n")
- (insert "description: Should be ignored.\n")
- (insert "---\n")
- (insert "# SKILL\n"))
- (let ((skills (ellama-skills--scan-directory root)))
- (should (= (length skills) 1))
- (should (equal (ellama-skill-id (car skills)) "valid-skill"))
- (should
- (equal (ellama-skill-name (car skills))
- "Valid Skill"))
- (should
- (equal (ellama-skill-description (car skills))
- "Works."))
- (should (equal (ellama-skill-path (car skills)) valid-dir))
- (should (equal (ellama-skill-file-path (car skills))
- valid-file))))
- (when (file-exists-p root)
- (delete-directory root t)))))
-
-(ert-deftest test-ellama-skills-get-project-dir ()
- (let ((ellama-skills-local-path "my-skills"))
- (cl-letf (((symbol-function 'ellama-tools-project-root-tool)
- (lambda () "/tmp/my-project")))
- (should (equal (ellama-skills-get-project-dir)
- "/tmp/my-project/my-skills")))
- (cl-letf (((symbol-function 'ellama-tools-project-root-tool)
- (lambda () nil)))
- (should-not (ellama-skills-get-project-dir)))))
-
-(ert-deftest test-ellama-get-skills-appends-global-then-local ()
- (let ((ellama-skills-global-path "/tmp/global-skills"))
- (cl-letf (((symbol-function 'ellama-skills-get-project-dir)
- (lambda () "/tmp/project-skills"))
- ((symbol-function 'ellama-skills--scan-directory)
- (lambda (dir)
- (if (equal dir "/tmp/global-skills")
- '(global-skill)
- '(local-skill)))))
- (should (equal (ellama-get-skills)
- '(global-skill local-skill))))))
-
-(ert-deftest test-ellama-skills-generate-prompt-empty-list ()
- (cl-letf (((symbol-function 'ellama-get-skills)
- (lambda () nil)))
- (should (equal (ellama-skills-generate-prompt) ""))))
-
-(ert-deftest test-ellama-skills-generate-prompt-renders-skills ()
- (let* ((skill-a (make-ellama-skill
- :id "a"
- :name "Skill A"
- :description "Do A"
- :path "/tmp/a"
- :file-path "/tmp/a/SKILL.md"))
- (skill-b (make-ellama-skill
- :id "b"
- :name "Skill B"
- :description "Do B"
- :path "/tmp/b"
- :file-path "/tmp/b/SKILL.md"))
- (prompt nil))
- (cl-letf (((symbol-function 'ellama-get-skills)
- (lambda () (list skill-a skill-b))))
- (setq prompt (ellama-skills-generate-prompt)))
- (should (string-match-p "<available_skills>" prompt))
- (should (string-match-p "<name>Skill A</name>" prompt))
- (should (string-match-p "<description>Do A</description>" prompt))
- (should (string-match-p "<location>/tmp/a/SKILL.md</location>" prompt))
- (should (string-match-p "<name>Skill B</name>" prompt))
- (should (string-match-p "<description>Do B</description>" prompt))
- (should (string-match-p "<location>/tmp/b/SKILL.md</location>" prompt))
- (should
- (string-match-p
- "You have access to the skills listed above\\."
- prompt))))
-
(ert-deftest test-ellama-remove-reasoning ()
(should (equal
(ellama-remove-reasoning "<think>\nabc\n</think>\nFinal")