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

Reply via email to