diff --git a/tools/check-node-fixtures b/tools/check-node-fixtures index 5e6c8180da..09b24484b4 100755 --- a/tools/check-node-fixtures +++ b/tools/check-node-fixtures @@ -68,6 +68,8 @@ EXEMPT_OPENAPI_NAMES = [ "update_global_notifications_event", # Additional keys(push_users_notify) due to bug in API. "message_event", + # tuple handling + "muted_topics_event", ] diff --git a/zerver/lib/data_types.py b/zerver/lib/data_types.py index fa1d94825b..254db33791 100644 --- a/zerver/lib/data_types.py +++ b/zerver/lib/data_types.py @@ -151,6 +151,30 @@ class OptionalType: return schema(var_name, self.sub_type) +@dataclass +class TupleType: + sub_types: Sequence[Any] + + def check_data(self, var_name: str, val: Any) -> None: + if not (isinstance(val, list) or isinstance(val, tuple)): + raise AssertionError(f"{var_name} is not a list/tuple") + + if len(val) != len(self.sub_types): + raise AssertionError(f"{var_name} should have {len(self.sub_types)} items") + + for i, sub_type in enumerate(self.sub_types): + vname = f"{var_name}[{i}]" + check_data(sub_type, vname, val[i]) + + def schema(self, var_name: str) -> str: + sub_schemas = "\n".join( + sorted( + schema(str(i), sub_type) for i, sub_type in enumerate(self.sub_types) + ) + ) + return f"{var_name} (tuple):\n{indent(sub_schemas)}" + + @dataclass class UnionType: sub_types: Sequence[Any] diff --git a/zerver/lib/event_schema.py b/zerver/lib/event_schema.py index 456190211f..84846d1a68 100644 --- a/zerver/lib/event_schema.py +++ b/zerver/lib/event_schema.py @@ -15,6 +15,7 @@ from zerver.lib.data_types import ( NumberType, OptionalType, StringDictType, + TupleType, UnionType, UrlType, check_data, @@ -244,6 +245,23 @@ invites_changed_event = event_dict_type( ) check_invites_changed = make_checker(invites_changed_event) +muted_topic_type = TupleType( + [ + # We should use objects, not tuples! + str, # stream name + str, # topic name + int, # timestamp + ] +) + +muted_topics_event = event_dict_type( + required_keys=[ + ("type", Equals("muted_topics")), + ("muted_topics", ListType(muted_topic_type)), + ] +) +check_muted_topics = make_checker(muted_topics_event) + message_fields = [ ("avatar_url", OptionalType(str)), ("client", str), diff --git a/zerver/tests/test_data_types.py b/zerver/tests/test_data_types.py index 0cd5f5fb3a..b2a4e499c0 100644 --- a/zerver/tests/test_data_types.py +++ b/zerver/tests/test_data_types.py @@ -6,6 +6,7 @@ from zerver.lib.data_types import ( NumberType, OptionalType, StringDictType, + TupleType, UnionType, UrlType, schema, @@ -26,6 +27,7 @@ class MiscTest(ZulipTestCase): ("s", str), ("timestamp", NumberType()), ("flag", bool), + ("tup", TupleType([int, str])), ("level", EnumType([1, 2, 3])), ("lst", ListType(int)), ("config", StringDictType()), @@ -43,6 +45,9 @@ test (dict): maybe_n: int s: str timestamp: number + tup (tuple): + 0: int + 1: str type in ['realm'] url: str value (union): diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py index 8c41ce9cde..e52fb8f445 100644 --- a/zerver/tests/test_events.py +++ b/zerver/tests/test_events.py @@ -101,6 +101,7 @@ from zerver.lib.event_schema import ( check_hotspots, check_invites_changed, check_message, + check_muted_topics, check_reaction, check_realm_bot_add, check_realm_bot_delete, @@ -1018,14 +1019,6 @@ class NormalActionsTest(BaseAction): num_events=0) def test_muted_topics_events(self) -> None: - muted_topics_checker = check_events_dict([ - ('type', equals('muted_topics')), - ('muted_topics', check_list(check_tuple([ - check_string, # stream name - check_string, # topic name - check_int, # timestamp - ]))), - ]) stream = get_stream('Denmark', self.user_profile.realm) recipient = stream.recipient events = self.verify_action( @@ -1034,14 +1027,14 @@ class NormalActionsTest(BaseAction): stream, recipient, "topic")) - muted_topics_checker('events[0]', events[0]) + check_muted_topics('events[0]', events[0]) events = self.verify_action( lambda: do_unmute_topic( self.user_profile, stream, "topic")) - muted_topics_checker('events[0]', events[0]) + check_muted_topics('events[0]', events[0]) def test_change_avatar_fields(self) -> None: events = self.verify_action(