jenkins-bot has submitted this change and it was merged. Change subject: Add requirements.txt, improve test coverage. ......................................................................
Add requirements.txt, improve test coverage. Change-Id: I1c9d5ff9c7eca222913fed8d34b6ea1cdfded66a --- M server/bin/eventlogging-devserver M server/eventlogging/schema.py A server/requirements.txt M server/setup.py M server/tests/fixtures.py M server/tests/test_parser.py M server/tests/test_schema.py 7 files changed, 73 insertions(+), 20 deletions(-) Approvals: Ori.livneh: Looks good to me, approved jenkins-bot: Verified diff --git a/server/bin/eventlogging-devserver b/server/bin/eventlogging-devserver index 2a76e28..6f8378c 100755 --- a/server/bin/eventlogging-devserver +++ b/server/bin/eventlogging-devserver @@ -1,15 +1,30 @@ #!/usr/bin/env python # -*- coding: utf8 -*- """ - el-devserver - ------------ + eventlogging-devserver + ---------------------- + + Invoking this command-line tool will spawn a web server that can serve + as a test logging endpoint. Events logged against this server will be + validated verbosely and pretty-printed to the terminal. + + To use this, you probably want to set '$wgEventLoggingBaseUri' on your + test wiki to point at the host and port of this web server. The value + '//localhost:8080/event.gif' should work. + + usage: eventlogging-devserver [-h] [--host HOST] [--port PORT] + + optional arguments: + -h, --help show this help message and exit + --host HOST server host (default: 'localhost') + --port PORT server port (default: 8080) :copyright: (c) 2012 by Ori Livneh <[email protected]> :license: GNU General Public Licence 2.0 or later """ # pylint: disable=E0611 -from __future__ import print_function +from __future__ import print_function, unicode_literals import argparse import itertools @@ -26,8 +41,10 @@ argparser = argparse.ArgumentParser() -argparser.add_argument('--host', default='0.0.0.0') -argparser.add_argument('--port', default=8080, type=int) +argparser.add_argument('--host', default='localhost', + help='server host (default: localhost)') +argparser.add_argument('--port', default=8080, type=int, + help='server port (default: 8080)') args = argparser.parse_args() lexer = JSONLexer() @@ -39,6 +56,12 @@ parser = eventlogging.LogParser('%q %l %n %t %h') log_fmt = ('?%(QUERY_STRING)s %(SERVER_NAME)s %(SEQ_ID)d ' '%(TIME)s %(REMOTE_ADDR)s') + + +def make_headers(map): + """Encode a dictionary of HTTP headers to a list of tuples + containing bytes.""" + return [(k.encode('utf-8'), v.encode('utf-8')) for k, v in map.items()] class EventLoggingHandler(WSGIRequestHandler): @@ -82,14 +105,16 @@ requests.""" log_line = log_fmt % environ event, errors = validate(log_line) - valid = not errors - headers = [ - ('Server', server_software), - ('EventLogging-Valid', str(int(valid))) - ] - headers.extend(('EventLogging-Error', str(err)) for err in errors) - start_response('204 No Content', headers) + headers = { + 'Server': server_software, + 'Requested-Event-Valid': str(int(not errors)) + } + + for i, error in enumerate(errors): + headers['Validation-Error-%d' % i] = str(error) + + start_response(b'204 No Content', make_headers(headers)) print('── request ────────────') print(log_line) @@ -101,7 +126,8 @@ print('── validation ──────────') for error in errors: print(ansiformat('red', '✘ Error:'), error) - if valid: + + if not errors: print(ansiformat('green', '✔ Valid.')) print('────────────────────────') @@ -111,7 +137,8 @@ httpd = make_server(args.host, args.port, handle_event, handler_class=EventLoggingHandler) -print('EventLogging DevServer running on %s:%s...' % (args.host, args.port)) +print(ansiformat('*blue*', '⚒ EventLogging DevServer')) +print('Receiving events at http://%s:%s/event.gif...' % (args.host, args.port)) try: httpd.serve_forever() diff --git a/server/eventlogging/schema.py b/server/eventlogging/schema.py index b7d12d2..13fc663 100644 --- a/server/eventlogging/schema.py +++ b/server/eventlogging/schema.py @@ -86,10 +86,8 @@ try: content = urlopen(url).read().decode('utf-8') schema = json.loads(content) - except EnvironmentError as ex: - raise jsonschema.SchemaError('Failed to retrieve schema: %s' % ex) - except ValueError: - raise jsonschema.SchemaError('Could not decode JSON Schema at ' + url) + except (ValueError, EnvironmentError) as ex: + raise jsonschema.SchemaError('Schema fetch failure: %s' % ex) jsonschema.Draft3Validator.check_schema(schema) return schema @@ -104,6 +102,6 @@ # If `schema`, `revision` or `event` keys are missing, a # KeyError exception will be raised. We re-raise it as a # :exc:`ValidationError` to provide a simpler API for callers. - raise jsonschema.ValidationError('Missing key: %s' % ex.message) + raise jsonschema.ValidationError('Missing key: %s' % ex) schema = get_schema(scid, encapsulate=True) jsonschema.validate(capsule, schema) diff --git a/server/requirements.txt b/server/requirements.txt new file mode 100644 index 0000000..3dc0456 --- /dev/null +++ b/server/requirements.txt @@ -0,0 +1,4 @@ +jsonschema>=0.7 +pygments>=1.5 +pyzmq>=2.1 +sqlalchemy>=0.7 diff --git a/server/setup.py b/server/setup.py index 358991c..c8e118b 100644 --- a/server/setup.py +++ b/server/setup.py @@ -55,8 +55,8 @@ zip_safe=False, test_suite='tests', install_requires=( - "pygments>=1.5", "jsonschema>=0.7", + "pygments>=1.5", "pyzmq>=2.1", "sqlalchemy>=0.7", ), diff --git a/server/tests/fixtures.py b/server/tests/fixtures.py index 0f75c56..d89e2d8 100644 --- a/server/tests/fixtures.py +++ b/server/tests/fixtures.py @@ -176,6 +176,7 @@ super(HttpSchemaTestMixin, self).setUp() self.orig_urlopen = eventlogging.schema.urlopen eventlogging.schema.urlopen = self.urlopen_stub + eventlogging.schema.schema_cache.clear() def tearDown(self): """Restore original `urlopen`.""" diff --git a/server/tests/test_parser.py b/server/tests/test_parser.py index 2a7b9f6..17303a6 100644 --- a/server/tests/test_parser.py +++ b/server/tests/test_parser.py @@ -6,11 +6,23 @@ This module contains tests for :class:`eventlogging.LogParser`. """ +import calendar +import datetime import unittest import eventlogging +class NcsaTimestampTestCase(unittest.TestCase): + """Test case for converting to or from NCSA Common Log format.""" + + def test_ncsa_timestamp_handling(self): + epoch_ts = calendar.timegm(datetime.datetime.utcnow().utctimetuple()) + ncsa_ts = eventlogging.ncsa_utcnow() + self.assertAlmostEqual(eventlogging.ncsa_to_epoch(ncsa_ts), + epoch_ts, delta=100) + + class LogParserTestCase(unittest.TestCase): """Test case for LogParser.""" diff --git a/server/tests/test_schema.py b/server/tests/test_schema.py index aee533b..08d60e1 100644 --- a/server/tests/test_schema.py +++ b/server/tests/test_schema.py @@ -42,6 +42,12 @@ with self.assertRaises(eventlogging.SchemaError): eventlogging.schema.http_get_schema(TEST_SCHEMA_SCID) + def test_caching(self): + """Valid HTTP responses containing JSON Schema are cached.""" + self.http_resp = b'{"properties":{"value":{"type":"number"}}}' + eventlogging.get_schema(TEST_SCHEMA_SCID) + self.assertIn(TEST_SCHEMA_SCID, eventlogging.schema.schema_cache) + class SchemaTestCase(SchemaTestMixin, unittest.TestCase): """Tests for :module:`eventlogging.schema`.""" @@ -50,6 +56,11 @@ """Valid events validate.""" self.assertIsValid(self.event) + def test_incomplete_scid(self): + """Missing SCID in capsule object triggers validation failure.""" + self.event.pop('schema') + self.assertIsInvalid(self.event) + def test_missing_property(self): """Missing property in capsule object triggers validation failure.""" self.event.pop('timestamp') -- To view, visit https://gerrit.wikimedia.org/r/49469 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1c9d5ff9c7eca222913fed8d34b6ea1cdfded66a Gerrit-PatchSet: 2 Gerrit-Project: mediawiki/extensions/EventLogging Gerrit-Branch: master Gerrit-Owner: Ori.livneh <[email protected]> Gerrit-Reviewer: Ori.livneh <[email protected]> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
