diff --git a/tests/model/test_model.py b/tests/model/test_model.py index 51c71eb753..5d8c90433a 100644 --- a/tests/model/test_model.py +++ b/tests/model/test_model.py @@ -554,6 +554,55 @@ def test_topics_in_stream(self, mocker, model, topics_index, fetched, stream_id= assert model.index["topics"][stream_id] == return_value assert model.index["topics"][stream_id] is not return_value + @pytest.mark.parametrize( + "response, return_value", + [ + ( + {"result": "success", "msg": "", "email": "username@example.com"}, + "username@example.com", + ), + ( + {"result": "error", "msg": "Invalid stream ID", "code": "BAD_REQUEST"}, + "invalid", + ), + ], + ) + def test__fetch_stream_email_address( + self, mocker, response, model, return_value, stream_id=1 + ) -> None: + self.client.call_endpoint = mocker.Mock(return_value=response) + + result = model._fetch_stream_email_address(stream_id) + + self.client.call_endpoint.assert_called_once_with( + f"/streams/{stream_id}/email_address", method="GET" + ) + assert result == return_value + + @pytest.mark.parametrize( + "stream, fetched, response, return_value", + [ + ( + {"email_address": "username@example.com"}, + False, + {}, + "username@example.com", + ), + ({}, True, "username@example.com", "username@example.com"), + ({}, True, "invalid", ""), + ], + ) + def test_stream_copy_text( + self, mocker, model, stream, fetched, response, return_value, stream_id=1 + ): + model.stream_dict[stream_id] = stream + model._fetch_stream_email_address = mocker.Mock(return_value=response) + + result = model.stream_copy_text(stream_id) + + assert model._fetch_stream_email_address.called == fetched + assert result == return_value + # pre server v3 provide user_id or id as a property within user key # post server v3 provide user_id as a property outside the user key @pytest.mark.parametrize("user_key", ["user_id", "id", None]) diff --git a/tests/ui_tools/test_popups.py b/tests/ui_tools/test_popups.py index 2c5ce92d84..c75b1ab745 100644 --- a/tests/ui_tools/test_popups.py +++ b/tests/ui_tools/test_popups.py @@ -1398,9 +1398,11 @@ def test_stream_info_content__email_copy_text( general_stream: Dict[str, Any], stream_email_present: bool, expected_copy_text: str, + mocker: MockerFixture, ) -> None: if not stream_email_present: del general_stream["email_address"] + self.controller.model.stream_copy_text.return_value = "" model = self.controller.model stream_id = general_stream["stream_id"] diff --git a/zulipterminal/model.py b/zulipterminal/model.py index 22988c609b..6bbd8dee74 100644 --- a/zulipterminal/model.py +++ b/zulipterminal/model.py @@ -891,6 +891,30 @@ def topics_in_stream(self, stream_id: int) -> List[str]: return list(self.index["topics"][stream_id]) + def _fetch_stream_email_address(self, stream_id: int) -> str: + """ + Fetch stream's email address with specified stream_id and + returns stream_email_address as a String. + Endpoint added in Zulip 7.5 (ZFL 226) + """ + url = f"/streams/{stream_id}/email_address" + + response = self.client.call_endpoint(url, method="GET") + + if response.get("result") == "success": + email_address = response.get("email", "") + return str(email_address) + return "invalid" + + def stream_copy_text(self, stream_id: int) -> str: + stream = self.stream_dict[stream_id] + stream_email = stream.get("email_address", None) + if stream_email is None: + stream_email = self._fetch_stream_email_address(stream_id) + if stream_email == "invalid" or stream_email is None: + stream_email = "" + return stream_email + @staticmethod def exception_safe_result(future: "Future[str]") -> str: try: diff --git a/zulipterminal/ui_tools/views.py b/zulipterminal/ui_tools/views.py index a5a0221f41..5839555d6a 100644 --- a/zulipterminal/ui_tools/views.py +++ b/zulipterminal/ui_tools/views.py @@ -1381,11 +1381,13 @@ def __init__(self, controller: Any, stream_id: int) -> None: ) member_keys = ", ".join(map(repr, keys_for_command("STREAM_MEMBERS"))) - # FIXME: This field was removed from the subscription data in Zulip 7.5 / ZFL226 - # We should use the new /streams/{stream_id}/email_address endpoint instead - self._stream_email = stream.get("email_address", None) - if self._stream_email is None: + # This field was removed from the subscription data in Zulip 7.5 / ZFL226 + # Using the new /streams/{stream_id}/email_address endpoint + # if field isn't present in subscription data + self._stream_email = controller.model.stream_copy_text(stream_id) + if self._stream_email == "": stream_copy_text = "< Stream email is unavailable >" + self._stream_email = None else: email_keys = ", ".join(map(repr, keys_for_command("COPY_STREAM_EMAIL"))) stream_copy_text = f"Press {email_keys} to copy Stream email address"