1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
;;; test-integration-debug.el --- Integration test with debug enabled -*- lexical-binding: t; -*-
;; Copyright (C) 2024 Craig Jennings
;;; Commentary:
;; Comprehensive integration test that:
;; 1. Enables debug mode
;; 2. Mocks weather fetch to avoid network calls
;; 3. Tests mode-line display with real weather data
;; 4. Verifies debug log captures key events
;;; Code:
(require 'ert)
(require 'wttrin)
(require 'testutil-wttrin)
;; Sample weather data from wttr.in custom format API
(defconst test-wttrin-sample-weather-data
"Berkeley, CA: ☀️ +62°F Clear"
"Sample weather data in wttr.in custom format.")
;;; Setup and Teardown
(defun test-integration-debug-setup ()
"Set up test environment with debug enabled."
;; Enable debug mode
(setq wttrin-debug t)
;; Load debug module if not already loaded
(unless (featurep 'wttrin-debug)
(require 'wttrin-debug))
;; Clear any existing debug log
(wttrin-debug-clear-log)
;; Clear cache
(wttrin-clear-cache)
;; Set test configuration
(setq wttrin-favorite-location "Berkeley, CA")
(setq wttrin-mode-line-startup-delay 1) ; Minimum valid value
(setq wttrin-unit-system "m"))
(defun test-integration-debug-teardown ()
"Clean up after tests."
(when (boundp 'wttrin-mode-line-mode)
(wttrin-mode-line-mode -1))
(wttrin-clear-cache)
(wttrin-debug-clear-log)
(setq wttrin-mode-line-string nil)
(setq wttrin--mode-line-tooltip-data nil))
;;; Mock URL Fetching
(defvar test-wttrin--original-fetch-url nil
"Original wttrin--fetch-url function for restoration after test.")
(defun test-integration-debug-mock-fetch (url callback)
"Mock version of wttrin--fetch-url that returns fake weather data.
URL is ignored. CALLBACK is called with mock data."
(when (featurep 'wttrin-debug)
(wttrin--debug-log "MOCK-FETCH: Called with URL: %s" url))
;; Call callback directly (synchronous) since run-at-time doesn't work well in batch mode
(when (featurep 'wttrin-debug)
(wttrin--debug-log "MOCK-FETCH: Calling callback with mock data"))
(funcall callback test-wttrin-sample-weather-data))
(defmacro test-integration-debug-with-mocked-fetch (&rest body)
"Execute BODY with wttrin--fetch-url mocked to return test data."
`(let ((test-wttrin--original-fetch-url (symbol-function 'wttrin--fetch-url)))
(unwind-protect
(progn
(fset 'wttrin--fetch-url #'test-integration-debug-mock-fetch)
,@body)
(fset 'wttrin--fetch-url test-wttrin--original-fetch-url))))
;;; Integration Tests
(ert-deftest test-integration-debug-mode-line-fetch-and-display-logs-events ()
"Test the mode-line weather fetch and display pipeline with debug logging.
Context: The mode-line weather feature fetches weather data asynchronously
and updates the mode-line string with an emoji icon and tooltip.
Components integrated:
- `wttrin--mode-line-fetch-weather' (async fetch trigger)
- `wttrin--mode-line-update-display' (mode-line string formatting)
- `wttrin--debug-log' (debug event capture)
Validates that a successful fetch sets the mode-line string with an emoji,
stores tooltip data, and logs fetch-start, data-received, display-update,
and emoji-extraction events to the debug log."
(test-integration-debug-setup)
(unwind-protect
(test-integration-debug-with-mocked-fetch
;; Clear debug log
(wttrin-debug-clear-log)
;; Fetch weather for mode-line
(wttrin--mode-line-fetch-weather)
;; Wait for async callback to complete (mocked, so should be fast)
(sleep-for 0.1)
;; Verify mode-line string was set
(should wttrin-mode-line-string)
(should (stringp wttrin-mode-line-string))
(should (string-match-p "☀" wttrin-mode-line-string)) ; Should contain emoji
;; Verify tooltip data was set
(should wttrin--mode-line-tooltip-data)
(should (string= test-wttrin-sample-weather-data wttrin--mode-line-tooltip-data))
;; Verify debug log captured key events
(let ((log-messages (mapcar #'cdr wttrin--debug-log)))
;; Should have logged the fetch start
(should (seq-some (lambda (msg) (string-match-p "mode-line-fetch: Starting" msg))
log-messages))
;; Should have logged receiving data
(should (seq-some (lambda (msg) (string-match-p "mode-line-fetch: Received data" msg))
log-messages))
;; Should have logged display update
(should (seq-some (lambda (msg) (string-match-p "mode-line-display:" msg))
log-messages))
;; Should have logged emoji extraction
(should (seq-some (lambda (msg) (string-match-p "Extracted emoji" msg))
log-messages))))
(test-integration-debug-teardown)))
(ert-deftest test-integration-debug-full-weather-query-creates-buffer ()
"Test the full weather query pipeline from fetch through buffer display.
Context: When a user calls `wttrin-query', the system fetches weather data,
processes it through ANSI filtering, and displays it in a dedicated buffer.
Components integrated:
- `wttrin-query' (user-facing entry point)
- `wttrin--fetch-url' (HTTP fetch, mocked)
- `wttrin--display-weather' (buffer creation and content rendering)
- `wttrin--debug-log' (debug event capture)
Validates that a full weather query creates the *wttr.in* buffer and
that the debug log records the mock fetch call."
(test-integration-debug-setup)
(unwind-protect
(progn
;; Clear debug log
(wttrin-debug-clear-log)
;; Mock full weather fetch (synchronous for batch mode)
(cl-letf (((symbol-function 'wttrin--fetch-url)
(lambda (url callback)
(when (featurep 'wttrin-debug)
(wttrin--debug-log "MOCK-FETCH: Full weather query for URL: %s" url))
;; Call directly instead of using run-at-time (doesn't work in batch)
(funcall callback testutil-wttrin-sample-full-weather))))
;; Start the query (now synchronous with mocked fetch)
(wttrin-query "Berkeley, CA")
;; Verify buffer was created
(should (get-buffer "*wttr.in*"))
;; Verify debug log shows mock was called
(let ((log-messages (mapcar #'cdr wttrin--debug-log)))
;; Should have logged the mock fetch
(should (seq-some (lambda (msg) (string-match-p "MOCK-FETCH: Full weather query" msg))
log-messages)))))
;; Cleanup
(when (get-buffer "*wttr.in*")
(kill-buffer "*wttr.in*"))
(test-integration-debug-teardown)))
(ert-deftest test-integration-debug-mode-line-mode-toggle-updates-global-string ()
"Test enabling and disabling wttrin-mode-line-mode updates global state.
Context: `wttrin-mode-line-mode' is a global minor mode that adds a weather
widget to the Emacs mode-line. Toggling it should cleanly add/remove state.
Components integrated:
- `wttrin-mode-line-mode' (global minor mode toggle)
- `wttrin--mode-line-fetch-weather' (initial data fetch)
- `global-mode-string' (Emacs mode-line display list)
Validates that enabling the mode sets `wttrin-mode-line-string' and adds it
to `global-mode-string', and that disabling clears both."
(test-integration-debug-setup)
(unwind-protect
(test-integration-debug-with-mocked-fetch
;; Clear debug log
(wttrin-debug-clear-log)
;; Enable mode-line mode
(wttrin-mode-line-mode 1)
(should wttrin-mode-line-mode)
;; Manually trigger the initial fetch instead of waiting for timer
;; (timers don't process well in batch mode)
(wttrin--mode-line-fetch-weather)
;; Verify mode-line string is set
(should wttrin-mode-line-string)
;; Verify global-mode-string contains our widget
(should (member 'wttrin-mode-line-string global-mode-string))
;; Disable mode-line mode
(wttrin-mode-line-mode -1)
(should-not wttrin-mode-line-mode)
;; Verify mode-line string is cleared
(should-not wttrin-mode-line-string)
;; Verify removed from global-mode-string
(should-not (member 'wttrin-mode-line-string global-mode-string)))
(test-integration-debug-teardown)))
(ert-deftest test-integration-debug-error-handling-logs-no-data-received ()
"Test that network errors are handled gracefully and logged to debug.
Context: When wttr.in is unreachable or returns an error, the fetch callback
receives nil. The mode-line handler should not crash and should log the error.
Components integrated:
- `wttrin--mode-line-fetch-weather' (fetch trigger)
- `wttrin--fetch-url' (HTTP fetch, mocked to return nil)
- `wttrin--debug-log' (error event capture)
Validates that a nil fetch response does not crash and that a
\"No data received\" message is recorded in the debug log."
(test-integration-debug-setup)
(unwind-protect
(progn
;; Clear debug log
(wttrin-debug-clear-log)
;; Mock fetch that returns nil (simulating network error)
(cl-letf (((symbol-function 'wttrin--fetch-url)
(lambda (url callback)
(when (featurep 'wttrin-debug)
(wttrin--debug-log "MOCK-FETCH: Simulating error for URL: %s" url))
(run-at-time 0 nil (lambda () (funcall callback nil))))))
;; Try to fetch (should handle error gracefully)
(wttrin--mode-line-fetch-weather)
;; Wait for async callback
(sleep-for 0.1)
;; Verify error was logged
(let ((log-messages (mapcar #'cdr wttrin--debug-log)))
(should (seq-some (lambda (msg) (string-match-p "No data received" msg))
log-messages)))))
(test-integration-debug-teardown)))
(ert-deftest test-integration-debug-log-inspection-stores-timestamped-entries ()
"Test that the debug log stores structured entries that can be inspected.
Context: The debug log is an alist of (timestamp . message) pairs used for
diagnosing issues. It should support programmatic inspection and clearing.
Components integrated:
- `wttrin--debug-log' (log writing)
- `wttrin-debug-clear-log' (log clearing)
- `wttrin--debug-log' variable (log storage as alist)
Validates that log entries are (string . string) cons cells, that formatted
messages are stored correctly, and that clearing empties the log."
(test-integration-debug-setup)
(unwind-protect
(progn
;; Clear and add some test log entries
(wttrin-debug-clear-log)
(wttrin--debug-log "Test message 1")
(wttrin--debug-log "Test message 2 with arg: %s" "value")
;; Verify log structure
(should (= 2 (length wttrin--debug-log)))
(should (consp (car wttrin--debug-log))) ; Each entry is (timestamp . message)
(should (stringp (caar wttrin--debug-log))) ; Timestamp is string
(should (stringp (cdar wttrin--debug-log))) ; Message is string
;; Verify messages
(let ((messages (mapcar #'cdr wttrin--debug-log)))
(should (member "Test message 1" messages))
(should (seq-some (lambda (msg) (string-match-p "Test message 2.*value" msg))
messages)))
;; Clear log
(wttrin-debug-clear-log)
(should (= 0 (length wttrin--debug-log))))
(test-integration-debug-teardown)))
(provide 'test-integration-debug)
;;; test-integration-debug.el ends here
|