;;; noise-new-chat-test.el --- Tests for noise-new-chat -*- lexical-binding: t; -*- (require 'ert) (require 'noise-new-chat) (defvar noise-new-chat-test--db (expand-file-name "noise-new-chat-test.db" temporary-file-directory)) (defun noise-new-chat-test--setup () "Set up a fresh test database." (require 'noise-db) (let ((noise-db-file noise-new-chat-test--db)) (when (file-exists-p noise-db-file) (delete-file noise-db-file)) (noise-db-init))) (defun noise-new-chat-test--teardown () "Clean up the test database." (require 'noise-db) (noise-db--close) (when (file-exists-p noise-new-chat-test--db) (delete-file noise-new-chat-test--db))) (defmacro noise-new-chat-test--with-db (&rest body) "Run BODY with a fresh test database bound to `noise-db-file'." `(let ((noise-db-file noise-new-chat-test--db)) (unwind-protect (progn (noise-new-chat-test--setup) ,@body) (noise-new-chat-test--teardown)))) (defun noise-new-chat-test--contact (&rest kvs) "Build a hash table simulating a signal-cli contact from KVS pairs." (let ((ht (make-hash-table :test 'equal))) (while kvs (puthash (car kvs) (cadr kvs) ht) (setq kvs (cddr kvs))) ht)) (ert-deftest noise-new-chat--contact-id-prefers-number () "Contact ID should prefer the phone number over the UUID." (let ((c (noise-new-chat-test--contact "number" "+15551234567" "uuid" "abc-123"))) (should (string= "+15551234567" (noise-new-chat--contact-id c))))) (ert-deftest noise-new-chat--contact-id-falls-back-to-uuid () "Contact ID should fall back to UUID when number is null." (let ((c (noise-new-chat-test--contact "number" :null "uuid" "abc-123"))) (should (string= "abc-123" (noise-new-chat--contact-id c))))) (ert-deftest noise-new-chat--contact-name-prefers-local-name () "Display name should prefer the local contact name." (let ((c (noise-new-chat-test--contact "number" "+15551234567" "name" "Alice" "profile" (noise-new-chat-test--contact "givenName" "Alicia")))) (should (string= "Alice" (noise-new-chat--contact-name c))))) (ert-deftest noise-new-chat--contact-name-uses-profile () "Display name should fall back to the profile name." (let ((c (noise-new-chat-test--contact "number" "+15551234567" "name" "" "profile" (noise-new-chat-test--contact "givenName" "Alicia" "familyName" "Jones")))) (should (string= "Alicia Jones" (noise-new-chat--contact-name c))))) (ert-deftest noise-new-chat--contact-name-falls-back-to-number () "Display name should fall back to the phone number when nothing else." (let ((c (noise-new-chat-test--contact "number" "+15551234567" "name" "" "profile" :null))) (should (string= "+15551234567" (noise-new-chat--contact-name c))))) (ert-deftest noise-new-chat--candidate-includes-number () "Completion candidates should include name and number." (let ((ht (make-hash-table :test 'equal))) (puthash "id" "+15551234567" ht) (puthash "name" "Alice" ht) (should (string= "Alice (+15551234567)" (noise-new-chat--candidate ht))))) (ert-deftest noise-new-chat--candidate-number-only () "Candidates with no name should be just the number." (let ((ht (make-hash-table :test 'equal))) (puthash "id" "+15551234567" ht) (puthash "name" "" ht) (should (string= "+15551234567" (noise-new-chat--candidate ht))))) (ert-deftest noise-new-chat-sync-contacts-stores-contacts () "Syncing should upsert contacts returned by signal-cli into the DB." (noise-new-chat-test--with-db (cl-letf (((symbol-function 'noise-rpc-call-for-account) (lambda (method &rest _) (should (string= "listContacts" method)) (vector (noise-new-chat-test--contact "number" "+1" "name" "Sarah") (noise-new-chat-test--contact "number" "+2" "name" "" "profile" (noise-new-chat-test--contact "givenName" "Samir" "familyName" "Darwish")) ;; No number and no uuid — should be skipped (noise-new-chat-test--contact "name" "Ghost"))))) (should (= 2 (noise-new-chat-sync-contacts))) (should (string= "Sarah" (gethash "name" (noise-db-get-contact "+1")))) (should (string= "Samir Darwish" (gethash "name" (noise-db-get-contact "+2"))))))) (ert-deftest noise-new-chat-sync-contacts-uses-account () "Syncing should go through the account-aware RPC helper." (noise-new-chat-test--with-db (defvar noise-account) (let ((noise-account "+19998887777") (seen-account nil)) (cl-letf (((symbol-function 'noise-rpc-call) (lambda (_method &rest params) (setq seen-account (plist-get params :account)) (vector)))) (noise-new-chat-sync-contacts) (should (string= "+19998887777" seen-account)))))) (ert-deftest noise-new-chat-with-contact-creates-conversation () "Starting a chat with a contact should create a direct conversation." (noise-new-chat-test--with-db (noise-db-upsert-contact "+1" :name "Sarah") (noise-new-chat-with-contact "+1" "Sarah") (let ((conv (noise-db-get-conversation "+1"))) (should conv) (should (string= "direct" (gethash "type" conv))) (should (string= "Sarah" (gethash "name" conv))) (should (equal '("+1") (mapcar (lambda (m) (gethash "contact_id" m)) (noise-db-get-conversation-members "+1"))))))) (ert-deftest noise-new-chat-with-contact-is-idempotent () "Starting a chat twice should not clobber the existing conversation." (noise-new-chat-test--with-db (noise-db-upsert-conversation "+1" :type "direct" :name "Sarah" :members '("+1") :unread-count 3 :last-message-preview "hello") (noise-new-chat-with-contact "+1" "Sarah") (let ((conv (noise-db-get-conversation "+1"))) (should (= 3 (gethash "unread_count" conv))) (should (string= "hello" (gethash "last_message_preview" conv))))))