mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
We now tell you how many fixtures were checked as part of the success message. Right now we're up to 118 checks.
124 lines
3.4 KiB
Python
Executable File
124 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
|
|
from zerver.lib.data_types import 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"]
|
|
|
|
# Work around the fact that we send a "saved_snippets" (plural)
|
|
# event type for an event that really just sends a single value.
|
|
if name == "saved_snippets":
|
|
name = "saved_snippet"
|
|
|
|
# Handle things like attachment_remove_event.
|
|
if "op" in event:
|
|
name += "_" + event["op"]
|
|
|
|
# And add the suffix.
|
|
name += "_event"
|
|
|
|
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.js 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()
|