diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-28 18:16:28 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-28 19:09:22 -0500 |
| commit | 3491d9b799f9678f6095149a348330e2a05a1924 (patch) | |
| tree | 496069dd668ce30d036090486b2c30dd6f681872 /tests | |
| parent | dc0db0f0e12d8af6d1d54a5dde1cd16cf890a33d (diff) | |
| download | gloss-3491d9b799f9678f6095149a348330e2a05a1924.tar.gz gloss-3491d9b799f9678f6095149a348330e2a05a1924.zip | |
feat: implement gloss-fetch network layer
Walks the `gloss-fetch--sources' registry in the order set by the
`gloss-fetch-sources' defcustom and aggregates per-source results into
the public `gloss-fetch-definitions' shape.
The Wiktionary REST fetcher GETs the page-definition endpoint, parses
JSON, walks only English (`en') entries, and HTML-strips each sense via
`libxml-parse-html-region'. A sense whose strip fails is dropped while
the source keeps its `:ok' status with N-1 entries.
The HTTP-status taxonomy is five values: `:ok', `:no-defs' (404 or no
English senses on a 200), `:rate-limited' (429), `:server-error' (5xx,
malformed JSON, schema mismatch, 4xx other than 404 or 429), and
`:unreachable' (nil from `url-retrieve-synchronously', or a signaled
error). The `:reason' string carries technical detail to *gloss-debug*
and never reaches the user.
libxml is probed once per session at first fetch. When absent, online
fetch is disabled package-wide and every call signals `user-error' with
the install hint. `url-retrieve-synchronously' is wrapped with the
`gloss-fetch-timeout' defcustom (default 5 seconds).
Tested with `make test'. 60 of 62 tests pass. The two pending failures
load Wiktionary fixtures via `gloss-test--load-wiktionary-fixture',
which is provided on a parallel branch and will pass once both branches
land. The implementation has been verified against the captured
fixtures end-to-end (anaphora returns 4 senses, SBIR returns 2,
matching the design's expected counts).
Diffstat (limited to 'tests')
6 files changed, 21 insertions, 19 deletions
diff --git a/tests/test-gloss-fetch--definitions-404-returns-no-defs.el b/tests/test-gloss-fetch--definitions-404-returns-no-defs.el index d8cd257..28587ac 100644 --- a/tests/test-gloss-fetch--definitions-404-returns-no-defs.el +++ b/tests/test-gloss-fetch--definitions-404-returns-no-defs.el @@ -22,8 +22,8 @@ "{\"detail\":\"Page not found\"}")) (let ((result (gloss-fetch-definitions "asdf-not-a-word"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :no-defs))) - (should-not (plist-get result :failed))))) + (should (member 'wiktionary (plist-get (cdr result) :no-defs))) + (should-not (plist-get (cdr result) :failed))))) (ert-deftest test-gloss-fetch-definitions-200-empty-rolls-up-to-empty-no-defs () "Boundary: a 200 with an empty JSON object also maps to :no-defs." @@ -31,8 +31,8 @@ (lambda (_url) (gloss-fetch-test--ok-response "{}")) (let ((result (gloss-fetch-definitions "term"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :no-defs))) - (should-not (plist-get result :failed))))) + (should (member 'wiktionary (plist-get (cdr result) :no-defs))) + (should-not (plist-get (cdr result) :failed))))) (ert-deftest test-gloss-fetch-definitions-200-no-english-rolls-up-to-no-defs () "Boundary: a 200 response with only non-English keys maps to :no-defs." @@ -42,7 +42,7 @@ (lambda (_url) (gloss-fetch-test--ok-response body)) (let ((result (gloss-fetch-definitions "term"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :no-defs))))))) + (should (member 'wiktionary (plist-get (cdr result) :no-defs))))))) (provide 'test-gloss-fetch--definitions-404-returns-no-defs) ;;; test-gloss-fetch--definitions-404-returns-no-defs.el ends here diff --git a/tests/test-gloss-fetch--definitions-429-returns-rate-limited.el b/tests/test-gloss-fetch--definitions-429-returns-rate-limited.el index a5fb719..cd2d2d4 100644 --- a/tests/test-gloss-fetch--definitions-429-returns-rate-limited.el +++ b/tests/test-gloss-fetch--definitions-429-returns-rate-limited.el @@ -21,8 +21,8 @@ (gloss-fetch-test--status-response "HTTP/1.1 429 Too Many Requests" "")) (let ((result (gloss-fetch-definitions "anaphora"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :failed))) - (should-not (plist-get result :no-defs))))) + (should (member 'wiktionary (plist-get (cdr result) :failed))) + (should-not (plist-get (cdr result) :no-defs))))) (ert-deftest test-gloss-fetch-definitions-429-tracked-separately-internally () "Boundary: per-source status taxonomy distinguishes :rate-limited from :server-error. diff --git a/tests/test-gloss-fetch--definitions-500-returns-server-error.el b/tests/test-gloss-fetch--definitions-500-returns-server-error.el index e81efe2..20e988b 100644 --- a/tests/test-gloss-fetch--definitions-500-returns-server-error.el +++ b/tests/test-gloss-fetch--definitions-500-returns-server-error.el @@ -22,8 +22,8 @@ "Server is sad.")) (let ((result (gloss-fetch-definitions "anaphora"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :failed))) - (should-not (plist-get result :no-defs))))) + (should (member 'wiktionary (plist-get (cdr result) :failed))) + (should-not (plist-get (cdr result) :no-defs))))) (ert-deftest test-gloss-fetch-definitions-503-rolls-up-to-failed () "Normal: HTTP 503 maps the source to :server-error (in :failed)." @@ -32,7 +32,7 @@ (gloss-fetch-test--status-response "HTTP/1.1 503 Service Unavailable" "")) (let ((result (gloss-fetch-definitions "anaphora"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :failed)))))) + (should (member 'wiktionary (plist-get (cdr result) :failed)))))) (ert-deftest test-gloss-fetch-definitions-malformed-json-rolls-up-to-failed () "Boundary: a 200 with non-JSON body also maps to :server-error." @@ -40,7 +40,7 @@ (lambda (_url) (gloss-fetch-test--ok-response "<html>not json</html>")) (let ((result (gloss-fetch-definitions "anaphora"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :failed)))))) + (should (member 'wiktionary (plist-get (cdr result) :failed)))))) (ert-deftest test-gloss-fetch-definitions-400-rolls-up-to-failed () "Error: HTTP 400 (4xx other than 404/429) maps to :server-error (in :failed)." @@ -49,7 +49,7 @@ (gloss-fetch-test--status-response "HTTP/1.1 400 Bad Request" "")) (let ((result (gloss-fetch-definitions "anaphora"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :failed)))))) + (should (member 'wiktionary (plist-get (cdr result) :failed)))))) (provide 'test-gloss-fetch--definitions-500-returns-server-error) ;;; test-gloss-fetch--definitions-500-returns-server-error.el ends here diff --git a/tests/test-gloss-fetch--definitions-timeout-returns-unreachable.el b/tests/test-gloss-fetch--definitions-timeout-returns-unreachable.el index 881b783..8067dde 100644 --- a/tests/test-gloss-fetch--definitions-timeout-returns-unreachable.el +++ b/tests/test-gloss-fetch--definitions-timeout-returns-unreachable.el @@ -20,8 +20,8 @@ (lambda (_url) nil) (let ((result (gloss-fetch-definitions "anaphora"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :failed))) - (should-not (plist-get result :no-defs))))) + (should (member 'wiktionary (plist-get (cdr result) :failed))) + (should-not (plist-get (cdr result) :no-defs))))) (ert-deftest test-gloss-fetch-definitions-timeout-marks-source-unreachable () "Boundary: per-source status is :unreachable, distinct from :server-error." diff --git a/tests/test-gloss-fetch--libxml-probe.el b/tests/test-gloss-fetch--libxml-probe.el index 830a278..758c185 100644 --- a/tests/test-gloss-fetch--libxml-probe.el +++ b/tests/test-gloss-fetch--libxml-probe.el @@ -17,7 +17,8 @@ (ert-deftest test-gloss-fetch-libxml-absent-signals-user-error () "Error: with libxml unavailable, the first fetch signals user-error and disables online." - (let ((gloss-fetch--libxml-disabled nil)) + (let ((gloss-fetch--libxml-disabled nil) + (gloss-fetch--libxml-checked nil)) (cl-letf (((symbol-function 'gloss-fetch--libxml-available-p) (lambda () nil))) (should-error (gloss-fetch-definitions "anything") :type 'user-error) @@ -26,7 +27,8 @@ (ert-deftest test-gloss-fetch-libxml-error-mentions-libxml2 () "Error: the user-error message names libxml2 so the user can act." - (let ((gloss-fetch--libxml-disabled nil)) + (let ((gloss-fetch--libxml-disabled nil) + (gloss-fetch--libxml-checked nil)) (cl-letf (((symbol-function 'gloss-fetch--libxml-available-p) (lambda () nil))) (let ((err (should-error (gloss-fetch-definitions "x") :type 'user-error))) @@ -56,7 +58,7 @@ (lambda (_url) (gloss-fetch-test--ok-response "{}")) (let ((result (gloss-fetch-definitions "term"))) (should (eq (car result) :empty)) - (should (member 'wiktionary (plist-get result :no-defs)))))))) + (should (member 'wiktionary (plist-get (cdr result) :no-defs)))))))) (provide 'test-gloss-fetch--libxml-probe) ;;; test-gloss-fetch--libxml-probe.el ends here diff --git a/tests/test-gloss-fetch--multi-source-walks-registry.el b/tests/test-gloss-fetch--multi-source-walks-registry.el index fe04e02..cb2b730 100644 --- a/tests/test-gloss-fetch--multi-source-walks-registry.el +++ b/tests/test-gloss-fetch--multi-source-walks-registry.el @@ -43,8 +43,8 @@ (gloss-fetch-sources '(alpha beta)) (result (gloss-fetch-definitions "x"))) (should (eq (car result) :empty)) - (should (member 'alpha (plist-get result :no-defs))) - (should (member 'beta (plist-get result :failed))))) + (should (member 'alpha (plist-get (cdr result) :no-defs))) + (should (member 'beta (plist-get (cdr result) :failed))))) (ert-deftest test-gloss-fetch-rollup-any-ok-yields-ok () "Boundary: if any source returns :ok with defs, the rollup is (:ok DEFS)." |
