tools: Add tools/check-node-fixtures.

This makes sure out fixture data for node tests
is realistic, according to the schemas in
zerver/lib/event_schema.py.

Note that we are still in the process of extracting
schemas from test_events.py -> event_schema.py,
so the checks here are somewhat incomplete as of
now.

One nice thing is that the program will tell us
what checkers are missing, so this can motivate
us to move more checkers to event_schema.py.

I considered just making this happen as part of
tools/test-js-with-node, but it's convenient to
run by itself.  Also, it currently requires
Django (although we could fix that), which makes
it just expensive enough that I wouldn't want
to always run it before the node tests.
This commit is contained in:
Steve Howell 2020-07-25 18:58:43 +00:00 committed by Tim Abbott
parent 0eb206f97e
commit f9808d0c15
2 changed files with 103 additions and 0 deletions

100
tools/check-node-fixtures Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/env python3
import argparse
import os
import subprocess
import sys
from typing import Any, Callable, Dict, Optional
import ujson
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)
# 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
SKIP_LIST = [
# The event_schema checker for user_status is overly strict.
"user_status__revoke_away",
"user_status__set_away",
"user_status__set_status_text",
]
def get_event_checker(
event: Dict[str, Any]
) -> Optional[Callable[[str, Dict[str, Any]], None]]:
name = "check_" + event["type"]
if "op" in event:
name += "_" + event["op"]
"""
In our backend tests we always want check_foo
to be the "main" API, but often _check_foo actually
conforms to validator name/event pattern better
than check_foo (which may layer on some more custom
checks). We can clean that up eventually, but now
we just work around it here in this younger tooling.
"""
for n in ["_" + name, name]:
if hasattr(event_schema, n):
return getattr(event_schema, n)
return None
def check_event(name: str, event: Dict[str, Any]) -> None:
event["id"] = 1
checker = get_event_checker(event)
if checker is not None:
checker(name, event)
else:
print(f"NEED SCHEMA: {name}")
def read_fixtures() -> Dict[str, Any]:
cmd = [
"node",
os.path.join(TOOLS_DIR, "node_lib/dump_fixtures.js"),
]
schema = subprocess.check_output(cmd)
return ujson.loads(schema)
def run() -> None:
fixtures = read_fixtures()
for name, event in fixtures.items():
if name in SKIP_LIST:
print(f"skip {name}")
continue
check_event(name, event)
if __name__ == "__main__":
run()

View File

@ -0,0 +1,3 @@
const events = require("../../frontend_tests/node_tests/lib/events.js");
console.info(JSON.stringify(events.fixtures, null, 4));