Skip to content

Commit 49b18eb

Browse files
authored
test: community chats (#6987)
* test: community chats * test: community chats review
1 parent daef497 commit 49b18eb

File tree

3 files changed

+301
-6
lines changed

3 files changed

+301
-6
lines changed

tests-functional/clients/services/wakuext.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,3 +694,48 @@ def joined_communities(self):
694694
def log_test(self):
695695
response = self.rpc_request("logTest")
696696
return response
697+
698+
def create_community_chat(self, community_id: str, c: dict):
699+
params = [community_id, c]
700+
response = self.rpc_request("createCommunityChat", params)
701+
return response
702+
703+
def edit_community_chat(self, community_id: str, chat_id: str, c: dict):
704+
params = [community_id, chat_id, c]
705+
response = self.rpc_request("editCommunityChat", params)
706+
return response
707+
708+
def delete_community_chat(self, community_id: str, chat_id: str):
709+
params = [community_id, chat_id]
710+
response = self.rpc_request("deleteCommunityChat", params)
711+
return response
712+
713+
def reorder_community_chat(self, community_id: str, chat_id: str, position: int):
714+
params = [{"communityId": community_id, "chatId": chat_id, "position": position}]
715+
response = self.rpc_request("reorderCommunityChat", params)
716+
return response
717+
718+
def share_community_channel_url_with_chat_key(self, community_id: str, channel_id: str):
719+
params = [{"communityId": community_id, "channelId": channel_id}]
720+
response = self.rpc_request("shareCommunityChannelURLWithChatKey", params)
721+
return response
722+
723+
def share_community_url_with_chat_key(self, community_id: str):
724+
params = [community_id]
725+
response = self.rpc_request("shareCommunityURLWithChatKey", params)
726+
return response
727+
728+
def parse_shared_url(self, url: str):
729+
params = [url]
730+
response = self.rpc_request("parseSharedURL", params)
731+
return response
732+
733+
def mute_community_chats(self, community_id: str, muted_type):
734+
params = [{"communityId": community_id, "mutedType": muted_type}]
735+
response = self.rpc_request("muteCommunityChats", params)
736+
return response
737+
738+
def un_mute_community_chats(self, community_id: str):
739+
params = [community_id]
740+
response = self.rpc_request("unMuteCommunityChats", params)
741+
return response

tests-functional/tests/test_wakuext_chats.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime, timedelta
1+
from datetime import datetime, timedelta, timezone
22

33
import pytest
44

@@ -92,8 +92,6 @@ def test_mute_chat(self):
9292
assert chat.get("muted", False) is True
9393
assert chat.get("muteTill", "") == result
9494

95-
@pytest.mark.skip(reason="Skipping mute chat tests due to failing on local build")
96-
# TODO: check in nightly build locally
9795
@pytest.mark.parametrize(
9896
"mute_type, time_delta",
9997
[
@@ -113,11 +111,11 @@ def test_mute_chat_v2(self, mute_type, time_delta):
113111
chat_id = self.receiver.public_key
114112

115113
result = self.sender.wakuext_service.mute_chat_v2(chat_id, mute_type)
116-
actual = datetime.strptime(result, "%Y-%m-%dT%H:%M:%SZ")
114+
actual = datetime.fromisoformat(result.replace("Z", "+00:00"))
117115

118-
expected = datetime.now() + time_delta
116+
expected = datetime.now(timezone.utc) + time_delta
119117
diff = expected - actual
120-
assert diff.total_seconds() < 2 # 2sec margin
118+
assert abs(diff.total_seconds()) < 2 # 2 sec margin
121119

122120
chat = self.sender.wakuext_service.chat(chat_id)
123121
assert chat.get("muted", False) is True
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
from uuid import uuid4
2+
from datetime import datetime, timezone, timedelta
3+
import pytest
4+
5+
from steps.messenger import MessengerSteps
6+
from clients.services.wakuext import ActivityCenterNotificationType
7+
from clients.signals import SignalType
8+
from resources.enums import MuteType
9+
10+
11+
@pytest.mark.rpc
12+
class TestCommunityChats(MessengerSteps):
13+
@pytest.fixture(autouse=True)
14+
def setup_backends(self, backend_new_profile):
15+
"""Initialize two backends (creator and requester) for each test function"""
16+
self.creator = backend_new_profile("creator")
17+
self.member = backend_new_profile("member")
18+
self.fake_address = "0x" + str(uuid4())[:8]
19+
self.community_id = self.create_community(self.creator)
20+
self.join_community(member=self.member, admin=self.creator)
21+
self.display_name = "chat_" + str(uuid4())
22+
self.chat_payload = {
23+
"identity": {
24+
"displayName": self.display_name,
25+
"emoji": "😀",
26+
"color": "#1f2c75",
27+
"description": self.display_name,
28+
},
29+
"viewersCanPostReactions": False,
30+
"hideIfPermissionsNotMet": False,
31+
"permissions": {"access": 1},
32+
}
33+
34+
def test_create_edit_delete_community_chat(self):
35+
create_resp = self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
36+
chats = create_resp.get("chats")
37+
communities = create_resp.get("communities")
38+
assert len(chats) == 1
39+
assert len(communities) == 1
40+
community_chats = communities[0].get("chats")
41+
42+
# assert chats[0].get("displayName") == self.chat_payload["identity"]["displayName"] # https://github.com/status-im/status-go/issues/6985
43+
assert chats[0].get("color") == self.chat_payload["identity"]["color"]
44+
assert chats[0].get("emoji") == self.chat_payload["identity"]["emoji"]
45+
assert chats[0].get("description") == self.chat_payload["identity"]["description"]
46+
assert chats[0].get("viewersCanPostReactions") == self.chat_payload["viewersCanPostReactions"]
47+
48+
# build a dict keyed by position
49+
chats_by_position = {chat["position"]: chat for chat in community_chats.values()}
50+
# position 1 should be the default chat
51+
assert chats_by_position[0].get("name") == "general"
52+
# assert chats_by_position[1].get("name") == self.chat_payload["identity"]["displayName"] # https://github.com/status-im/status-go/issues/6985
53+
assert chats_by_position[1].get("hideIfPermissionsNotMet") == self.chat_payload["hideIfPermissionsNotMet"]
54+
assert chats_by_position[1].get("permissions") == self.chat_payload["permissions"]
55+
added_chat_id = chats_by_position[1].get("id")
56+
57+
comm_before_delete = self.fetch_community(self.creator, self.community_id)
58+
assert added_chat_id in comm_before_delete.get("chats")
59+
60+
edit_payload = {
61+
"identity": {
62+
"displayName": "edited_" + str(uuid4()),
63+
"emoji": "🔑",
64+
"color": "#212331",
65+
"description": "edited_" + str(uuid4()),
66+
}
67+
}
68+
edit_resp = self.creator.wakuext_service.edit_community_chat(self.community_id, added_chat_id, edit_payload)
69+
assert edit_resp.get("chats")[0].get("color") == edit_payload["identity"]["color"]
70+
assert edit_resp.get("chats")[0].get("emoji") == edit_payload["identity"]["emoji"]
71+
assert edit_resp.get("chats")[0].get("description") == edit_payload["identity"]["description"]
72+
73+
del_resp = self.creator.wakuext_service.delete_community_chat(self.community_id, added_chat_id)
74+
assert del_resp.get("removedChats")[0] == added_chat_id
75+
assert added_chat_id not in del_resp.get("communities")[0].get("chats")
76+
77+
comm_after_delete = self.fetch_community(self.creator, self.community_id)
78+
assert added_chat_id not in comm_after_delete.get("chats")
79+
80+
def test_reorder_community_chat(self):
81+
resp = self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
82+
83+
# sort chats by position
84+
chats_before = sorted(resp.get("communities")[0].get("chats").values(), key=lambda c: c["position"])
85+
86+
first_chat = next(c for c in chats_before if c["position"] == 0)
87+
second_chat = next(c for c in chats_before if c["position"] == 1)
88+
89+
# reorder: move the new chat to position 0
90+
reorder_resp = self.creator.wakuext_service.reorder_community_chat(self.community_id, second_chat["id"], 0)
91+
92+
# fetch reordered chats and sort them again by position
93+
chats_after = sorted(reorder_resp["communities"][0]["chats"].values(), key=lambda c: c["position"])
94+
95+
# assertions: positions must be swapped
96+
assert chats_after[0]["id"] == second_chat["id"]
97+
assert chats_after[0]["position"] == 0
98+
assert chats_after[1]["id"] == first_chat["id"]
99+
assert chats_after[1]["position"] == 1
100+
101+
def test_create_and_share_community_channel_url(self):
102+
self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
103+
104+
channel_id = str(uuid4())
105+
share_resp = self.creator.wakuext_service.share_community_channel_url_with_chat_key(self.community_id, channel_id)
106+
assert share_resp is not None
107+
assert f"https://status.app/cc/{channel_id}" in share_resp
108+
109+
parsed = self.creator.wakuext_service.parse_shared_url(share_resp)
110+
assert parsed["channel"].get("channelUuid") == channel_id
111+
assert parsed["community"].get("communityId") == self.community_id
112+
113+
share_comm_resp = self.creator.wakuext_service.share_community_url_with_chat_key(self.community_id)
114+
assert "https://status.app/c" in share_comm_resp
115+
parsed_comm = self.creator.wakuext_service.parse_shared_url(share_comm_resp)
116+
assert parsed_comm["community"].get("communityId") == self.community_id
117+
118+
def test_mute_and_unmute_community_chats(self):
119+
self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
120+
121+
community_before = self.creator.wakuext_service.fetch_community(self.community_id)
122+
assert community_before.get("muted") is False
123+
124+
mute_resp = self.creator.wakuext_service.mute_community_chats(self.community_id, MuteType.MUTE_FOR15_MIN.value)
125+
assert mute_resp is not None
126+
127+
community_after_mute = self.creator.wakuext_service.fetch_community(self.community_id)
128+
assert community_after_mute.get("muted") is True
129+
assert community_after_mute.get("muteTill") == mute_resp
130+
131+
unmute_resp = self.creator.wakuext_service.un_mute_community_chats(self.community_id)
132+
assert unmute_resp is not None
133+
134+
community_after_unmute = self.creator.wakuext_service.fetch_community(self.community_id)
135+
assert community_after_unmute.get("muted") is False
136+
assert community_after_unmute.get("muteTill") == "0001-01-01T00:00:00Z"
137+
138+
@pytest.mark.parametrize("muted_type", [mt.value for mt in MuteType])
139+
def test_mute_types_are_applied(self, muted_type):
140+
create_resp = self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
141+
chat_id = create_resp.get("chats")[0].get("id")
142+
self.member.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=chat_id, timeout=10)
143+
144+
mute_resp = self.member.wakuext_service.mute_community_chats(self.community_id, muted_type)
145+
community_after_mute = self.member.wakuext_service.fetch_community(self.community_id)
146+
muted_flag = community_after_mute.get("muted")
147+
comm_mute_till = community_after_mute.get("muteTill")
148+
149+
if muted_type == MuteType.UNMUTED.value:
150+
assert muted_flag is False
151+
else:
152+
assert muted_flag is True
153+
154+
assert comm_mute_till is not None
155+
assert str(comm_mute_till) == str(mute_resp)
156+
157+
parsed_mute_till = datetime.fromisoformat(comm_mute_till.replace("Z", "+00:00"))
158+
159+
now = datetime.now(timezone.utc)
160+
# time remaining until mute expires
161+
remaining = parsed_mute_till - now
162+
163+
# map expected durations
164+
expected_map = {
165+
MuteType.MUTE_FOR15_MIN.value: timedelta(minutes=15),
166+
MuteType.MUTE_FOR1_HR.value: timedelta(hours=1),
167+
MuteType.MUTE_FOR8_HR.value: timedelta(hours=8),
168+
MuteType.MUTE_FOR24_HR.value: timedelta(hours=24),
169+
MuteType.MUTE_FOR1_WEEK.value: timedelta(days=7),
170+
MuteType.MUTE_TILL1_MIN.value: timedelta(minutes=1),
171+
}
172+
173+
# allow some tolerance for clocks and propagation delays
174+
tolerance = timedelta(seconds=30)
175+
176+
if muted_type in expected_map:
177+
expected = expected_map[muted_type]
178+
# remaining should be roughly equal to expected (allow +/- tolerance)
179+
assert abs(remaining - expected) <= tolerance, f"mute remaining {remaining}, expected ~{expected}"
180+
else:
181+
assert comm_mute_till == "0001-01-01T00:00:00Z"
182+
183+
def test_send_community_chat_message_with_mention(self):
184+
create_resp = self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
185+
chat_id = create_resp.get("chats")[0].get("id")
186+
self.member.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=chat_id, timeout=10)
187+
188+
text = f"Hi @{self.member.public_key}"
189+
# creator sends a chat message with a mention to trigger a notification
190+
send_resp = self.creator.wakuext_service.send_chat_message(chat_id, text)
191+
assert send_resp.get("chats")[0].get("lastMessage").get("text") == text
192+
message_id = send_resp.get("messages", [])[0].get("id", "")
193+
194+
# member receives that message even if chat is muted
195+
self.member.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_id, timeout=10)
196+
member_msgs_resp = self.member.wakuext_service.chat_messages(chat_id)
197+
assert member_msgs_resp.get("messages")[0].get("text") == text
198+
assert member_msgs_resp.get("messages")[0].get("mentioned") is True
199+
200+
# member will receive the mention notification
201+
resp = self.member.wakuext_service.get_activity_center_notifications([activity for activity in ActivityCenterNotificationType])
202+
notifications = resp.get("notifications")
203+
assert len(notifications) == 2
204+
assert notifications[0].get("communityId") == self.community_id
205+
assert notifications[0].get("chatId") == chat_id
206+
assert notifications[0].get("message").get("text") == text
207+
208+
def test_send_community_chat_message_while_chat_is_muted_and_then_unmuted(self):
209+
create_resp = self.creator.wakuext_service.create_community_chat(self.community_id, self.chat_payload)
210+
chat_id = create_resp.get("chats")[0].get("id")
211+
self.member.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=chat_id, timeout=10)
212+
213+
# muting the community chats
214+
self.member.wakuext_service.mute_community_chats(self.community_id, MuteType.MUTE_FOR15_MIN.value)
215+
216+
text = f"Hi @{self.member.public_key}"
217+
# creator sends a chat message with a mention to trigger a notification
218+
send_resp = self.creator.wakuext_service.send_chat_message(chat_id, text)
219+
assert send_resp.get("chats")[0].get("lastMessage").get("text") == text
220+
message_id = send_resp.get("messages", [])[0].get("id", "")
221+
222+
# member receives that message even if chat is muted
223+
self.member.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_id, timeout=10)
224+
member_msgs_resp = self.member.wakuext_service.chat_messages(chat_id)
225+
assert member_msgs_resp.get("messages")[0].get("text") == text
226+
assert member_msgs_resp.get("messages")[0].get("mentioned") is True
227+
228+
# member doesn't received the mention notification because the chat was muted
229+
resp = self.member.wakuext_service.get_activity_center_notifications([activity for activity in ActivityCenterNotificationType])
230+
notifications = resp.get("notifications")
231+
assert len(notifications) == 1
232+
assert notifications[0].get("chatId") == ""
233+
234+
self.member.wakuext_service.un_mute_community_chats(self.community_id)
235+
236+
send_resp = self.creator.wakuext_service.send_chat_message(chat_id, text)
237+
assert send_resp.get("chats")[0].get("lastMessage").get("text") == text
238+
message_id = send_resp.get("messages", [])[0].get("id", "")
239+
240+
# member receives that message even if chat is muted
241+
self.member.find_signal_containing_pattern(SignalType.MESSAGES_NEW.value, event_pattern=message_id, timeout=10)
242+
member_msgs_resp = self.member.wakuext_service.chat_messages(chat_id)
243+
assert member_msgs_resp.get("messages")[0].get("text") == text
244+
assert member_msgs_resp.get("messages")[0].get("mentioned") is True
245+
246+
# member receives again the mention notification because the chat was unmuted
247+
resp = self.member.wakuext_service.get_activity_center_notifications([activity for activity in ActivityCenterNotificationType])
248+
notifications = resp.get("notifications")
249+
assert len(notifications) == 2
250+
assert notifications[0].get("communityId") == self.community_id
251+
assert notifications[0].get("chatId") == chat_id
252+
assert notifications[0].get("message").get("text") == text

0 commit comments

Comments
 (0)