diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-04 12:26:16 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-04 12:26:16 -0500 |
| commit | ea5636c51361b86d132d647ee3548d208394878e (patch) | |
| tree | a60ab847c7324417ca7de46e73d370ba2af90d05 /tests | |
| parent | b74b98f177d92d50ddbede900ba41212e07c5f63 (diff) | |
| download | emacs-wttrin-ea5636c51361b86d132d647ee3548d208394878e.tar.gz emacs-wttrin-ea5636c51361b86d132d647ee3548d208394878e.zip | |
test: add 50 tests for untested functions; fix nil crash in save-debug-data
Add 11 new test files covering wttrin--save-debug-data,
wttrin--buffer-cache-refresh, wttrin--mode-line-stop,
wttrin--mode-line-start, wttrin-query, wttrin-requery-force,
wttrin-mode-line-click, wttrin-mode-line-force-refresh,
wttrin-fetch-raw-string, wttrin-clear-cache, and wttrin-requery.
Fix bug in wttrin--save-debug-data where nil raw-string caused
(insert nil) crash — reachable when debug mode is on and fetch fails.
Refactor wttrin-requery: extract wttrin--requery-location so the
core kill-buffer-and-query logic is testable without mocking
completing-read.
267 tests total (was 217), all passing.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-wttrin--buffer-cache-refresh.el | 120 | ||||
| -rw-r--r-- | tests/test-wttrin--mode-line-start.el | 157 | ||||
| -rw-r--r-- | tests/test-wttrin--mode-line-stop.el | 104 | ||||
| -rw-r--r-- | tests/test-wttrin--save-debug-data.el | 144 | ||||
| -rw-r--r-- | tests/test-wttrin-clear-cache.el | 69 | ||||
| -rw-r--r-- | tests/test-wttrin-fetch-raw-string.el | 65 | ||||
| -rw-r--r-- | tests/test-wttrin-mode-line-click.el | 69 | ||||
| -rw-r--r-- | tests/test-wttrin-mode-line-force-refresh.el | 71 | ||||
| -rw-r--r-- | tests/test-wttrin-query.el | 122 | ||||
| -rw-r--r-- | tests/test-wttrin-requery-force.el | 91 | ||||
| -rw-r--r-- | tests/test-wttrin-requery.el | 152 |
11 files changed, 1164 insertions, 0 deletions
diff --git a/tests/test-wttrin--buffer-cache-refresh.el b/tests/test-wttrin--buffer-cache-refresh.el new file mode 100644 index 0000000..a407450 --- /dev/null +++ b/tests/test-wttrin--buffer-cache-refresh.el @@ -0,0 +1,120 @@ +;;; test-wttrin--buffer-cache-refresh.el --- Tests for wttrin--buffer-cache-refresh -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin--buffer-cache-refresh function. +;; Tests the proactive background refresh that keeps buffer cache fresh. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin--buffer-cache-refresh-setup () + "Setup for buffer-cache-refresh tests." + (testutil-wttrin-setup)) + +(defun test-wttrin--buffer-cache-refresh-teardown () + "Teardown for buffer-cache-refresh tests." + (testutil-wttrin-teardown)) + +;;; Normal Cases + +(ert-deftest test-wttrin--buffer-cache-refresh-normal-success-updates-cache () + "Successful fetch should store fresh data in the buffer cache." + (test-wttrin--buffer-cache-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris")) + (testutil-wttrin-mock-http-response "Fresh weather data for Paris" + (wttrin--buffer-cache-refresh) + ;; Cache should now have an entry for Paris + (let* ((cache-key (wttrin--make-cache-key "Paris")) + (cached (gethash cache-key wttrin--cache))) + (should cached) + (should (equal (cdr cached) "Fresh weather data for Paris"))))) + (test-wttrin--buffer-cache-refresh-teardown))) + +(ert-deftest test-wttrin--buffer-cache-refresh-normal-uses-favorite-location () + "Refresh should fetch weather for the configured favorite location." + (test-wttrin--buffer-cache-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Tokyo, JP") + (fetched-query nil)) + (cl-letf (((symbol-function 'wttrin-fetch-raw-string) + (lambda (query callback) + (setq fetched-query query) + (funcall callback "some data")))) + (wttrin--buffer-cache-refresh) + (should (equal fetched-query "Tokyo, JP")))) + (test-wttrin--buffer-cache-refresh-teardown))) + +(ert-deftest test-wttrin--buffer-cache-refresh-normal-cache-key-respects-unit-system () + "Cache entry should use the correct key based on unit system settings." + (test-wttrin--buffer-cache-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (wttrin-unit-system "m")) + (testutil-wttrin-mock-http-response "metric data" + (wttrin--buffer-cache-refresh) + ;; Cache key should include unit system + (let* ((expected-key (wttrin--make-cache-key "Paris")) + (cached (gethash expected-key wttrin--cache))) + (should cached) + (should (equal (cdr cached) "metric data"))))) + (test-wttrin--buffer-cache-refresh-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--buffer-cache-refresh-boundary-nil-location-is-noop () + "When favorite-location is nil, no fetch should be attempted." + (test-wttrin--buffer-cache-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location nil) + (fetch-called nil)) + (cl-letf (((symbol-function 'wttrin-fetch-raw-string) + (lambda (_query _callback) (setq fetch-called t)))) + (wttrin--buffer-cache-refresh) + (should-not fetch-called) + (should (= 0 (testutil-wttrin-cache-size))))) + (test-wttrin--buffer-cache-refresh-teardown))) + +(ert-deftest test-wttrin--buffer-cache-refresh-boundary-overwrites-stale-entry () + "A refresh should replace any existing stale cache entry for the same location." + (test-wttrin--buffer-cache-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris")) + ;; Seed cache with old data + (testutil-wttrin-add-to-cache "Paris" "old stale data" 9999) + (testutil-wttrin-mock-http-response "fresh new data" + (wttrin--buffer-cache-refresh) + (let* ((cache-key (wttrin--make-cache-key "Paris")) + (cached (gethash cache-key wttrin--cache))) + (should (equal (cdr cached) "fresh new data"))))) + (test-wttrin--buffer-cache-refresh-teardown))) + +;;; Error Cases + +(ert-deftest test-wttrin--buffer-cache-refresh-error-fetch-failure-preserves-cache () + "Failed fetch should not overwrite existing cache entry." + (test-wttrin--buffer-cache-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris")) + ;; Seed cache with existing data + (testutil-wttrin-add-to-cache "Paris" "existing good data" 300) + (cl-letf (((symbol-function 'wttrin-fetch-raw-string) + (lambda (_query callback) (funcall callback nil)))) + (wttrin--buffer-cache-refresh) + ;; Existing cache should be untouched + (let* ((cache-key (wttrin--make-cache-key "Paris")) + (cached (gethash cache-key wttrin--cache))) + (should cached) + (should (equal (cdr cached) "existing good data"))))) + (test-wttrin--buffer-cache-refresh-teardown))) + +(provide 'test-wttrin--buffer-cache-refresh) +;;; test-wttrin--buffer-cache-refresh.el ends here diff --git a/tests/test-wttrin--mode-line-start.el b/tests/test-wttrin--mode-line-start.el new file mode 100644 index 0000000..e97c185 --- /dev/null +++ b/tests/test-wttrin--mode-line-start.el @@ -0,0 +1,157 @@ +;;; test-wttrin--mode-line-start.el --- Tests for wttrin--mode-line-start -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin--mode-line-start function. +;; Tests that starting mode-line display sets up placeholder, timers, and scheduling. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin--mode-line-start-setup () + "Setup for mode-line-start tests." + (testutil-wttrin-setup) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-cache nil) + (setq wttrin--mode-line-timer nil) + (setq wttrin--buffer-refresh-timer nil)) + +(defun test-wttrin--mode-line-start-teardown () + "Teardown for mode-line-start tests." + (testutil-wttrin-teardown) + (when (timerp wttrin--mode-line-timer) + (cancel-timer wttrin--mode-line-timer)) + (when (timerp wttrin--buffer-refresh-timer) + (cancel-timer wttrin--buffer-refresh-timer)) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-cache nil) + (setq wttrin--mode-line-timer nil) + (setq wttrin--buffer-refresh-timer nil)) + +;;; Normal Cases + +(ert-deftest test-wttrin--mode-line-start-normal-shows-placeholder-immediately () + "Starting mode-line should show the hourglass placeholder right away." + (test-wttrin--mode-line-start-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (wttrin-mode-line-emoji-font nil)) + ;; Mock run-at-time so no real timers fire + (cl-letf (((symbol-function 'run-at-time) + (lambda (_time _repeat _func) (list 'mock-timer)))) + (wttrin--mode-line-start) + ;; Placeholder should be visible + (should wttrin-mode-line-string) + (should (string-match-p "⏳" (substring-no-properties wttrin-mode-line-string))))) + (test-wttrin--mode-line-start-teardown))) + +(ert-deftest test-wttrin--mode-line-start-normal-schedules-delayed-initial-fetch () + "Initial weather fetch should be scheduled after startup-delay seconds." + (test-wttrin--mode-line-start-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (wttrin-mode-line-startup-delay 5) + (scheduled-calls nil)) + (cl-letf (((symbol-function 'run-at-time) + (lambda (time repeat func) + (push (list :time time :repeat repeat :func func) scheduled-calls) + (list 'mock-timer)))) + (wttrin--mode-line-start) + ;; One of the calls should be the delayed initial fetch + (let ((initial-fetch (seq-find (lambda (call) + (and (= (plist-get call :time) 5) + (null (plist-get call :repeat)))) + scheduled-calls))) + (should initial-fetch) + (should (eq (plist-get initial-fetch :func) + #'wttrin--mode-line-fetch-weather))))) + (test-wttrin--mode-line-start-teardown))) + +(ert-deftest test-wttrin--mode-line-start-normal-creates-repeating-mode-line-timer () + "A repeating timer should be created for periodic mode-line refresh." + (test-wttrin--mode-line-start-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (wttrin-mode-line-refresh-interval 3600) + (scheduled-calls nil)) + (cl-letf (((symbol-function 'run-at-time) + (lambda (time repeat func) + (push (list :time time :repeat repeat :func func) scheduled-calls) + (list 'mock-timer)))) + (wttrin--mode-line-start) + ;; Should have a repeating timer for mode-line fetch + (let ((repeating-fetch (seq-find (lambda (call) + (and (equal (plist-get call :repeat) 3600) + (eq (plist-get call :func) + #'wttrin--mode-line-fetch-weather))) + scheduled-calls))) + (should repeating-fetch)))) + (test-wttrin--mode-line-start-teardown))) + +(ert-deftest test-wttrin--mode-line-start-normal-creates-buffer-refresh-timer () + "A repeating timer should be created for periodic buffer cache refresh." + (test-wttrin--mode-line-start-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (wttrin-refresh-interval 3600) + (scheduled-calls nil)) + (cl-letf (((symbol-function 'run-at-time) + (lambda (time repeat func) + (push (list :time time :repeat repeat :func func) scheduled-calls) + (list 'mock-timer)))) + (wttrin--mode-line-start) + ;; Should have a repeating timer for buffer cache refresh + (let ((buffer-refresh (seq-find (lambda (call) + (and (equal (plist-get call :repeat) 3600) + (eq (plist-get call :func) + #'wttrin--buffer-cache-refresh))) + scheduled-calls))) + (should buffer-refresh)))) + (test-wttrin--mode-line-start-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--mode-line-start-boundary-nil-location-skips-setup () + "When no favorite location is set, no timers or placeholder should be created." + (test-wttrin--mode-line-start-setup) + (unwind-protect + (let ((wttrin-favorite-location nil) + (run-at-time-called nil)) + (cl-letf (((symbol-function 'run-at-time) + (lambda (_time _repeat _func) + (setq run-at-time-called t) + (list 'mock-timer)))) + (wttrin--mode-line-start) + (should-not run-at-time-called) + (should-not wttrin-mode-line-string) + (should-not wttrin--mode-line-timer))) + (test-wttrin--mode-line-start-teardown))) + +(ert-deftest test-wttrin--mode-line-start-boundary-cancels-existing-timers () + "Starting when timers already exist should cancel old timers before creating new ones." + (test-wttrin--mode-line-start-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (cancelled-timers nil)) + ;; Create fake existing timers + (setq wttrin--mode-line-timer (run-at-time 99999 nil #'ignore)) + (setq wttrin--buffer-refresh-timer (run-at-time 99999 nil #'ignore)) + (let ((old-ml-timer wttrin--mode-line-timer) + (old-buf-timer wttrin--buffer-refresh-timer)) + (cl-letf (((symbol-function 'run-at-time) + (lambda (_time _repeat _func) (list 'mock-timer)))) + (wttrin--mode-line-start) + ;; Old timers should have been replaced + (should-not (eq wttrin--mode-line-timer old-ml-timer)) + (should-not (eq wttrin--buffer-refresh-timer old-buf-timer))))) + (test-wttrin--mode-line-start-teardown))) + +(provide 'test-wttrin--mode-line-start) +;;; test-wttrin--mode-line-start.el ends here diff --git a/tests/test-wttrin--mode-line-stop.el b/tests/test-wttrin--mode-line-stop.el new file mode 100644 index 0000000..e791ee5 --- /dev/null +++ b/tests/test-wttrin--mode-line-stop.el @@ -0,0 +1,104 @@ +;;; test-wttrin--mode-line-stop.el --- Tests for wttrin--mode-line-stop -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin--mode-line-stop function. +;; Tests that stopping mode-line display cleans up all state properly. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin--mode-line-stop-setup () + "Setup for mode-line-stop tests." + (testutil-wttrin-setup) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-cache nil) + (setq wttrin--mode-line-timer nil) + (setq wttrin--buffer-refresh-timer nil)) + +(defun test-wttrin--mode-line-stop-teardown () + "Teardown for mode-line-stop tests." + (testutil-wttrin-teardown) + ;; Cancel any real timers that may have been created + (when (timerp wttrin--mode-line-timer) + (cancel-timer wttrin--mode-line-timer)) + (when (timerp wttrin--buffer-refresh-timer) + (cancel-timer wttrin--buffer-refresh-timer)) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-cache nil) + (setq wttrin--mode-line-timer nil) + (setq wttrin--buffer-refresh-timer nil)) + +;;; Normal Cases + +(ert-deftest test-wttrin--mode-line-stop-normal-clears-mode-line-string () + "After stop, mode-line-string should be nil so nothing shows in the mode-line." + (test-wttrin--mode-line-stop-setup) + (unwind-protect + (progn + (setq wttrin-mode-line-string "some weather display") + (wttrin--mode-line-stop) + (should-not wttrin-mode-line-string)) + (test-wttrin--mode-line-stop-teardown))) + +(ert-deftest test-wttrin--mode-line-stop-normal-clears-mode-line-cache () + "After stop, cached mode-line data should be discarded." + (test-wttrin--mode-line-stop-setup) + (unwind-protect + (progn + (setq wttrin--mode-line-cache (cons (float-time) "Paris: ☀️ +61°F Clear")) + (wttrin--mode-line-stop) + (should-not wttrin--mode-line-cache)) + (test-wttrin--mode-line-stop-teardown))) + +(ert-deftest test-wttrin--mode-line-stop-normal-cancels-mode-line-timer () + "After stop, the mode-line refresh timer should be cancelled and nil." + (test-wttrin--mode-line-stop-setup) + (unwind-protect + (progn + ;; Create a real timer so cancel-timer has something to work with + (setq wttrin--mode-line-timer + (run-at-time 99999 nil #'ignore)) + (wttrin--mode-line-stop) + (should-not wttrin--mode-line-timer)) + (test-wttrin--mode-line-stop-teardown))) + +(ert-deftest test-wttrin--mode-line-stop-normal-cancels-buffer-refresh-timer () + "After stop, the buffer-refresh timer should be cancelled and nil." + (test-wttrin--mode-line-stop-setup) + (unwind-protect + (progn + (setq wttrin--buffer-refresh-timer + (run-at-time 99999 nil #'ignore)) + (wttrin--mode-line-stop) + (should-not wttrin--buffer-refresh-timer)) + (test-wttrin--mode-line-stop-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--mode-line-stop-boundary-safe-when-already-stopped () + "Calling stop when everything is already nil should not error." + (test-wttrin--mode-line-stop-setup) + (unwind-protect + (progn + ;; Everything is already nil from setup + (should-not wttrin--mode-line-timer) + (should-not wttrin--buffer-refresh-timer) + (should-not wttrin-mode-line-string) + (should-not wttrin--mode-line-cache) + ;; This should not error + (wttrin--mode-line-stop) + ;; Still nil + (should-not wttrin--mode-line-timer) + (should-not wttrin-mode-line-string)) + (test-wttrin--mode-line-stop-teardown))) + +(provide 'test-wttrin--mode-line-stop) +;;; test-wttrin--mode-line-stop.el ends here diff --git a/tests/test-wttrin--save-debug-data.el b/tests/test-wttrin--save-debug-data.el new file mode 100644 index 0000000..b480329 --- /dev/null +++ b/tests/test-wttrin--save-debug-data.el @@ -0,0 +1,144 @@ +;;; test-wttrin--save-debug-data.el --- Tests for wttrin--save-debug-data -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin--save-debug-data function. +;; Tests that debug data files are created correctly with the expected contents. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin--save-debug-data-setup () + "Setup for save-debug-data tests." + (testutil-wttrin-setup)) + +(defun test-wttrin--save-debug-data-teardown () + "Teardown for save-debug-data tests." + (testutil-wttrin-teardown)) + +;;; Normal Cases + +(ert-deftest test-wttrin--save-debug-data-normal-creates-file-that-exists () + "Returned filepath should point to a file that actually exists on disk." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let ((filepath (wttrin--save-debug-data "Paris" "some weather data"))) + (unwind-protect + (should (file-exists-p filepath)) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +(ert-deftest test-wttrin--save-debug-data-normal-file-contains-location () + "File should contain a Location header with the queried location." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let ((filepath (wttrin--save-debug-data "New Orleans, LA" "weather"))) + (unwind-protect + (let ((contents (with-temp-buffer + (insert-file-contents filepath) + (buffer-string)))) + (should (string-match-p "^Location: New Orleans, LA$" contents))) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +(ert-deftest test-wttrin--save-debug-data-normal-file-contains-unit-system () + "File should record the wttrin-unit-system value at the time of capture." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let* ((wttrin-unit-system "m") + (filepath (wttrin--save-debug-data "Paris" "weather data"))) + (unwind-protect + (let ((contents (with-temp-buffer + (insert-file-contents filepath) + (buffer-string)))) + (should (string-match-p "wttrin-unit-system: m" contents))) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +(ert-deftest test-wttrin--save-debug-data-normal-file-contains-raw-response () + "File should contain the raw weather response body after the separator." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let ((filepath (wttrin--save-debug-data "Berlin" "Clear skies 72°F"))) + (unwind-protect + (let ((contents (with-temp-buffer + (insert-file-contents filepath) + (buffer-string)))) + (should (string-match-p "--- Raw Response ---" contents)) + (should (string-match-p "Clear skies 72°F" contents))) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +(ert-deftest test-wttrin--save-debug-data-normal-file-contains-timestamp () + "File should contain a human-readable timestamp." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let ((filepath (wttrin--save-debug-data "Tokyo" "data"))) + (unwind-protect + (let ((contents (with-temp-buffer + (insert-file-contents filepath) + (buffer-string)))) + ;; Timestamp should look like YYYY-MM-DD HH:MM:SS + (should (string-match-p "^Timestamp: [0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}$" + contents))) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--save-debug-data-boundary-unicode-location () + "File should preserve unicode characters in the location name." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let ((filepath (wttrin--save-debug-data "São Paulo, BR" "data"))) + (unwind-protect + (let ((contents (with-temp-buffer + (insert-file-contents filepath) + (buffer-string)))) + (should (string-match-p "São Paulo" contents))) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +(ert-deftest test-wttrin--save-debug-data-boundary-empty-raw-string () + "Empty raw-string should still produce a valid file (just no response body)." + (test-wttrin--save-debug-data-setup) + (unwind-protect + (let ((filepath (wttrin--save-debug-data "Paris" ""))) + (unwind-protect + (progn + (should (file-exists-p filepath)) + (let ((contents (with-temp-buffer + (insert-file-contents filepath) + (buffer-string)))) + ;; Should still have the headers and separator + (should (string-match-p "Location: Paris" contents)) + (should (string-match-p "--- Raw Response ---" contents)))) + (when (file-exists-p filepath) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +;;; Error Cases + +(ert-deftest test-wttrin--save-debug-data-error-nil-raw-string-should-not-crash () + "Nil raw-string should be handled gracefully, not cause an insert error. +This can happen when wttrin--display-weather is called with nil data +and debug mode is on — save-debug-data is called before validation." + (test-wttrin--save-debug-data-setup) + (unwind-protect + ;; wttrin--save-debug-data calls (insert raw-string) which errors on nil. + ;; This test documents the bug: a nil raw-string should produce a file + ;; with an empty or placeholder response body, not crash. + (let ((filepath (wttrin--save-debug-data "Paris" nil))) + (unwind-protect + (should (file-exists-p filepath)) + (when (and filepath (file-exists-p filepath)) (delete-file filepath)))) + (test-wttrin--save-debug-data-teardown))) + +(provide 'test-wttrin--save-debug-data) +;;; test-wttrin--save-debug-data.el ends here diff --git a/tests/test-wttrin-clear-cache.el b/tests/test-wttrin-clear-cache.el new file mode 100644 index 0000000..3404cf8 --- /dev/null +++ b/tests/test-wttrin-clear-cache.el @@ -0,0 +1,69 @@ +;;; test-wttrin-clear-cache.el --- Tests for wttrin-clear-cache -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin-clear-cache function. +;; Tests the interactive command that clears all cached weather data. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-clear-cache-setup () + "Setup for clear-cache tests." + (testutil-wttrin-setup)) + +(defun test-wttrin-clear-cache-teardown () + "Teardown for clear-cache tests." + (testutil-wttrin-teardown)) + +;;; Normal Cases + +(ert-deftest test-wttrin-clear-cache-normal-empties-all-entries () + "Clearing the cache should remove all stored weather entries." + (test-wttrin-clear-cache-setup) + (unwind-protect + (progn + ;; Populate cache with several entries + (testutil-wttrin-add-to-cache "Paris" "data1") + (testutil-wttrin-add-to-cache "London" "data2") + (testutil-wttrin-add-to-cache "Tokyo" "data3") + (should (= 3 (testutil-wttrin-cache-size))) + (wttrin-clear-cache) + (should (= 0 (testutil-wttrin-cache-size)))) + (test-wttrin-clear-cache-teardown))) + +(ert-deftest test-wttrin-clear-cache-normal-shows-confirmation () + "User should be told the cache was cleared." + (test-wttrin-clear-cache-setup) + (unwind-protect + (let ((displayed-message nil)) + (cl-letf (((symbol-function 'message) + (lambda (fmt &rest args) + (setq displayed-message (apply #'format fmt args))))) + (wttrin-clear-cache) + (should displayed-message) + (should (string-match-p "cache cleared" displayed-message)))) + (test-wttrin-clear-cache-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin-clear-cache-boundary-already-empty () + "Clearing an already empty cache should not error." + (test-wttrin-clear-cache-setup) + (unwind-protect + (progn + (should (= 0 (testutil-wttrin-cache-size))) + ;; Should not error + (wttrin-clear-cache) + (should (= 0 (testutil-wttrin-cache-size)))) + (test-wttrin-clear-cache-teardown))) + +(provide 'test-wttrin-clear-cache) +;;; test-wttrin-clear-cache.el ends here diff --git a/tests/test-wttrin-fetch-raw-string.el b/tests/test-wttrin-fetch-raw-string.el new file mode 100644 index 0000000..963fd29 --- /dev/null +++ b/tests/test-wttrin-fetch-raw-string.el @@ -0,0 +1,65 @@ +;;; test-wttrin-fetch-raw-string.el --- Tests for wttrin-fetch-raw-string -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin-fetch-raw-string function. +;; Tests the public API for fetching weather data by location query. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-fetch-raw-string-setup () + "Setup for fetch-raw-string tests." + (testutil-wttrin-setup)) + +(defun test-wttrin-fetch-raw-string-teardown () + "Teardown for fetch-raw-string tests." + (testutil-wttrin-teardown)) + +;;; Normal Cases + +(ert-deftest test-wttrin-fetch-raw-string-normal-builds-correct-url () + "The fetch should use a properly constructed wttr.in URL for the query." + (test-wttrin-fetch-raw-string-setup) + (unwind-protect + (let ((fetched-url nil)) + (cl-letf (((symbol-function 'wttrin--fetch-url) + (lambda (url _callback) (setq fetched-url url)))) + (wttrin-fetch-raw-string "Paris" #'ignore) + ;; URL should contain wttr.in and the encoded location + (should (string-match-p "wttr\\.in" fetched-url)) + (should (string-match-p "Paris" fetched-url)))) + (test-wttrin-fetch-raw-string-teardown))) + +(ert-deftest test-wttrin-fetch-raw-string-normal-passes-callback-through () + "The user's callback should receive the fetched data." + (test-wttrin-fetch-raw-string-setup) + (unwind-protect + (let ((received-data nil)) + (cl-letf (((symbol-function 'wttrin--fetch-url) + (lambda (_url callback) + (funcall callback "weather response")))) + (wttrin-fetch-raw-string "Paris" + (lambda (data) (setq received-data data))) + (should (equal received-data "weather response")))) + (test-wttrin-fetch-raw-string-teardown))) + +;;; Error Cases + +(ert-deftest test-wttrin-fetch-raw-string-error-nil-query-signals-error () + "Passing nil as query should signal an error (invalid URL construction)." + (test-wttrin-fetch-raw-string-setup) + (unwind-protect + (should-error (wttrin-fetch-raw-string nil #'ignore) + :type 'error) + (test-wttrin-fetch-raw-string-teardown))) + +(provide 'test-wttrin-fetch-raw-string) +;;; test-wttrin-fetch-raw-string.el ends here diff --git a/tests/test-wttrin-mode-line-click.el b/tests/test-wttrin-mode-line-click.el new file mode 100644 index 0000000..fccd45e --- /dev/null +++ b/tests/test-wttrin-mode-line-click.el @@ -0,0 +1,69 @@ +;;; test-wttrin-mode-line-click.el --- Tests for wttrin-mode-line-click -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin-mode-line-click function. +;; Tests the left-click handler for the mode-line weather widget. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-mode-line-click-setup () + "Setup for mode-line-click tests." + (testutil-wttrin-setup)) + +(defun test-wttrin-mode-line-click-teardown () + "Teardown for mode-line-click tests." + (testutil-wttrin-teardown) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +;;; Normal Cases + +(ert-deftest test-wttrin-mode-line-click-normal-opens-weather-for-favorite () + "Clicking the mode-line widget should open weather for the favorite location." + (test-wttrin-mode-line-click-setup) + (unwind-protect + (let ((wttrin-favorite-location "New Orleans, LA") + (opened-location nil)) + (cl-letf (((symbol-function 'wttrin) + (lambda (location) (setq opened-location location)))) + (wttrin-mode-line-click) + (should (equal opened-location "New Orleans, LA")))) + (test-wttrin-mode-line-click-teardown))) + +(ert-deftest test-wttrin-mode-line-click-normal-passes-exact-location-string () + "The exact favorite location string should be passed to wttrin, not modified." + (test-wttrin-mode-line-click-setup) + (unwind-protect + (let ((wttrin-favorite-location "São Paulo, BR") + (opened-location nil)) + (cl-letf (((symbol-function 'wttrin) + (lambda (location) (setq opened-location location)))) + (wttrin-mode-line-click) + (should (equal opened-location "São Paulo, BR")))) + (test-wttrin-mode-line-click-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin-mode-line-click-boundary-nil-location-is-noop () + "When no favorite location is configured, clicking should do nothing." + (test-wttrin-mode-line-click-setup) + (unwind-protect + (let ((wttrin-favorite-location nil) + (wttrin-called nil)) + (cl-letf (((symbol-function 'wttrin) + (lambda (_location) (setq wttrin-called t)))) + (wttrin-mode-line-click) + (should-not wttrin-called))) + (test-wttrin-mode-line-click-teardown))) + +(provide 'test-wttrin-mode-line-click) +;;; test-wttrin-mode-line-click.el ends here diff --git a/tests/test-wttrin-mode-line-force-refresh.el b/tests/test-wttrin-mode-line-force-refresh.el new file mode 100644 index 0000000..2275cee --- /dev/null +++ b/tests/test-wttrin-mode-line-force-refresh.el @@ -0,0 +1,71 @@ +;;; test-wttrin-mode-line-force-refresh.el --- Tests for wttrin-mode-line-force-refresh -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin-mode-line-force-refresh function. +;; Tests the right-click handler that force-refreshes mode-line weather. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-mode-line-force-refresh-setup () + "Setup for mode-line-force-refresh tests." + (testutil-wttrin-setup) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-cache nil)) + +(defun test-wttrin-mode-line-force-refresh-teardown () + "Teardown for mode-line-force-refresh tests." + (testutil-wttrin-teardown) + (setq wttrin-mode-line-string nil) + (setq wttrin--mode-line-cache nil)) + +;;; Normal Cases + +(ert-deftest test-wttrin-mode-line-force-refresh-normal-calls-fetch () + "Right-click should trigger a weather fetch." + (test-wttrin-mode-line-force-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (fetch-called nil)) + (cl-letf (((symbol-function 'wttrin--mode-line-fetch-weather) + (lambda () (setq fetch-called t)))) + (wttrin-mode-line-force-refresh) + (should fetch-called))) + (test-wttrin-mode-line-force-refresh-teardown))) + +(ert-deftest test-wttrin-mode-line-force-refresh-normal-sets-force-flag () + "The fetch should run with force-refresh bound to t to bypass cache." + (test-wttrin-mode-line-force-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location "Paris") + (force-flag-during-fetch nil)) + (cl-letf (((symbol-function 'wttrin--mode-line-fetch-weather) + (lambda () (setq force-flag-during-fetch wttrin--force-refresh)))) + (wttrin-mode-line-force-refresh) + (should force-flag-during-fetch))) + (test-wttrin-mode-line-force-refresh-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin-mode-line-force-refresh-boundary-nil-location-is-noop () + "When no favorite location is set, right-click should do nothing." + (test-wttrin-mode-line-force-refresh-setup) + (unwind-protect + (let ((wttrin-favorite-location nil) + (fetch-called nil)) + (cl-letf (((symbol-function 'wttrin--mode-line-fetch-weather) + (lambda () (setq fetch-called t)))) + (wttrin-mode-line-force-refresh) + (should-not fetch-called))) + (test-wttrin-mode-line-force-refresh-teardown))) + +(provide 'test-wttrin-mode-line-force-refresh) +;;; test-wttrin-mode-line-force-refresh.el ends here diff --git a/tests/test-wttrin-query.el b/tests/test-wttrin-query.el new file mode 100644 index 0000000..a44040a --- /dev/null +++ b/tests/test-wttrin-query.el @@ -0,0 +1,122 @@ +;;; test-wttrin-query.el --- Tests for wttrin-query -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin-query function. +;; Tests the async weather query orchestration: buffer creation, +;; loading state, cache lookup, and display callback. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-query-setup () + "Setup for wttrin-query tests." + (testutil-wttrin-setup) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +(defun test-wttrin-query-teardown () + "Teardown for wttrin-query tests." + (testutil-wttrin-teardown) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +;;; Normal Cases + +(ert-deftest test-wttrin-query-normal-creates-buffer () + "Calling query should create the *wttr.in* buffer." + (test-wttrin-query-setup) + (unwind-protect + (cl-letf (((symbol-function 'wttrin--get-cached-or-fetch) + (lambda (_location _callback) nil))) + (wttrin-query "Paris") + (should (get-buffer "*wttr.in*"))) + (test-wttrin-query-teardown))) + +(ert-deftest test-wttrin-query-normal-shows-loading-message () + "Buffer should show a loading message with the location name while fetching." + (test-wttrin-query-setup) + (unwind-protect + (cl-letf (((symbol-function 'wttrin--get-cached-or-fetch) + (lambda (_location _callback) nil))) + (wttrin-query "New Orleans, LA") + (with-current-buffer "*wttr.in*" + (let ((contents (buffer-string))) + (should (string-match-p "Loading" contents)) + (should (string-match-p "New Orleans, LA" contents))))) + (test-wttrin-query-teardown))) + +(ert-deftest test-wttrin-query-normal-buffer-is-read-only-during-loading () + "The loading buffer should be read-only to prevent user edits." + (test-wttrin-query-setup) + (unwind-protect + (cl-letf (((symbol-function 'wttrin--get-cached-or-fetch) + (lambda (_location _callback) nil))) + (wttrin-query "Tokyo") + (with-current-buffer "*wttr.in*" + (should buffer-read-only))) + (test-wttrin-query-teardown))) + +(ert-deftest test-wttrin-query-normal-fetches-for-correct-location () + "Query should request weather for the specified location." + (test-wttrin-query-setup) + (unwind-protect + (let ((fetched-location nil)) + (cl-letf (((symbol-function 'wttrin--get-cached-or-fetch) + (lambda (location _callback) + (setq fetched-location location)))) + (wttrin-query "Berlin, DE") + (should (equal fetched-location "Berlin, DE")))) + (test-wttrin-query-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin-query-boundary-dead-buffer-callback-is-safe () + "If the buffer is killed before the async callback fires, it should not error." + (test-wttrin-query-setup) + (unwind-protect + (let ((saved-callback nil)) + (cl-letf (((symbol-function 'wttrin--get-cached-or-fetch) + (lambda (_location callback) + (setq saved-callback callback)))) + (wttrin-query "Paris") + ;; Kill the buffer before callback fires + (kill-buffer "*wttr.in*") + ;; Invoke callback — should not error + (should-not (get-buffer "*wttr.in*")) + (funcall saved-callback "weather data") + ;; Buffer should NOT be recreated + (should-not (get-buffer "*wttr.in*")))) + (test-wttrin-query-teardown))) + +;;; Error Cases + +(ert-deftest test-wttrin-query-error-nil-response-shows-error-message () + "When fetch returns nil, the user should see an error message, not a crash." + (test-wttrin-query-setup) + (unwind-protect + (let ((saved-callback nil) + (displayed-message nil)) + (cl-letf (((symbol-function 'wttrin--get-cached-or-fetch) + (lambda (_location callback) + (setq saved-callback callback))) + ((symbol-function 'message) + (lambda (fmt &rest args) + (setq displayed-message (apply #'format fmt args))))) + (wttrin-query "BadLocation") + ;; Simulate fetch returning nil + (funcall saved-callback nil) + ;; Should have shown error message (from wttrin--display-weather validation) + (should displayed-message) + (should (string-match-p "Cannot retrieve" displayed-message)))) + (test-wttrin-query-teardown))) + +(provide 'test-wttrin-query) +;;; test-wttrin-query.el ends here diff --git a/tests/test-wttrin-requery-force.el b/tests/test-wttrin-requery-force.el new file mode 100644 index 0000000..04ad40a --- /dev/null +++ b/tests/test-wttrin-requery-force.el @@ -0,0 +1,91 @@ +;;; test-wttrin-requery-force.el --- Tests for wttrin-requery-force -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin-requery-force function. +;; Tests the force-refresh behavior that bypasses cache for the current location. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-requery-force-setup () + "Setup for requery-force tests." + (testutil-wttrin-setup) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +(defun test-wttrin-requery-force-teardown () + "Teardown for requery-force tests." + (testutil-wttrin-teardown) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +;;; Normal Cases + +(ert-deftest test-wttrin-requery-force-normal-queries-current-location () + "Force refresh should query weather for the buffer's current location." + (test-wttrin-requery-force-setup) + (unwind-protect + (let ((queried-location nil)) + (cl-letf (((symbol-function 'wttrin-query) + (lambda (location) (setq queried-location location)))) + ;; Set up a weather buffer with a known location + (with-current-buffer (get-buffer-create "*wttr.in*") + (setq-local wttrin--current-location "Berlin, DE") + (wttrin-requery-force) + (should (equal queried-location "Berlin, DE"))))) + (test-wttrin-requery-force-teardown))) + +(ert-deftest test-wttrin-requery-force-normal-sets-force-refresh-flag () + "The force-refresh flag should be true when the query executes." + (test-wttrin-requery-force-setup) + (unwind-protect + (let ((force-refresh-was-set nil)) + (cl-letf (((symbol-function 'wttrin-query) + (lambda (_location) + (setq force-refresh-was-set wttrin--force-refresh)))) + (with-current-buffer (get-buffer-create "*wttr.in*") + (setq-local wttrin--current-location "Paris") + (wttrin-requery-force) + (should force-refresh-was-set)))) + (test-wttrin-requery-force-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin-requery-force-boundary-no-location-shows-message () + "When no current location is set, user should be told there's nothing to refresh." + (test-wttrin-requery-force-setup) + (unwind-protect + (let ((displayed-message nil)) + (cl-letf (((symbol-function 'message) + (lambda (fmt &rest args) + (setq displayed-message (apply #'format fmt args))))) + (with-current-buffer (get-buffer-create "*wttr.in*") + ;; wttrin--current-location is nil (buffer-local default) + (wttrin-requery-force) + (should displayed-message) + (should (string-match-p "No location" displayed-message))))) + (test-wttrin-requery-force-teardown))) + +(ert-deftest test-wttrin-requery-force-boundary-force-flag-does-not-leak () + "The force-refresh flag should not persist after the requery completes." + (test-wttrin-requery-force-setup) + (unwind-protect + (progn + (cl-letf (((symbol-function 'wttrin-query) (lambda (_location) nil))) + (with-current-buffer (get-buffer-create "*wttr.in*") + (setq-local wttrin--current-location "Paris") + (wttrin-requery-force))) + ;; After the let-binding unwinds, force-refresh should be back to nil + (should-not wttrin--force-refresh)) + (test-wttrin-requery-force-teardown))) + +(provide 'test-wttrin-requery-force) +;;; test-wttrin-requery-force.el ends here diff --git a/tests/test-wttrin-requery.el b/tests/test-wttrin-requery.el new file mode 100644 index 0000000..e643a34 --- /dev/null +++ b/tests/test-wttrin-requery.el @@ -0,0 +1,152 @@ +;;; test-wttrin-requery.el --- Tests for wttrin-requery -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin--requery-location and wttrin-requery. +;; wttrin--requery-location holds the core logic (kill buffer, query new location). +;; wttrin-requery is the interactive wrapper that adds completing-read. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin-requery-setup () + "Setup for requery tests." + (testutil-wttrin-setup) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +(defun test-wttrin-requery-teardown () + "Teardown for requery tests." + (testutil-wttrin-teardown) + (when (get-buffer "*wttr.in*") + (kill-buffer "*wttr.in*"))) + +;;; -------------------------------------------------------------------------- +;;; wttrin--requery-location (core logic) +;;; -------------------------------------------------------------------------- + +;;; Normal Cases + +(ert-deftest test-wttrin--requery-location-normal-kills-existing-buffer () + "Requerying should kill the existing *wttr.in* buffer before opening a new one." + (test-wttrin-requery-setup) + (unwind-protect + (let ((old-buffer (get-buffer-create "*wttr.in*"))) + (cl-letf (((symbol-function 'wttrin-query) (lambda (_loc) nil))) + (wttrin--requery-location "Tokyo") + ;; Old buffer should be dead + (should-not (buffer-live-p old-buffer)))) + (test-wttrin-requery-teardown))) + +(ert-deftest test-wttrin--requery-location-normal-queries-new-location () + "Requerying should fetch weather for the newly specified location." + (test-wttrin-requery-setup) + (unwind-protect + (let ((queried-location nil)) + (cl-letf (((symbol-function 'wttrin-query) + (lambda (loc) (setq queried-location loc)))) + (wttrin--requery-location "Berlin, DE") + (should (equal queried-location "Berlin, DE")))) + (test-wttrin-requery-teardown))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--requery-location-boundary-no-existing-buffer () + "Requerying when no weather buffer exists should still query the new location." + (test-wttrin-requery-setup) + (unwind-protect + (let ((queried-location nil)) + ;; Ensure no buffer exists + (should-not (get-buffer "*wttr.in*")) + (cl-letf (((symbol-function 'wttrin-query) + (lambda (loc) (setq queried-location loc)))) + (wttrin--requery-location "Paris") + (should (equal queried-location "Paris")))) + (test-wttrin-requery-teardown))) + +(ert-deftest test-wttrin--requery-location-boundary-unicode-location () + "Requerying with unicode characters should pass them through unchanged." + (test-wttrin-requery-setup) + (unwind-protect + (let ((queried-location nil)) + (cl-letf (((symbol-function 'wttrin-query) + (lambda (loc) (setq queried-location loc)))) + (wttrin--requery-location "Zürich, CH") + (should (equal queried-location "Zürich, CH")))) + (test-wttrin-requery-teardown))) + +;;; -------------------------------------------------------------------------- +;;; wttrin-requery (interactive wrapper) +;;; -------------------------------------------------------------------------- + +(ert-deftest test-wttrin-requery-normal-uses-selected-location () + "The interactive command should pass the user's completing-read selection +to the core requery function." + (test-wttrin-requery-setup) + (unwind-protect + (let ((requeried-location nil)) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _collection &rest _args) "London, GB")) + ((symbol-function 'wttrin--requery-location) + (lambda (loc) (setq requeried-location loc)))) + (wttrin-requery) + (should (equal requeried-location "London, GB")))) + (test-wttrin-requery-teardown))) + +(ert-deftest test-wttrin-requery-normal-offers-default-locations () + "Completing-read should be called with wttrin-default-locations as candidates." + (test-wttrin-requery-setup) + (unwind-protect + (let ((offered-collection nil) + (wttrin-default-locations '("Paris" "London" "Tokyo"))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt collection &rest _args) + (setq offered-collection collection) + "Paris")) + ((symbol-function 'wttrin--requery-location) + (lambda (_loc) nil))) + (wttrin-requery) + (should (equal offered-collection '("Paris" "London" "Tokyo"))))) + (test-wttrin-requery-teardown))) + +(ert-deftest test-wttrin-requery-boundary-single-default-prefills () + "When only one default location exists, it should be pre-filled in the prompt." + (test-wttrin-requery-setup) + (unwind-protect + (let ((initial-input nil) + (wttrin-default-locations '("Solo City"))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _collection _predicate _require-match init &rest _args) + (setq initial-input init) + "Solo City")) + ((symbol-function 'wttrin--requery-location) + (lambda (_loc) nil))) + (wttrin-requery) + (should (equal initial-input "Solo City")))) + (test-wttrin-requery-teardown))) + +(ert-deftest test-wttrin-requery-boundary-multiple-defaults-no-prefill () + "When multiple default locations exist, nothing should be pre-filled." + (test-wttrin-requery-setup) + (unwind-protect + (let ((initial-input 'not-called) + (wttrin-default-locations '("Paris" "London"))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _collection _predicate _require-match init &rest _args) + (setq initial-input init) + "Paris")) + ((symbol-function 'wttrin--requery-location) + (lambda (_loc) nil))) + (wttrin-requery) + (should-not initial-input))) + (test-wttrin-requery-teardown))) + +(provide 'test-wttrin-requery) +;;; test-wttrin-requery.el ends here |
