cedd82738a
Three gaps caused messages received via SSE to be invisible when the conversation buffer wasn't already open: 1. Echo-area notification (noise-receive.el): when an SSE message arrives and the conversation buffer isn't in a visible window, show "[Noise] <name>" in the echo area. 2. Modeline unread counter (noise.el): the noise-mode lighter now shows Noise[N] for N unread messages via a lightweight SUM aggregation across all conversations. 3. Auto-refresh hook (noise-conversation.el): a window-buffer-change-functions hook re-renders *signal:* buffers when they become visible, covering direct C-x b switches. Also adds regression test: noise-receive-sse-stores-then-open-shows-message — store via SSE handler with no buffer open, then open and verify.
236 lines
10 KiB
EmacsLisp
236 lines
10 KiB
EmacsLisp
;;; noise-receive-test.el --- Tests for noise-receive -*- lexical-binding: t; -*-
|
|
|
|
(require 'ert)
|
|
(require 'noise-receive)
|
|
|
|
(defvar noise-receive-test--db
|
|
(expand-file-name "noise-receive-test.db" temporary-file-directory))
|
|
|
|
(defmacro noise-receive-test--with-db (&rest body)
|
|
"Run BODY with a fresh test database and clean up conversation buffers."
|
|
`(let ((noise-db-file noise-receive-test--db))
|
|
(unwind-protect
|
|
(progn
|
|
(when (file-exists-p noise-db-file)
|
|
(delete-file noise-db-file))
|
|
(noise-db-init)
|
|
,@body)
|
|
(noise-db--close)
|
|
(when (file-exists-p noise-receive-test--db)
|
|
(delete-file noise-receive-test--db))
|
|
(dolist (buf (buffer-list))
|
|
(when (string-prefix-p "*signal:" (buffer-name buf))
|
|
(kill-buffer buf))))))
|
|
|
|
(defun noise-receive-test--ht (&rest kvs)
|
|
"Build a hash table from KVS key/value pairs."
|
|
(let ((ht (make-hash-table :test 'equal)))
|
|
(while kvs
|
|
(puthash (car kvs) (cadr kvs) ht)
|
|
(setq kvs (cddr kvs)))
|
|
ht))
|
|
|
|
(defun noise-receive-test--run (&rest envelopes)
|
|
"Run `noise-receive' against mocked ENVELOPES, returning its result."
|
|
(cl-letf (((symbol-function 'noise-rpc-call-for-account)
|
|
(lambda (method &rest _)
|
|
(should (string= "receive" method))
|
|
(apply #'vector
|
|
(mapcar (lambda (e)
|
|
(noise-receive-test--ht "envelope" e))
|
|
envelopes)))))
|
|
(noise-receive)))
|
|
|
|
(ert-deftest noise-receive-direct-message ()
|
|
"An incoming direct message creates contact, conversation, and message."
|
|
(noise-receive-test--with-db
|
|
(should (= 1 (noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1" "sourceName" "Sarah"
|
|
"dataMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"message" "are you coming?")))))
|
|
(should (string= "Sarah" (gethash "name" (noise-db-get-contact "+1"))))
|
|
(let ((conv (noise-db-get-conversation "+1")))
|
|
(should (string= "direct" (gethash "type" conv)))
|
|
(should (string= "Sarah" (gethash "name" conv)))
|
|
(should (= 1 (gethash "unread_count" conv)))
|
|
;; direct chats have no sender prefix in the preview
|
|
(should (string= "" (gethash "last_message_sender" conv)))
|
|
(should (string= "are you coming?"
|
|
(gethash "last_message_preview" conv))))
|
|
(let ((msg (car (noise-db-get-messages "+1"))))
|
|
(should (string= "Sarah" (gethash "source" msg)))
|
|
(should (= 1717930000.0 (gethash "timestamp" msg))))))
|
|
|
|
(ert-deftest noise-receive-group-message ()
|
|
"An incoming group message creates a group conversation keyed by groupId."
|
|
(noise-receive-test--with-db
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+2" "sourceName" "Priya"
|
|
"dataMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"message" "saturday 7am, usual spot?"
|
|
"groupInfo" (noise-receive-test--ht
|
|
"groupId" "grp1"
|
|
"groupName" "cycling crew"))))
|
|
(let ((conv (noise-db-get-conversation "grp1")))
|
|
(should (string= "group" (gethash "type" conv)))
|
|
(should (string= "cycling crew" (gethash "name" conv)))
|
|
;; group previews keep the sender prefix
|
|
(should (string= "Priya" (gethash "last_message_sender" conv))))))
|
|
|
|
(ert-deftest noise-receive-attachment-only-message ()
|
|
"An attachment-only message is stored and gets a photo preview."
|
|
(noise-receive-test--with-db
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1" "sourceName" "Sarah"
|
|
"dataMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"message" :null
|
|
"attachments" (vector (noise-receive-test--ht
|
|
"filename" "IMG_2041.jpg")))))
|
|
(let ((msg (car (noise-db-get-messages "+1"))))
|
|
(should (= 1 (gethash "has_attachment" msg)))
|
|
(should (string= "IMG_2041.jpg" (gethash "attachment_path" msg))))
|
|
(should (string= "photo: IMG_2041.jpg"
|
|
(gethash "last_message_preview"
|
|
(noise-db-get-conversation "+1"))))))
|
|
|
|
(ert-deftest noise-receive-sync-message ()
|
|
"A sync sentMessage from another device is stored as from `me'."
|
|
(noise-receive-test--with-db
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+me"
|
|
"syncMessage" (noise-receive-test--ht
|
|
"sentMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"destinationNumber" "+1"
|
|
"message" "sent from my phone"))))
|
|
(let ((msg (car (noise-db-get-messages "+1"))))
|
|
(should (string= "me" (gethash "source" msg)))
|
|
(should (string= "sent" (gethash "receipt_state" msg)))
|
|
(should (string= "sent from my phone" (gethash "body" msg))))
|
|
;; own messages don't bump unread
|
|
(should (= 0 (gethash "unread_count" (noise-db-get-conversation "+1"))))))
|
|
|
|
(ert-deftest noise-receive-delivery-receipt ()
|
|
"A delivery receipt upgrades the receipt state of the matching message."
|
|
(noise-receive-test--with-db
|
|
(noise-db-upsert-conversation "+1" :type "direct" :name "Sarah")
|
|
(noise-db-insert-message "+1" :timestamp 1717930000.0
|
|
:source "me" :body "hi" :receipt-state "sent")
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1"
|
|
"receiptMessage" (noise-receive-test--ht
|
|
"isDelivery" t "isRead" :false
|
|
"timestamps" (vector 1717930000000))))
|
|
(should (string= "delivered"
|
|
(gethash "receipt_state"
|
|
(car (noise-db-get-messages "+1")))))))
|
|
|
|
(ert-deftest noise-receive-receipt-never-downgrades ()
|
|
"A delivery receipt arriving after a read receipt is ignored."
|
|
(noise-receive-test--with-db
|
|
(noise-db-upsert-conversation "+1" :type "direct" :name "Sarah")
|
|
(noise-db-insert-message "+1" :timestamp 1717930000.0
|
|
:source "me" :body "hi" :receipt-state "read")
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1"
|
|
"receiptMessage" (noise-receive-test--ht
|
|
"isDelivery" t "isRead" :false
|
|
"timestamps" (vector 1717930000000))))
|
|
(should (string= "read"
|
|
(gethash "receipt_state"
|
|
(car (noise-db-get-messages "+1")))))))
|
|
|
|
(ert-deftest noise-receive-receipt-ignores-incoming-messages ()
|
|
"Receipts only apply to own messages, not the contact's."
|
|
(noise-receive-test--with-db
|
|
(noise-db-upsert-conversation "+1" :type "direct" :name "Sarah")
|
|
(noise-db-insert-message "+1" :timestamp 1717930000.0
|
|
:source "Sarah" :body "hi")
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1"
|
|
"receiptMessage" (noise-receive-test--ht
|
|
"isDelivery" t "isRead" :false
|
|
"timestamps" (vector 1717930000000))))
|
|
(should (string= ""
|
|
(gethash "receipt_state"
|
|
(car (noise-db-get-messages "+1")))))))
|
|
|
|
(ert-deftest noise-receive-typing-without-buffer ()
|
|
"Typing indicators for conversations without open buffers are harmless."
|
|
(noise-receive-test--with-db
|
|
(should (= 0 (noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1" "sourceName" "Sarah"
|
|
"typingMessage" (noise-receive-test--ht
|
|
"action" "STARTED"
|
|
"timestamp" 1717930000000)))))))
|
|
|
|
(ert-deftest noise-receive-refreshes-open-conversation ()
|
|
"Receiving a message re-renders an open conversation buffer."
|
|
(noise-receive-test--with-db
|
|
(noise-db-upsert-conversation "+1" :type "direct" :name "Sarah"
|
|
:members '("+1"))
|
|
(require 'noise-conversation)
|
|
(with-current-buffer (noise-conversation-open "+1")
|
|
(noise-receive-test--run
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1" "sourceName" "Sarah"
|
|
"dataMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"message" "new message here")))
|
|
(should (string-match-p "Sarah> new message here" (buffer-string))))))
|
|
|
|
(ert-deftest noise-receive-handle-sse-event-stores-message ()
|
|
"An SSE envelope event stores the message and refreshes the open buffer."
|
|
(noise-receive-test--with-db
|
|
(noise-db-upsert-conversation "+1" :type "direct" :name "Sarah"
|
|
:members '("+1"))
|
|
(require 'noise-conversation)
|
|
(with-current-buffer (noise-conversation-open "+1")
|
|
(noise-receive--handle-sse-event
|
|
(noise-receive-test--ht
|
|
"envelope"
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1" "sourceName" "Sarah"
|
|
"dataMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"message" "from sse"))))
|
|
(should (string-match-p "Sarah> from sse" (buffer-string))))
|
|
(let ((msg (car (noise-db-get-messages "+1"))))
|
|
(should (string= "from sse" (gethash "body" msg))))))
|
|
|
|
(ert-deftest noise-receive-sse-stores-then-open-shows-message ()
|
|
"An SSE event stores the message; opening the conversation later shows it."
|
|
(noise-receive-test--with-db
|
|
;; No conversation buffer open — simulate SSE event arriving
|
|
(noise-receive--handle-sse-event
|
|
(noise-receive-test--ht
|
|
"envelope"
|
|
(noise-receive-test--ht
|
|
"sourceNumber" "+1" "sourceName" "Sarah"
|
|
"dataMessage" (noise-receive-test--ht
|
|
"timestamp" 1717930000000
|
|
"message" "hello from the void"))))
|
|
;; Verify message is in DB
|
|
(let ((msgs (noise-db-get-messages "+1")))
|
|
(should (= 1 (length msgs)))
|
|
(should (string= "hello from the void" (gethash "body" (car msgs)))))
|
|
;; Verify conversation has unread count
|
|
(let ((conv (noise-db-get-conversation "+1")))
|
|
(should (= 1 (gethash "unread_count" conv))))
|
|
;; Now open the conversation and verify message renders
|
|
(require 'noise-conversation)
|
|
(with-current-buffer (noise-conversation-open "+1")
|
|
(should (string-match-p "Sarah> hello from the void" (buffer-string)))
|
|
(should (= 0 (gethash "unread_count" (noise-db-get-conversation "+1")))))))
|