Fix unbounded number of calls to fetch notifications (#14250)

This commit is contained in:
Icaro Motta 2022-10-27 16:21:41 -03:00 committed by GitHub
parent 69303cd7d1
commit 91b237d3d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 512 additions and 527 deletions

View File

@ -5,503 +5,488 @@
[status-im.constants :as const] [status-im.constants :as const]
[status-im.ethereum.json-rpc :as json-rpc] [status-im.ethereum.json-rpc :as json-rpc]
status-im.events status-im.events
[status-im.test-helpers :as h])) [status-im.test-helpers :as h]
[status-im.utils.config :as config]))
(defn setup [] (defn setup []
(h/register-helper-events) (h/register-helper-events)
(rf/dispatch [:init/app-started])) (rf/dispatch [:init/app-started]))
(defn remove-color-key
"Remove `:color` key from notifications because they have random values that are
inconvenient to assert against."
[grouped-notifications {:keys [type status]}]
(update-in grouped-notifications
[type status :data]
(fn [old _]
(map #(dissoc % :color) old))
nil))
;;;; Contact verification ;;;; Contact verification
(deftest contact-verification-decline-test (deftest contact-verification-decline-test
(testing "successfully declines and reconciles returned notification" (with-redefs [config/new-activity-center-enabled? true]
(rf-test/run-test-sync (testing "successfully declines and reconciles returned notification"
(setup) (rf-test/run-test-sync
(let [spy-queue (atom []) (setup)
contact-verification-id 24 (let [spy-queue (atom [])
expected-notification {:accepted false contact-verification-id 24
:author "0x04d03f" expected-notification {:accepted false
:chat-id "0x04d03f" :author "0x04d03f"
:contact-verification-status 3 :chat-id "0x04d03f"
:dismissed false :contact-verification-status 3
:id 24 :dismissed false
:last-message nil :id 24
:message {:command-parameters nil :last-message nil
:content {:chat-id nil :message {:command-parameters nil
:ens-name nil :content {:chat-id nil
:image nil :ens-name nil
:line-count nil :image nil
:links nil :line-count nil
:parsed-text nil :links nil
:response-to nil :parsed-text nil
:rtl? nil :response-to nil
:sticker nil :rtl? nil
:text nil} :sticker nil
:outgoing false :text nil}
:outgoing-status nil :outgoing false
:quoted-message nil} :outgoing-status nil
:name "0x04d03f" :quoted-message nil}
:read true :name "0x04d03f"
:reply-message nil :read true
:timestamp 1666647286000 :reply-message nil
:type const/activity-center-notification-type-contact-verification}] :timestamp 1666647286000
(h/stub-fx-with-callbacks :type const/activity-center-notification-type-contact-verification}]
::json-rpc/call (h/stub-fx-with-callbacks
:on-success (constantly {:activityCenterNotifications ::json-rpc/call
[{:accepted false :on-success (constantly {:activityCenterNotifications
:author "0x04d03f" [{:accepted false
:chatId "0x04d03f" :author "0x04d03f"
:contactVerificationStatus 3 :chatId "0x04d03f"
:dismissed false :contactVerificationStatus 3
:id contact-verification-id :dismissed false
:message {} :id contact-verification-id
:name "0x04d03f" :message {}
:read true :name "0x04d03f"
:timestamp 1666647286000 :read true
:type const/activity-center-notification-type-contact-verification}]})) :timestamp 1666647286000
:type const/activity-center-notification-type-contact-verification}]}))
(h/spy-fx spy-queue ::json-rpc/call) (h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:activity-center.contact-verification/decline contact-verification-id]) (rf/dispatch [:activity-center.contact-verification/decline contact-verification-id])
(is (= {:method "wakuext_declineContactVerificationRequest" (is (= {:method "wakuext_declineContactVerificationRequest"
:params [contact-verification-id]} :params [contact-verification-id]}
(-> @spy-queue (-> @spy-queue
(get-in [0 :args 0]) (get-in [0 :args 0])
(select-keys [:method :params])))) (select-keys [:method :params]))))
(is (= {const/activity-center-notification-type-no-type (is (= {const/activity-center-notification-type-no-type
{:read {:data [expected-notification]} {:read {:data [expected-notification]}
:unread {:data []}} :unread {:data []}}
const/activity-center-notification-type-contact-verification const/activity-center-notification-type-contact-verification
{:read {:data [expected-notification]} {:read {:data [expected-notification]}
:unread {:data []}}} :unread {:data []}}}
(-> (h/db) (get-in (h/db) [:activity-center :notifications]))))))
(get-in [:activity-center :notifications])
(remove-color-key {:type const/activity-center-notification-type-no-type
:status :read})
(remove-color-key {:type const/activity-center-notification-type-contact-verification
:status :read})))))))
(testing "logs failure" (testing "logs failure"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(let [contact-verification-id 666] (let [contact-verification-id 666]
(h/using-log-test-appender (h/using-log-test-appender
(fn [logs] (fn [logs]
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error)) (h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
(rf/dispatch [:activity-center.contact-verification/decline contact-verification-id]) (rf/dispatch [:activity-center.contact-verification/decline contact-verification-id])
(is (= {:args ["Failed to decline contact verification" (is (= {:args ["Failed to decline contact verification"
{:contact-verification-id contact-verification-id {:contact-verification-id contact-verification-id
:error :fake-error}] :error :fake-error}]
:level :warn} :level :warn}
(last @logs))))))))) (last @logs))))))))))
;;;; Notification reconciliation ;;;; Notification reconciliation
(deftest notifications-reconcile-test (deftest notifications-reconcile-test
(testing "does nothing when there are no new notifications" (with-redefs [config/new-activity-center-enabled? true]
(rf-test/run-test-sync (testing "does nothing when there are no new notifications"
(setup) (rf-test/run-test-sync
(let [notifications {const/activity-center-notification-type-one-to-one-chat (setup)
{:read {:cursor "" (let [notifications {const/activity-center-notification-type-one-to-one-chat
:data [{:id "0x1" {:read {:cursor ""
:read true :data [{:id "0x1"
:type const/activity-center-notification-type-one-to-one-chat} :read true
{:id "0x2" :type const/activity-center-notification-type-one-to-one-chat}
:read true {:id "0x2"
:type const/activity-center-notification-type-one-to-one-chat}]} :read true
:unread {:cursor "" :type const/activity-center-notification-type-one-to-one-chat}]}
:data [{:id "0x3" :unread {:cursor ""
:read false :data [{:id "0x3"
:type const/activity-center-notification-type-one-to-one-chat}]}} :read false
const/activity-center-notification-type-private-group-chat :type const/activity-center-notification-type-one-to-one-chat}]}}
{:unread {:cursor "" const/activity-center-notification-type-private-group-chat
:data [{:id "0x4" {:unread {:cursor ""
:read false :data [{:id "0x4"
:type const/activity-center-notification-type-private-group-chat}]}}}] :read false
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications]) :type const/activity-center-notification-type-private-group-chat}]}}}]
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
(rf/dispatch [:activity-center.notifications/reconcile nil]) (rf/dispatch [:activity-center.notifications/reconcile nil])
(is (= notifications (get-in (h/db) [:activity-center :notifications])))))) (is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
(testing "removes dismissed or accepted notifications" (testing "removes dismissed or accepted notifications"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(rf/dispatch [:test/assoc-in [:activity-center :notifications] (rf/dispatch [:test/assoc-in [:activity-center :notifications]
{const/activity-center-notification-type-one-to-one-chat {const/activity-center-notification-type-one-to-one-chat
{:read {:cursor "" {:read {:cursor ""
:data [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat} :data [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat}
{:id "0x2" :read true :type const/activity-center-notification-type-one-to-one-chat}]} {:id "0x2" :read true :type const/activity-center-notification-type-one-to-one-chat}]}
:unread {:cursor "" :unread {:cursor ""
:data [{:id "0x3" :read false :type const/activity-center-notification-type-one-to-one-chat}]}} :data [{:id "0x3" :read false :type const/activity-center-notification-type-one-to-one-chat}]}}
const/activity-center-notification-type-private-group-chat const/activity-center-notification-type-private-group-chat
{:unread {:cursor "" {:unread {:cursor ""
:data [{:id "0x4" :read false :type const/activity-center-notification-type-private-group-chat} :data [{:id "0x4" :read false :type const/activity-center-notification-type-private-group-chat}
{:id "0x6" :read false :type const/activity-center-notification-type-private-group-chat}]}}}]) {:id "0x6" :read false :type const/activity-center-notification-type-private-group-chat}]}}}])
(rf/dispatch [:activity-center.notifications/reconcile (rf/dispatch [:activity-center.notifications/reconcile
[{:id "0x1" [{:id "0x1"
:read true :read true
:type const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-one-to-one-chat
:dismissed true} :dismissed true}
{:id "0x3" {:id "0x3"
:read false :read false
:type const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-one-to-one-chat
:accepted true} :accepted true}
{:id "0x4" {:id "0x4"
:read false :read false
:type const/activity-center-notification-type-private-group-chat :type const/activity-center-notification-type-private-group-chat
:dismissed true} :dismissed true}
{:id "0x5" {:id "0x5"
:read false :read false
:type const/activity-center-notification-type-private-group-chat :type const/activity-center-notification-type-private-group-chat
:accepted true}]]) :accepted true}]])
(is (= {const/activity-center-notification-type-no-type (is (= {const/activity-center-notification-type-no-type
{:read {:data []} {:read {:data []}
:unread {:data []}} :unread {:data []}}
const/activity-center-notification-type-one-to-one-chat const/activity-center-notification-type-one-to-one-chat
{:read {:cursor "" {:read {:cursor ""
:data [{:id "0x2" :data [{:id "0x2"
:read true :read true
:type const/activity-center-notification-type-one-to-one-chat}]} :type const/activity-center-notification-type-one-to-one-chat}]}
:unread {:cursor "" :unread {:cursor ""
:data []}} :data []}}
const/activity-center-notification-type-private-group-chat const/activity-center-notification-type-private-group-chat
{:read {:data []} {:read {:data []}
:unread {:cursor "" :unread {:cursor ""
:data [{:id "0x6" :data [{:id "0x6"
:read false :read false
:type const/activity-center-notification-type-private-group-chat}]}}} :type const/activity-center-notification-type-private-group-chat}]}}}
(get-in (h/db) [:activity-center :notifications]))))) (get-in (h/db) [:activity-center :notifications])))))
(testing "replaces old notifications with newly arrived ones" (testing "replaces old notifications with newly arrived ones"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(rf/dispatch [:test/assoc-in [:activity-center :notifications] (rf/dispatch [:test/assoc-in [:activity-center :notifications]
{const/activity-center-notification-type-no-type {const/activity-center-notification-type-no-type
{:read {:cursor "" {:read {:cursor ""
:data [{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat}]}
:unread {:cursor ""
:data [{:id "0x4"
:read false
:type const/activity-center-notification-type-private-group-chat}
{:id "0x6"
:read false
:type const/activity-center-notification-type-private-group-chat}]}}
const/activity-center-notification-type-one-to-one-chat
{:read {:cursor ""
:data [{:id "0x1" :data [{:id "0x1"
:read true :read true
:type const/activity-center-notification-type-one-to-one-chat}]} :type const/activity-center-notification-type-one-to-one-chat}]}}
:unread {:cursor "" const/activity-center-notification-type-private-group-chat
:data [{:id "0x4" {:unread {:cursor ""
:read false :data [{:id "0x4"
:type const/activity-center-notification-type-private-group-chat} :read false
{:id "0x6" :type const/activity-center-notification-type-private-group-chat}
:read false {:id "0x6"
:type const/activity-center-notification-type-private-group-chat}]}} :read false
const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-private-group-chat}]}}}])
{:read {:cursor ""
:data [{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat}]}}
const/activity-center-notification-type-private-group-chat
{:unread {:cursor ""
:data [{:id "0x4"
:read false
:type const/activity-center-notification-type-private-group-chat}
{:id "0x6"
:read false
:type const/activity-center-notification-type-private-group-chat}]}}}])
(rf/dispatch [:activity-center.notifications/reconcile (rf/dispatch [:activity-center.notifications/reconcile
[{:id "0x1" [{:id "0x1"
:read true :read true
:type const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-one-to-one-chat
:last-message {}} :last-message {}}
{:id "0x4" {:id "0x4"
:read false :read false
:type const/activity-center-notification-type-private-group-chat :type const/activity-center-notification-type-private-group-chat
:author "0xabc"} :author "0xabc"}
{:id "0x6" {:id "0x6"
:read false :read false
:type const/activity-center-notification-type-private-group-chat}]]) :type const/activity-center-notification-type-private-group-chat}]])
(is (= {const/activity-center-notification-type-no-type (is (= {const/activity-center-notification-type-no-type
{:read {:cursor "" {:read {:cursor ""
:data [{:id "0x1" :data [{:id "0x1"
:read true :read true
:type const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-one-to-one-chat
:last-message {}}]} :last-message {}}]}
:unread {:cursor "" :unread {:cursor ""
:data [{:id "0x6" :data [{:id "0x6"
:read false
:type const/activity-center-notification-type-private-group-chat}
{:id "0x4"
:read false
:type const/activity-center-notification-type-private-group-chat
:author "0xabc"}]}}
const/activity-center-notification-type-one-to-one-chat
{:read {:cursor ""
:data [{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat
:last-message {}}]}
:unread {:data []}}
const/activity-center-notification-type-private-group-chat
{:read {:data []}
:unread {:cursor ""
:data [{:id "0x6"
:read false
:type const/activity-center-notification-type-private-group-chat}
{:id "0x4"
:read false
:type const/activity-center-notification-type-private-group-chat
:author "0xabc"}]}}}
(get-in (h/db) [:activity-center :notifications])))))
(testing "reconciles notifications that switched their read/unread status"
(rf-test/run-test-sync
(setup)
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
{const/activity-center-notification-type-one-to-one-chat
{:read {:cursor ""
:data [{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat}]}}}])
(rf/dispatch [:activity-center.notifications/reconcile
[{:id "0x1"
:read false
:type const/activity-center-notification-type-one-to-one-chat}]])
(is (= {const/activity-center-notification-type-no-type
{:read {:data []}
:unread {:data [{:id "0x1"
:read false :read false
:type const/activity-center-notification-type-private-group-chat} :type const/activity-center-notification-type-one-to-one-chat}]}}
{:id "0x4"
:read false const/activity-center-notification-type-one-to-one-chat
:type const/activity-center-notification-type-private-group-chat {:read {:cursor ""
:author "0xabc"}]}} :data []}
const/activity-center-notification-type-one-to-one-chat :unread {:data [{:id "0x1"
{:read {:cursor ""
:data [{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat
:last-message {}}]}
:unread {:data []}}
const/activity-center-notification-type-private-group-chat
{:read {:data []}
:unread {:cursor ""
:data [{:id "0x6"
:read false :read false
:type const/activity-center-notification-type-private-group-chat} :type const/activity-center-notification-type-one-to-one-chat}]}}}
{:id "0x4" (get-in (h/db) [:activity-center :notifications])))))
:read false
:type const/activity-center-notification-type-private-group-chat
:author "0xabc"}]}}}
(get-in (h/db) [:activity-center :notifications])))))
(testing "reconciles notifications that switched their read/unread status" ;; Sorting by timestamp and ID is compatible with what the backend does when
(rf-test/run-test-sync ;; returning paginated results.
(setup) (testing "sorts notifications by timestamp and id in descending order"
(rf/dispatch [:test/assoc-in [:activity-center :notifications] (rf-test/run-test-sync
{const/activity-center-notification-type-one-to-one-chat (setup)
{:read {:cursor "" (rf/dispatch [:test/assoc-in [:activity-center :notifications]
:data [{:id "0x1" {const/activity-center-notification-type-one-to-one-chat
:read true {:read {:cursor ""
:type const/activity-center-notification-type-one-to-one-chat}]}}}]) :data [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1}
{:id "0x2" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
:unread {:cursor ""
:data [{:id "0x3" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 50}
{:id "0x4" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100}
{:id "0x5" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
(rf/dispatch [:activity-center.notifications/reconcile (rf/dispatch [:activity-center.notifications/reconcile
[{:id "0x1" [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
:read false {:id "0x4" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
:type const/activity-center-notification-type-one-to-one-chat}]])
(is (= {const/activity-center-notification-type-no-type (is (= {const/activity-center-notification-type-no-type
{:read {:data []} {:read {:data [{:id "0x1"
:unread {:data [{:id "0x1"
:read false
:type const/activity-center-notification-type-one-to-one-chat}]}}
const/activity-center-notification-type-one-to-one-chat
{:read {:cursor ""
:data []}
:unread {:data [{:id "0x1"
:read false
:type const/activity-center-notification-type-one-to-one-chat}]}}}
(get-in (h/db) [:activity-center :notifications])))))
;; Sorting by timestamp and ID is compatible with what the backend does when
;; returning paginated results.
(testing "sorts notifications by timestamp and id in descending order"
(rf-test/run-test-sync
(setup)
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
{const/activity-center-notification-type-one-to-one-chat
{:read {:cursor ""
:data [{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1}
{:id "0x2" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
:unread {:cursor ""
:data [{:id "0x3" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 50}
{:id "0x4" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100}
{:id "0x5" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
(rf/dispatch [:activity-center.notifications/reconcile
[{:id "0x1" :read true :type const/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
{:id "0x4" :read false :type const/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
(is (= {const/activity-center-notification-type-no-type
{:read {:data [{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 1
:last-message {}}]}
:unread {:data [{:id "0x4"
:read false
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 100
:last-message {}}]}}
const/activity-center-notification-type-one-to-one-chat
{:read {:cursor ""
:data [{:id "0x2"
:read true
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 1}
{:id "0x1"
:read true :read true
:type const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-one-to-one-chat
:timestamp 1 :timestamp 1
:last-message {}}]} :last-message {}}]}
:unread {:cursor "" :unread {:data [{:id "0x4"
:data [{:id "0x5"
:read false
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 100}
{:id "0x4"
:read false :read false
:type const/activity-center-notification-type-one-to-one-chat :type const/activity-center-notification-type-one-to-one-chat
:timestamp 100 :timestamp 100
:last-message {}} :last-message {}}]}}
{:id "0x3" const/activity-center-notification-type-one-to-one-chat
:read false {:read {:cursor ""
:type const/activity-center-notification-type-one-to-one-chat :data [{:id "0x2"
:timestamp 50}]}}} :read true
(get-in (h/db) [:activity-center :notifications])))))) :type const/activity-center-notification-type-one-to-one-chat
:timestamp 1}
{:id "0x1"
:read true
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 1
:last-message {}}]}
:unread {:cursor ""
:data [{:id "0x5"
:read false
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 100}
{:id "0x4"
:read false
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 100
:last-message {}}
{:id "0x3"
:read false
:type const/activity-center-notification-type-one-to-one-chat
:timestamp 50}]}}}
(get-in (h/db) [:activity-center :notifications])))))))
;;;; Notifications fetching and pagination ;;;; Notifications fetching and pagination
(deftest notifications-fetch-test (deftest notifications-fetch-test
(testing "fetches first page" (with-redefs [config/new-activity-center-enabled? true]
(rf-test/run-test-sync (testing "fetches first page"
(setup) (rf-test/run-test-sync
(let [spy-queue (atom [])] (setup)
(h/stub-fx-with-callbacks (let [spy-queue (atom [])]
::json-rpc/call (h/stub-fx-with-callbacks
:on-success (constantly {:cursor "10" ::json-rpc/call
:notifications [{:id "0x1" :on-success (constantly {:cursor "10"
:type const/activity-center-notification-type-one-to-one-chat :notifications [{:id "0x1"
:read false :type const/activity-center-notification-type-one-to-one-chat
:chatId "0x9"}]})) :read false
(h/spy-fx spy-queue ::json-rpc/call) :chatId "0x9"}]}))
(h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:activity-center.notifications/fetch-first-page (rf/dispatch [:activity-center.notifications/fetch-first-page
{:filter-type const/activity-center-notification-type-one-to-one-chat}]) {:filter-type const/activity-center-notification-type-one-to-one-chat}])
(is (= :unread (get-in (h/db) [:activity-center :filter :status]))) (is (= :unread (get-in (h/db) [:activity-center :filter :status])))
(is (= "" (get-in @spy-queue [0 :args 0 :params 0])) (is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
"Should be called with empty cursor when fetching first page") "Should be called with empty cursor when fetching first page")
(is (= {const/activity-center-notification-type-one-to-one-chat (is (= {const/activity-center-notification-type-one-to-one-chat
{:unread {:cursor "10" {:unread {:cursor "10"
:data [{:chat-id "0x9" :data [{:chat-id "0x9"
:chat-name nil :chat-name nil
:chat-type const/activity-center-notification-type-one-to-one-chat :chat-type const/activity-center-notification-type-one-to-one-chat
:group-chat false :group-chat false
:id "0x1" :id "0x1"
:public? false :public? false
:last-message nil :last-message nil
:message nil :message nil
:read false :read false
:reply-message nil :reply-message nil
:type const/activity-center-notification-type-one-to-one-chat}]}}} :type const/activity-center-notification-type-one-to-one-chat}]}}}
(remove-color-key (get-in (h/db) [:activity-center :notifications]) (get-in (h/db) [:activity-center :notifications]))))))
{:status :unread
:type const/activity-center-notification-type-one-to-one-chat}))))))
(testing "does not fetch next page when pagination cursor reached the end" (testing "does not fetch next page when pagination cursor reached the end"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(let [spy-queue (atom [])] (let [spy-queue (atom [])]
(h/spy-fx spy-queue ::json-rpc/call) (h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status] (rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread]) :unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type] (rf/dispatch [:test/assoc-in [:activity-center :filter :type]
const/activity-center-notification-type-one-to-one-chat]) const/activity-center-notification-type-one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor] (rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor]
""]) ""])
(rf/dispatch [:activity-center.notifications/fetch-next-page]) (rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= [] @spy-queue))))) (is (= [] @spy-queue)))))
;; The cursor can be nil sometimes because the reconciliation doesn't care ;; The cursor can be nil sometimes because the reconciliation doesn't care
;; about updating the cursor value, but we have to make sure the next page is ;; about updating the cursor value, but we have to make sure the next page is
;; only fetched if the current cursor is valid. ;; only fetched if the current cursor is valid.
(testing "does not fetch next page when cursor is nil" (testing "does not fetch next page when cursor is nil"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(let [spy-queue (atom [])] (let [spy-queue (atom [])]
(h/spy-fx spy-queue ::json-rpc/call) (h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status] (rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread]) :unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type] (rf/dispatch [:test/assoc-in [:activity-center :filter :type]
const/activity-center-notification-type-one-to-one-chat]) const/activity-center-notification-type-one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor] (rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor]
nil]) nil])
(rf/dispatch [:activity-center.notifications/fetch-next-page]) (rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= [] @spy-queue))))) (is (= [] @spy-queue)))))
(testing "fetches next page when pagination cursor is not empty" (testing "fetches next page when pagination cursor is not empty"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(let [spy-queue (atom [])] (let [spy-queue (atom [])]
(h/stub-fx-with-callbacks (h/stub-fx-with-callbacks
::json-rpc/call ::json-rpc/call
:on-success (constantly {:cursor "" :on-success (constantly {:cursor ""
:notifications [{:id "0x1" :notifications [{:id "0x1"
:type const/activity-center-notification-type-mention :type const/activity-center-notification-type-mention
:read false :read false
:chatId "0x9"}]})) :chatId "0x9"}]}))
(h/spy-fx spy-queue ::json-rpc/call) (h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status] (rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread]) :unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type] (rf/dispatch [:test/assoc-in [:activity-center :filter :type]
const/activity-center-notification-type-mention]) const/activity-center-notification-type-mention])
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-mention :unread :cursor] (rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-mention :unread :cursor]
"10"]) "10"])
(rf/dispatch [:activity-center.notifications/fetch-next-page]) (rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method]))) (is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0])) (is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
"Should be called with current cursor") "Should be called with current cursor")
(is (= {const/activity-center-notification-type-mention (is (= {const/activity-center-notification-type-mention
{:unread {:cursor "" {:unread {:cursor ""
:data [{:chat-id "0x9" :data [{:chat-id "0x9"
:chat-name nil :chat-name nil
:chat-type 3 :chat-type 3
:id "0x1" :id "0x1"
:last-message nil :last-message nil
:message nil :message nil
:read false :read false
:reply-message nil :reply-message nil
:type const/activity-center-notification-type-mention}]}}} :type const/activity-center-notification-type-mention}]}}}
(remove-color-key (get-in (h/db) [:activity-center :notifications]) (get-in (h/db) [:activity-center :notifications]))))))
{:status :unread
:type const/activity-center-notification-type-mention}))))))
(testing "does not fetch next page while it is still loading" (testing "does not fetch next page while it is still loading"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(let [spy-queue (atom [])] (let [spy-queue (atom [])]
(h/spy-fx spy-queue ::json-rpc/call) (h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status] (rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:read]) :read])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type] (rf/dispatch [:test/assoc-in [:activity-center :filter :type]
const/activity-center-notification-type-one-to-one-chat]) const/activity-center-notification-type-one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :read :cursor] (rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :read :cursor]
"10"]) "10"])
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :read :loading?] (rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :read :loading?]
true]) true])
(rf/dispatch [:activity-center.notifications/fetch-next-page]) (rf/dispatch [:activity-center.notifications/fetch-next-page])
(is (= [] @spy-queue))))) (is (= [] @spy-queue)))))
(testing "resets loading flag after an error" (testing "resets loading flag after an error"
(rf-test/run-test-sync (rf-test/run-test-sync
(setup) (setup)
(let [spy-queue (atom [])] (let [spy-queue (atom [])]
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error)) (h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error) (h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
(h/spy-fx spy-queue ::json-rpc/call) (h/spy-fx spy-queue ::json-rpc/call)
(rf/dispatch [:test/assoc-in [:activity-center :filter :status] (rf/dispatch [:test/assoc-in [:activity-center :filter :status]
:unread]) :unread])
(rf/dispatch [:test/assoc-in [:activity-center :filter :type] (rf/dispatch [:test/assoc-in [:activity-center :filter :type]
const/activity-center-notification-type-one-to-one-chat]) const/activity-center-notification-type-one-to-one-chat])
(rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor] (rf/dispatch [:test/assoc-in [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :cursor]
""]) ""])
(rf/dispatch [:activity-center.notifications/fetch-first-page]) (rf/dispatch [:activity-center.notifications/fetch-first-page])
(is (nil? (get-in (h/db) [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :loading?]))) (is (nil? (get-in (h/db) [:activity-center :notifications const/activity-center-notification-type-one-to-one-chat :unread :loading?])))
(is (= [:activity-center.notifications/fetch-error (is (= [:activity-center.notifications/fetch-error
const/activity-center-notification-type-one-to-one-chat const/activity-center-notification-type-one-to-one-chat
:unread :unread
:fake-error] :fake-error]
(:args (last @spy-queue)))))))) (:args (last @spy-queue)))))))))

View File

@ -2,7 +2,8 @@
(:require [clojure.set :as set] (:require [clojure.set :as set]
[quo.design-system.colors :as colors] [quo.design-system.colors :as colors]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.messages :as messages])) [status-im.data-store.messages :as messages]
[status-im.utils.config :as config]))
(defn- rpc->type [{:keys [type name] :as chat}] (defn- rpc->type [{:keys [type name] :as chat}]
(case type (case type
@ -33,14 +34,15 @@
chat)) chat))
(defn <-rpc [item] (defn <-rpc [item]
(-> item (cond-> (-> item
rpc->type rpc->type
(set/rename-keys {:lastMessage :last-message (set/rename-keys {:lastMessage :last-message
:replyMessage :reply-message :replyMessage :reply-message
:chatId :chat-id :chatId :chat-id
:contactVerificationStatus :contact-verification-status}) :contactVerificationStatus :contact-verification-status})
(assoc :color (rand-nth colors/chat-colors)) (update :last-message #(when % (messages/<-rpc %)))
(update :last-message #(when % (messages/<-rpc %))) (update :message #(when % (messages/<-rpc %)))
(update :message #(when % (messages/<-rpc %))) (update :reply-message #(when % (messages/<-rpc %)))
(update :reply-message #(when % (messages/<-rpc %))) (dissoc :chatId))
(dissoc :chatId))) (not config/new-activity-center-enabled?)
(assoc :color (rand-nth colors/chat-colors))))

View File

@ -1,8 +1,8 @@
(ns status-im.data-store.activities-test (ns status-im.data-store.activities-test
(:require [cljs.test :refer [deftest is testing]] (:require [cljs.test :refer [deftest is testing]]
[quo.design-system.colors :as colors]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.data-store.activities :as store])) [status-im.data-store.activities :as store]
[status-im.utils.config :as config]))
(def chat-id (def chat-id
"0x04c66155") "0x04c66155")
@ -18,84 +18,82 @@
:replyMessage {}}) :replyMessage {}})
(deftest <-rpc-test (deftest <-rpc-test
(testing "assocs random chat color" (with-redefs [config/new-activity-center-enabled? true]
(is (contains? (set colors/chat-colors) (:color (store/<-rpc raw-notification))))) (testing "renames keys"
(is (= {:name chat-name
:chat-id chat-id
:contact-verification-status constants/contact-verification-state-pending}
(-> raw-notification
store/<-rpc
(dissoc :last-message :message :reply-message)))))
(testing "renames keys" (testing "transforms messages from RPC response"
(is (= {:name chat-name (is (= {:last-message {:quoted-message nil
:chat-id chat-id :outgoing-status nil
:contact-verification-status constants/contact-verification-state-pending} :command-parameters nil
(-> raw-notification :content {:sticker nil
store/<-rpc :rtl? nil
(dissoc :color :last-message :message :reply-message))))) :ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}
:message nil
:reply-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}}
(-> raw-notification
store/<-rpc
(select-keys [:last-message :message :reply-message])))))
(testing "transforms messages from RPC response" (testing "augments notification based on its type"
(is (= {:last-message {:quoted-message nil (is (= {:chat-name chat-name
:outgoing-status nil :chat-type constants/private-group-chat-type
:command-parameters nil :name chat-name}
:content {:sticker nil (-> raw-notification
:rtl? nil (assoc :type constants/activity-center-notification-type-reply)
:ens-name nil store/<-rpc
:parsed-text nil (select-keys [:name :chat-type :chat-name :public? :group-chat]))))
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}
:message nil
:reply-message {:quoted-message nil
:outgoing-status nil
:command-parameters nil
:content {:sticker nil
:rtl? nil
:ens-name nil
:parsed-text nil
:response-to nil
:chat-id nil
:image nil
:line-count nil
:links nil
:text nil}
:outgoing false}}
(-> raw-notification
store/<-rpc
(select-keys [:last-message :message :reply-message])))))
(testing "augments notification based on its type" (is (= {:chat-name chat-name
(is (= {:chat-name chat-name :chat-type constants/private-group-chat-type
:chat-type constants/private-group-chat-type :name chat-name}
:name chat-name} (-> raw-notification
(-> raw-notification (assoc :type constants/activity-center-notification-type-mention)
(assoc :type constants/activity-center-notification-type-reply) store/<-rpc
store/<-rpc (select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name (is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type :chat-type constants/private-group-chat-type
:name chat-name} :group-chat true
(-> raw-notification :name chat-name
(assoc :type constants/activity-center-notification-type-mention) :public? false}
store/<-rpc (-> raw-notification
(select-keys [:name :chat-type :chat-name :public? :group-chat])))) (assoc :type constants/activity-center-notification-type-private-group-chat)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))
(is (= {:chat-name chat-name (is (= {:chat-name chat-name
:chat-type constants/private-group-chat-type :chat-type constants/one-to-one-chat-type
:group-chat true :group-chat false
:name chat-name :name chat-name
:public? false} :public? false}
(-> raw-notification (-> raw-notification
(assoc :type constants/activity-center-notification-type-private-group-chat) (assoc :type constants/activity-center-notification-type-one-to-one-chat)
store/<-rpc store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat])))) (select-keys [:name :chat-type :chat-name :public? :group-chat])))))))
(is (= {:chat-name chat-name
:chat-type constants/one-to-one-chat-type
:group-chat false
:name chat-name
:public? false}
(-> raw-notification
(assoc :type constants/activity-center-notification-type-one-to-one-chat)
store/<-rpc
(select-keys [:name :chat-type :chat-name :public? :group-chat]))))))