aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-30 01:07:44 -0500
committerCraig Jennings <c@cjennings.net>2026-04-30 01:07:44 -0500
commitd313c37f14511564849c70c564c14ca51bd4ae7c (patch)
tree315a0bf268c937f406e3060f07325dae9378152c
parenteefd55510cf6b180a7dcc9be40fde894d9adf3ac (diff)
downloadgloss-d313c37f14511564849c70c564c14ca51bd4ae7c.tar.gz
gloss-d313c37f14511564849c70c564c14ca51bd4ae7c.zip
test: add gloss secondary commands test suite (red phase)
Six test files for the remaining stub commands. All 14 tests fail at this commit because the implementations are stubs. `gloss--add-finish-internal' (the pure save side of `gloss-add') gets N/B/E coverage on validation and the persistence side effect. `gloss--stats-text' (the pure stats string formatter) covers empty, populated, and missing-file cases. The interactive commands (`gloss-edit', `gloss-list-terms', `gloss-reload', `gloss-drill-export') get smoke tests only — the design treats them as mode-glue with 70% coverage targets, since prompts and `switch-to-buffer' are framework behaviour Emacs already tests. Two error-path tests assert the message contains a specific substring, not just that `user-error' was raised. The stubs raise `user-error' too, so a bare `should-error' would pass for the wrong reason. The substring check anchors red against the real error path.
-rw-r--r--tests/test-gloss--add-finish-internal.el61
-rw-r--r--tests/test-gloss--drill-export-smoke.el32
-rw-r--r--tests/test-gloss--edit-smoke.el38
-rw-r--r--tests/test-gloss--list-terms-smoke.el35
-rw-r--r--tests/test-gloss--reload-smoke.el24
-rw-r--r--tests/test-gloss--stats-text.el40
6 files changed, 230 insertions, 0 deletions
diff --git a/tests/test-gloss--add-finish-internal.el b/tests/test-gloss--add-finish-internal.el
new file mode 100644
index 0000000..688d6f2
--- /dev/null
+++ b/tests/test-gloss--add-finish-internal.el
@@ -0,0 +1,61 @@
+;;; test-gloss--add-finish-internal.el --- Tests for gloss--add-finish-internal -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests for the pure save-side helper `gloss--add-finish-internal'.
+;; The interactive temp-buffer UI is exercised separately at the smoke
+;; level; this file covers the term/body validation and the persistence
+;; side effect via a real temp glossary.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'gloss)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-add-finish-internal-saves-and-shows ()
+ "Normal: a fresh term + body is saved with source `manual' and shown."
+ (gloss-test--with-missing-glossary
+ (let (shown)
+ (cl-letf (((symbol-function 'gloss-display-show-entry)
+ (lambda (term body) (setq shown (list term body)))))
+ (gloss--add-finish-internal "newterm" "A new definition.")
+ (let ((saved (gloss-core-lookup "newterm")))
+ (should saved)
+ (should (equal (plist-get saved :body) "A new definition."))
+ (should (eq (plist-get saved :source) 'manual)))
+ (should (equal shown '("newterm" "A new definition.")))))))
+
+(ert-deftest test-gloss-add-finish-internal-empty-term-raises ()
+ "Error: empty TERM raises `user-error'."
+ (gloss-test--with-missing-glossary
+ (cl-letf (((symbol-function 'gloss-display-show-entry)
+ (lambda (_ _) nil)))
+ (should-error (gloss--add-finish-internal "" "Body.")
+ :type 'user-error)
+ (should-error (gloss--add-finish-internal " " "Body.")
+ :type 'user-error))))
+
+(ert-deftest test-gloss-add-finish-internal-empty-body-raises ()
+ "Error: empty BODY raises `user-error'."
+ (gloss-test--with-missing-glossary
+ (cl-letf (((symbol-function 'gloss-display-show-entry)
+ (lambda (_ _) nil)))
+ (should-error (gloss--add-finish-internal "term" "")
+ :type 'user-error)
+ (should-error (gloss--add-finish-internal "term" " \n ")
+ :type 'user-error))))
+
+(ert-deftest test-gloss-add-finish-internal-trims-body-whitespace ()
+ "Boundary: leading/trailing whitespace in BODY is trimmed before save."
+ (gloss-test--with-missing-glossary
+ (cl-letf (((symbol-function 'gloss-display-show-entry)
+ (lambda (_ _) nil)))
+ (gloss--add-finish-internal "term" " Body content.\n\n")
+ (let ((saved (gloss-core-lookup "term")))
+ (should (equal (plist-get saved :body) "Body content."))))))
+
+(provide 'test-gloss--add-finish-internal)
+;;; test-gloss--add-finish-internal.el ends here
diff --git a/tests/test-gloss--drill-export-smoke.el b/tests/test-gloss--drill-export-smoke.el
new file mode 100644
index 0000000..55b83e8
--- /dev/null
+++ b/tests/test-gloss--drill-export-smoke.el
@@ -0,0 +1,32 @@
+;;; test-gloss--drill-export-smoke.el --- Smoke test for gloss-drill-export -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Smoke test confirming `gloss-drill-export' is a thin wrapper around
+;; `gloss-drill-export-all' and runs end-to-end against a real glossary.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss)
+(require 'testutil-gloss)
+(require 'testutil-gloss-drill)
+
+(ert-deftest test-gloss-drill-export-tags-every-entry ()
+ "Smoke: drill-export delegates and tags every entry with :drill:."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (gloss-test--with-org-drill-feature
+ (gloss-drill-export)
+ (with-current-buffer (find-file-noselect gloss-file)
+ (revert-buffer t t t)
+ (let ((tagged 0))
+ (org-map-entries
+ (lambda ()
+ (when (and (= 1 (org-current-level))
+ (member "drill" (org-get-tags nil t)))
+ (setq tagged (1+ tagged)))))
+ (should (= tagged 2)))))))
+
+(provide 'test-gloss--drill-export-smoke)
+;;; test-gloss--drill-export-smoke.el ends here
diff --git a/tests/test-gloss--edit-smoke.el b/tests/test-gloss--edit-smoke.el
new file mode 100644
index 0000000..6d1a2a0
--- /dev/null
+++ b/tests/test-gloss--edit-smoke.el
@@ -0,0 +1,38 @@
+;;; test-gloss--edit-smoke.el --- Smoke tests for gloss-edit -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Smoke tests for `gloss-edit'. Switches to the source buffer and
+;; positions point at the heading; installs a buffer-local
+;; `after-save-hook' so cache refresh follows hand edits.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-edit-known-term-positions-at-heading ()
+ "Smoke: edit on a known term puts point at its heading line."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (save-window-excursion
+ (gloss-edit "anaphora")
+ (should (looking-at-p "^\\* anaphora")))))
+
+(ert-deftest test-gloss-edit-installs-buffer-local-cache-refresh-hook ()
+ "Smoke: edit installs `gloss--after-save-refresh-cache' buffer-locally."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (save-window-excursion
+ (gloss-edit "anaphora")
+ (should (memq 'gloss--after-save-refresh-cache after-save-hook))
+ (should (local-variable-p 'after-save-hook)))))
+
+(ert-deftest test-gloss-edit-unknown-term-raises ()
+ "Error: edit on a non-existent term raises `user-error' naming the term."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let ((err (should-error (gloss-edit "no-such-term") :type 'user-error)))
+ (should (string-match-p "no-such-term" (error-message-string err))))))
+
+(provide 'test-gloss--edit-smoke)
+;;; test-gloss--edit-smoke.el ends here
diff --git a/tests/test-gloss--list-terms-smoke.el b/tests/test-gloss--list-terms-smoke.el
new file mode 100644
index 0000000..0e331b6
--- /dev/null
+++ b/tests/test-gloss--list-terms-smoke.el
@@ -0,0 +1,35 @@
+;;; test-gloss--list-terms-smoke.el --- Smoke tests for gloss-list-terms -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Smoke tests for `gloss-list-terms'. Uses `completing-read' on the
+;; cached term list and dispatches to the lookup flow with the chosen
+;; term.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'gloss)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-list-terms-empty-glossary-raises ()
+ "Error: list-terms on an empty glossary raises `user-error' mentioning empty."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (let ((err (should-error (gloss-list-terms) :type 'user-error)))
+ (should (string-match-p "empty" (error-message-string err))))))
+
+(ert-deftest test-gloss-list-terms-dispatches-chosen-to-lookup ()
+ "Smoke: list-terms passes the chosen term to `gloss--lookup-flow'."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let (looked-up)
+ (cl-letf (((symbol-function 'completing-read)
+ (lambda (&rest _) "anaphora"))
+ ((symbol-function 'gloss--lookup-flow)
+ (lambda (term &optional _) (setq looked-up term) :show)))
+ (gloss-list-terms)
+ (should (equal looked-up "anaphora"))))))
+
+(provide 'test-gloss--list-terms-smoke)
+;;; test-gloss--list-terms-smoke.el ends here
diff --git a/tests/test-gloss--reload-smoke.el b/tests/test-gloss--reload-smoke.el
new file mode 100644
index 0000000..14af147
--- /dev/null
+++ b/tests/test-gloss--reload-smoke.el
@@ -0,0 +1,24 @@
+;;; test-gloss--reload-smoke.el --- Smoke test for gloss-reload -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Smoke test for `gloss-reload'. Clears the in-memory cache, then the
+;; next lookup repopulates from disk (handled by core's mtime path).
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-reload-resets-and-repopulates-cache ()
+ "Smoke: reload clears the cache and the next lookup re-reads from disk."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (gloss-core-lookup "anaphora")
+ (should gloss-core--cache)
+ (gloss-reload)
+ (should (gloss-core-lookup "anaphora"))))
+
+(provide 'test-gloss--reload-smoke)
+;;; test-gloss--reload-smoke.el ends here
diff --git a/tests/test-gloss--stats-text.el b/tests/test-gloss--stats-text.el
new file mode 100644
index 0000000..32093b1
--- /dev/null
+++ b/tests/test-gloss--stats-text.el
@@ -0,0 +1,40 @@
+;;; test-gloss--stats-text.el --- Tests for gloss--stats-text -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests for the pure helper `gloss--stats-text' which formats a
+;; multi-line stats summary. Drill-tagged count walks the org file via
+;; the same primitives as `gloss-drill', so this also indirectly
+;; exercises that integration.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-stats-text-empty-glossary-reports-zero ()
+ "Boundary: empty glossary file reports zero terms."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n#+STARTUP: showall\n"
+ (let ((text (gloss--stats-text)))
+ (should (string-match-p "Total terms: +0" text))
+ (should (string-match-p "Drill-tagged: +0" text)))))
+
+(ert-deftest test-gloss-stats-text-populated-glossary-counts-terms ()
+ "Normal: populated glossary reports total terms and source breakdown."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let ((text (gloss--stats-text)))
+ (should (string-match-p "Total terms: +2" text))
+ (should (string-match-p "wiktionary=2" text))
+ (should (string-match-p "File size:" text))
+ (should (string-match-p "Cache mtime:" text)))))
+
+(ert-deftest test-gloss-stats-text-missing-file-reports-zero-and-never ()
+ "Error: missing glossary file reports zero terms and \"never\" mtime."
+ (gloss-test--with-missing-glossary
+ (let ((text (gloss--stats-text)))
+ (should (string-match-p "Total terms: +0" text)))))
+
+(provide 'test-gloss--stats-text)
+;;; test-gloss--stats-text.el ends here