164 lines
6.4 KiB
EmacsLisp
164 lines
6.4 KiB
EmacsLisp
;;; noise-sse-test.el --- Tests for SSE client -*- lexical-binding: t; -*-
|
|
|
|
;;; Commentary:
|
|
|
|
;; Tests for noise-sse.el SSE event parsing and connection management.
|
|
|
|
;;; Code:
|
|
|
|
(require 'ert)
|
|
(require 'noise-sse)
|
|
|
|
(defvar noise-signal-cli-address)
|
|
(defvar noise-account)
|
|
|
|
(ert-deftest noise-sse--parse-event-basic ()
|
|
"Parse a basic SSE event with data field."
|
|
(let ((event "data: {\"type\":\"message\",\"content\":\"hello\"}\n\n"))
|
|
(let ((result (noise-sse--parse-event event)))
|
|
(should (hash-table-p result))
|
|
(should (equal (gethash "type" result) "message"))
|
|
(should (equal (gethash "content" result) "hello")))))
|
|
|
|
(ert-deftest noise-sse--parse-event-multiline ()
|
|
"Parse SSE event with multiline data."
|
|
(let ((event "data: {\"type\":\"message\",\ndata: \"content\":\"hello\"}\n\n"))
|
|
(let ((result (noise-sse--parse-event event)))
|
|
(should (hash-table-p result))
|
|
(should (equal (gethash "type" result) "message")))))
|
|
|
|
(ert-deftest noise-sse--parse-event-empty ()
|
|
"Parse empty SSE event returns nil."
|
|
(let ((event "\n\n"))
|
|
(should (null (noise-sse--parse-event event)))))
|
|
|
|
(ert-deftest noise-sse--initial-state ()
|
|
"SSE client starts in disconnected state."
|
|
(let ((noise-sse--process nil)
|
|
(noise-sse--reconnect-timer nil)
|
|
(noise-sse--reconnect-delay 1))
|
|
(should (null noise-sse--process))
|
|
(should (null noise-sse--reconnect-timer))
|
|
(should (= 1 noise-sse--reconnect-delay))))
|
|
|
|
(ert-deftest noise-sse--reconnect-delay-backoff ()
|
|
"Reconnect delay doubles on each attempt."
|
|
(let ((noise-sse--reconnect-delay 1))
|
|
(noise-sse--increase-reconnect-delay)
|
|
(should (= 2 noise-sse--reconnect-delay))
|
|
(noise-sse--increase-reconnect-delay)
|
|
(should (= 4 noise-sse--reconnect-delay))
|
|
(noise-sse--increase-reconnect-delay)
|
|
(should (= 8 noise-sse--reconnect-delay))))
|
|
|
|
(ert-deftest noise-sse--reconnect-delay-max ()
|
|
"Reconnect delay caps at max value."
|
|
(let ((noise-sse--reconnect-delay 32)
|
|
(noise-sse-max-reconnect-delay 60))
|
|
(noise-sse--increase-reconnect-delay)
|
|
(should (= 60 noise-sse--reconnect-delay))))
|
|
|
|
(ert-deftest noise-sse--filter-processes-complete-event ()
|
|
"Process filter parses and calls handler for complete events."
|
|
(let ((processed-events '())
|
|
(noise-sse--buffer "")
|
|
(noise-sse--http-buffer "")
|
|
(noise-sse--chunk-buffer "")
|
|
(noise-sse--headers-received t)
|
|
(noise-sse--chunked-response-p nil))
|
|
(let ((noise-sse--event-handler
|
|
(lambda (event) (push event processed-events))))
|
|
(noise-sse--filter nil "data: {\"type\":\"message\"}\n\n")
|
|
(should (= 1 (length processed-events)))
|
|
(should (hash-table-p (car processed-events))))))
|
|
|
|
(ert-deftest noise-sse--filter-buffers-incomplete-event ()
|
|
"Process filter buffers incomplete events until newline."
|
|
(let ((processed-events '())
|
|
(noise-sse--buffer "")
|
|
(noise-sse--http-buffer "")
|
|
(noise-sse--chunk-buffer "")
|
|
(noise-sse--headers-received t)
|
|
(noise-sse--chunked-response-p nil))
|
|
(let ((noise-sse--event-handler
|
|
(lambda (event) (push event processed-events))))
|
|
(noise-sse--filter nil "data: {\"type\":\"message\"")
|
|
(should (= 0 (length processed-events)))
|
|
(noise-sse--filter nil "}\n\n")
|
|
(should (= 1 (length processed-events))))))
|
|
|
|
(ert-deftest noise-sse--filter-skips-http-headers ()
|
|
"HTTP response headers should not block the first SSE event."
|
|
(let ((processed-events '())
|
|
(noise-sse--buffer "")
|
|
(noise-sse--http-buffer "")
|
|
(noise-sse--chunk-buffer "")
|
|
(noise-sse--headers-received nil)
|
|
(noise-sse--chunked-response-p nil))
|
|
(let ((noise-sse--event-handler
|
|
(lambda (event) (push event processed-events))))
|
|
(noise-sse--filter nil
|
|
(concat "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: text/event-stream\r\n\r\n"
|
|
"data: {\"type\":\"message\"}\n\n"))
|
|
(should (= 1 (length processed-events)))
|
|
(should (equal "message" (gethash "type" (car processed-events)))))))
|
|
|
|
(ert-deftest noise-sse--filter-decodes-chunked-http-body ()
|
|
"Chunked HTTP SSE responses should be decoded before parsing events."
|
|
(let ((processed-events '())
|
|
(noise-sse--buffer "")
|
|
(noise-sse--http-buffer "")
|
|
(noise-sse--chunk-buffer "")
|
|
(noise-sse--headers-received nil)
|
|
(noise-sse--chunked-response-p nil))
|
|
(let ((noise-sse--event-handler
|
|
(lambda (event) (push event processed-events))))
|
|
(noise-sse--filter nil
|
|
(concat "HTTP/1.1 200 OK\r\n"
|
|
"Transfer-Encoding: chunked\r\n"
|
|
"Content-Type: text/event-stream\r\n\r\n"
|
|
"12\r\n"
|
|
"data: {\"type\":\"msg"
|
|
"\r\n"
|
|
"4\r\n"
|
|
"\"}\n\n"
|
|
"\r\n"
|
|
"0\r\n\r\n"))
|
|
(should (= 1 (length processed-events)))
|
|
(should (equal "msg" (gethash "type" (car processed-events)))))))
|
|
|
|
(ert-deftest noise-sse--build-url ()
|
|
"Build SSE endpoint URL from base address."
|
|
(let ((noise-signal-cli-address "http://localhost:8080")
|
|
(noise-account "+15551234567"))
|
|
(let ((url (noise-sse--build-url)))
|
|
(should (string-match-p "/api/v1/events" url))
|
|
(should (string-match-p "account=%2B15551234567" url)))))
|
|
|
|
(ert-deftest noise-sse--build-url-no-account ()
|
|
"Build SSE endpoint URL without account parameter."
|
|
(let ((noise-signal-cli-address "http://localhost:8080")
|
|
(noise-account nil))
|
|
(let ((url (noise-sse--build-url)))
|
|
(should (string-match-p "/api/v1/events" url))
|
|
(should-not (string-match-p "account=" url)))))
|
|
|
|
(ert-deftest noise-sse-connect-is-interactive ()
|
|
"noise-sse-connect is an interactive command."
|
|
(should (commandp 'noise-sse-connect)))
|
|
|
|
(ert-deftest noise-sse-disconnect-is-interactive ()
|
|
"noise-sse-disconnect is an interactive command."
|
|
(should (commandp 'noise-sse-disconnect)))
|
|
|
|
(ert-deftest noise-sse-disconnect-cleans-up ()
|
|
"noise-sse-disconnect cleans up process and timer."
|
|
(let ((noise-sse--reconnect-timer (run-with-timer 1000 nil #'ignore)))
|
|
(noise-sse-disconnect)
|
|
(should (null noise-sse--process))
|
|
(should (null noise-sse--reconnect-timer))))
|
|
|
|
(provide 'noise-sse-test)
|
|
;;; noise-sse-test.el ends here
|