mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 13:33:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			127 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			127 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
#
 | 
						|
# Validates that 3 data sources agree about the structure of Zulip's events API:
 | 
						|
#
 | 
						|
# * Node fixtures for the server_events_dispatch.js tests.
 | 
						|
# * OpenAPI definitions in zerver/openapi/zulip.yaml
 | 
						|
# * The schemas defined in zerver/lib/events_schema.py used for the
 | 
						|
#   Zulip server's test suite.
 | 
						|
#
 | 
						|
# We compare the Python and OpenAPI schemas by converting the OpenAPI data
 | 
						|
# into the event_schema style of types and the diffing the schemas.
 | 
						|
import argparse
 | 
						|
import os
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
from collections.abc import Callable
 | 
						|
from typing import Any
 | 
						|
 | 
						|
import orjson
 | 
						|
 | 
						|
TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
						|
sys.path.insert(0, os.path.dirname(TOOLS_DIR))
 | 
						|
ROOT_DIR = os.path.dirname(TOOLS_DIR)
 | 
						|
 | 
						|
EVENTS_JS = "web/tests/lib/events.cjs"
 | 
						|
 | 
						|
# check for the venv
 | 
						|
from tools.lib import sanity_check
 | 
						|
 | 
						|
sanity_check.check_venv(__file__)
 | 
						|
 | 
						|
USAGE = """
 | 
						|
 | 
						|
    This program reads in fixture data for our
 | 
						|
    node tests, and then it validates the fixture
 | 
						|
    data with checkers from event_schema.py (which
 | 
						|
    are the same Python functions we use to validate
 | 
						|
    events in test_events.py).
 | 
						|
 | 
						|
    It currently takes no arguments.
 | 
						|
"""
 | 
						|
 | 
						|
parser = argparse.ArgumentParser(usage=USAGE)
 | 
						|
parser.parse_args()
 | 
						|
 | 
						|
# We can eliminate the django dependency in event_schema,
 | 
						|
# but unfortunately it"s coupled to modules like validate.py
 | 
						|
# and topic.py.
 | 
						|
import django
 | 
						|
 | 
						|
os.environ["DJANGO_SETTINGS_MODULE"] = "zproject.test_settings"
 | 
						|
django.setup()
 | 
						|
 | 
						|
from zerver.lib import event_schema
 | 
						|
 | 
						|
make_checker = event_schema.__dict__["make_checker"]
 | 
						|
 | 
						|
 | 
						|
def get_event_checker(event: dict[str, Any]) -> Callable[[str, dict[str, Any]], None]:
 | 
						|
    # Follow the naming convention to find the event checker.
 | 
						|
    # Start by grabbing the event type.
 | 
						|
    name = event["type"]
 | 
						|
 | 
						|
    # Handle things like AttachmentRemoveEvent
 | 
						|
    if "op" in event:
 | 
						|
        name += "_" + event["op"].title()
 | 
						|
 | 
						|
    # Change to CamelCase
 | 
						|
    name = name.replace("_", " ").title().replace(" ", "")
 | 
						|
 | 
						|
    # Use EventModernPresence type to check "presence" events
 | 
						|
    if name == "Presence":
 | 
						|
        name = "Modern" + name
 | 
						|
 | 
						|
    # And add the prefix.
 | 
						|
    name = "Event" + name
 | 
						|
 | 
						|
    if not hasattr(event_schema, name):
 | 
						|
        raise ValueError(f"We could not find {name} in event_schemas.py")
 | 
						|
 | 
						|
    return make_checker(getattr(event_schema, name))
 | 
						|
 | 
						|
 | 
						|
def check_event(name: str, event: dict[str, Any]) -> None:
 | 
						|
    event["id"] = 1
 | 
						|
    checker = get_event_checker(event)
 | 
						|
    try:
 | 
						|
        checker(name, event)
 | 
						|
    except AssertionError:
 | 
						|
        print(f"\n{EVENTS_JS} has bad data for {name}:\n\n")
 | 
						|
        raise
 | 
						|
 | 
						|
 | 
						|
def read_fixtures() -> dict[str, Any]:
 | 
						|
    cmd = [
 | 
						|
        "node",
 | 
						|
        os.path.join(TOOLS_DIR, "node_lib/dump_fixtures.js"),
 | 
						|
    ]
 | 
						|
    schema = subprocess.check_output(cmd)
 | 
						|
    return orjson.loads(schema)
 | 
						|
 | 
						|
 | 
						|
def verify_fixtures_are_sorted(names: list[str]) -> None:
 | 
						|
    for i in range(1, len(names)):
 | 
						|
        if names[i] < names[i - 1]:
 | 
						|
            raise Exception(
 | 
						|
                f"""
 | 
						|
                Please keep your fixtures in order within
 | 
						|
                your events.cjs file.  The following
 | 
						|
                key is out of order
 | 
						|
 | 
						|
                {names[i]}
 | 
						|
                """
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
def run() -> None:
 | 
						|
    fixtures = read_fixtures()
 | 
						|
    verify_fixtures_are_sorted(list(fixtures.keys()))
 | 
						|
    for name, event in fixtures.items():
 | 
						|
        check_event(name, event)
 | 
						|
    print(f"Successfully checked {len(fixtures)} fixtures. All tests passed.")
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    run()
 |