Mforns has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/192795

Change subject: Merge branch 'master' into scheduler
......................................................................

Merge branch 'master' into scheduler

Change-Id: I31aaed94021d0d46c73ae508d26e699ea1af01f2
---
M reportupdater/reportupdater/executor.py
M reportupdater/reportupdater/reader.py
M reportupdater/reportupdater/selector.py
A reportupdater/test/executor_test.py
M reportupdater/test/reader_test.py
M reportupdater/test/selector_test.py
6 files changed, 402 insertions(+), 107 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/analytics/limn-mobile-data 
refs/changes/95/192795/1

diff --git a/reportupdater/reportupdater/executor.py 
b/reportupdater/reportupdater/executor.py
index e3e5cf5..2ea045c 100644
--- a/reportupdater/reportupdater/executor.py
+++ b/reportupdater/reportupdater/executor.py
@@ -1,6 +1,7 @@
 
 import MySQLdb
 from datetime import datetime
+from selector import Selector
 
 
 TIMESTAMP_FORMAT = '%Y%m%d%H%M%S'
@@ -10,56 +11,97 @@
 class Executor(object):
 
     def __init__(self, selector, config):
+        if not isinstance(selector, Selector):
+            raise ValueError('Selector is not valid.')
+        if not isinstance(config, dict):
+            raise ValueError('Config is not a dict.')
         self.selector = selector
         self.config = config
 
     def run(self):
+        if 'databases' not in self.config:
+            raise KeyError('Databases is not in config.')
+        if not isinstance(self.config['databases'], dict):
+            raise ValueError('Databases is not a dict.')
         connections = {}
         for report in self.selector.run():
             sql_query = self.instantiate_sql(report)
             if report.db_key not in connections:
-                db_config = self.config['databases'][report.db_key]
-                connections[report.db_key] = self.create_connection(db_config)
+                connections[report.db_key] = 
self.create_connection(report.db_key)
             connection = connections[report.db_key]
             report.result = self.execute_sql(sql_query, connection)
             yield report
 
     def instantiate_sql(self, report):
         if report.is_timeboxed:
-            return report.sql_template.format(
-                from_timestamp=report.start.strftime(TIMESTAMP_FORMAT),
-                to_timestamp=report.end.strftime(TIMESTAMP_FORMAT),
-            )
+            try:
+                return report.sql_template.format(
+                    from_timestamp=report.start.strftime(TIMESTAMP_FORMAT),
+                    to_timestamp=report.end.strftime(TIMESTAMP_FORMAT),
+                )
+            except KeyError:
+                raise ValueError('SQL template contains unknown placeholders.')
         else:
             return report.sql_template
 
-    def create_connection(self, db_config):
-        return 'fake_connection'
-        # return MySQLdb.connect(
-        #     host=db_config['host'],
-        #     port=db_config['port'],
-        #     read_default_file=db_config['creds_file'],
-        #     db=db_config['db'],
-        #     charset='utf8',
-        #     use_unicode=True
-        # )
+    def create_connection(self, db_key):
+        databases = self.config['databases']
+        if db_key not in databases:
+            raise KeyError('DB key is not in config databases.')
+        db_config = databases[db_key]
+        if not isinstance(db_config, dict):
+            raise ValueError('DB config is not a dict.')
+
+        if 'host' not in db_config:
+            raise KeyError('Host is not in DB config.')
+        if 'port' not in db_config:
+            raise KeyError('Port is not in DB config.')
+        if 'creds_file' not in db_config:
+            raise KeyError('Creds file is not in DB config.')
+        if 'db' not in db_config:
+            raise KeyError('DB name is not in DB config.')
+
+        db_host = db_config['host']
+        db_port = db_config['port']
+        db_creds_file = db_config['creds_file']
+        db_name = db_config['db']
+
+        if not isinstance(db_host, str):
+            raise ValueError('Host is not a string.')
+        if not isinstance(db_port, int):
+            raise ValueError('Port is not an integer.')
+        if not isinstance(db_creds_file, str):
+            raise ValueError('Creds file is not a string.')
+        if not isinstance(db_name, str):
+            raise ValueError('DB name is not a string.')
+
+        try:
+            return MySQLdb.connect(
+                host=db_host,
+                port=db_port,
+                read_default_file=db_creds_file,
+                db=db_name,
+                charset='utf8',
+                use_unicode=True
+            )
+        except Exception, e:
+            raise RuntimeError('MySQLdb can not connect to database (' + 
str(e) + ').')
 
     def execute_sql(self, sql_query, connection):
-        return {
-            'header': [u'timestamp', u'value1', u'value2'],
-            'data': {datetime.strptime('2015-02-23', '%Y-%m-%d'): 
[u'2015-02-23', u'11', u'22']}
-        }
-        # cursor = connection.cursor()
-        # try:
-        #     cursor.execute(sql_query)
-        #     rows = cursor.fetchall()
-        #     header = [field[0] for field in cursor.description]
-        # except Exception, e:
-        #     pass # error!
-        # finally:
-        #     cursor.close()
-        # data = {}
-        # for row in rows:
-        #     date = datetime.strptime(row[0], DATE_FORMAT)
-        #     data[date] = row
-        # return {'header': header, 'data': data}
+        cursor = connection.cursor()
+        try:
+            cursor.execute(sql_query)
+            rows = cursor.fetchall()
+            header = [field[0] for field in cursor.description]
+        except Exception, e:
+            raise RuntimeError('MySQLdb can not execute query (' + str(e) + 
').')
+        finally:
+            cursor.close()
+        data = {}
+        for row in rows:
+            try:
+                date = datetime.strptime(row[0], DATE_FORMAT)
+            except ValueError:
+                raise ValueError('Query result contains bad formatted dates.')
+            data[date] = row
+        return {'header': header, 'data': data}
diff --git a/reportupdater/reportupdater/reader.py 
b/reportupdater/reportupdater/reader.py
index 7db2b50..fceed8e 100644
--- a/reportupdater/reportupdater/reader.py
+++ b/reportupdater/reportupdater/reader.py
@@ -1,11 +1,9 @@
 
-# TODO: error logging (run method)
-
-
 import os
 import io
 from datetime import datetime
 from report import Report
+import logging
 
 
 DATE_FORMAT = '%Y-%m-%d'
@@ -22,23 +20,24 @@
 
     def run(self):
         if 'reports' not in self.config:
-            raise KeyError('Reports is not in config.')
+            self.raise_critical(KeyError, 'Reports is not in config.')
         reports = self.config['reports']
         if not isinstance(reports, dict):
-            raise ValueError('Reports is not a dict.')
+            self.raise_critical(ValueError, 'Reports is not a dict.')
         for report_key, report_config in reports.iteritems():
             try:
                 report = self.create_report(report_key, report_config)
                 yield report
             except Exception, e:
-                # TODO: log error
-                pass
+                message = ('Report "{report_key}" could not be read from 
config '
+                           'because of error: {error}')
+                logging.error(message.format(report_key=report_key, 
error=str(e)))
 
 
     def create_report(self, report_key, report_config):
         if not isinstance(report_key, str):
             raise TypeError('Report key is not a string.')
-        elif not isinstance(report_config, dict):
+        if not isinstance(report_config, dict):
             raise TypeError('Report config is not a dict.')
         report = Report()
         report.key = report_key
@@ -73,9 +72,9 @@
             first_date_str = report_config['starts']
             try:
                 return datetime.strptime(first_date_str, DATE_FORMAT)
-            except TypeError, e:
+            except TypeError:
                 raise TypeError('Report starts is not a string.')
-            except ValueError, e:
+            except ValueError:
                 raise ValueError('Report starts does not match date format')
         elif is_timeboxed:
             raise ValueError('Timeboxed report does not specify starts.')
@@ -86,7 +85,7 @@
     def get_db_key(self):
         if 'defaults' not in self.config:
             raise KeyError('Defaults is not in config.')
-        elif 'db' not in self.config['defaults']:
+        if 'db' not in self.config['defaults']:
             raise KeyError('DB default is not in defaults config.')
         db_key = self.config['defaults']['db']
         if not isinstance(db_key, str):
@@ -129,9 +128,15 @@
                     date_str = line.split(',')[0]
                     try:
                         date = datetime.strptime(date_str, DATE_FORMAT)
-                    except ValueError, e:
+                    except ValueError:
                         raise ValueError('Output file date does not match date 
format.')
                     data[date] = line.strip().split(',')
                 previous_result['header'] = header
                 previous_result['data'] = data
         return previous_result
+
+
+    def raise_critical(self, error_class, message):
+        logging.critical(message)
+        raise error_class(message)
+
diff --git a/reportupdater/reportupdater/selector.py 
b/reportupdater/reportupdater/selector.py
index c11ba4d..532c1e7 100644
--- a/reportupdater/reportupdater/selector.py
+++ b/reportupdater/reportupdater/selector.py
@@ -1,11 +1,9 @@
 
-# TODO: error logging (run method)
-
-
 from copy import copy
 from datetime import datetime
 from dateutil.relativedelta import relativedelta
 from reader import Reader
+import logging
 
 class Selector(object):
 
@@ -13,7 +11,7 @@
     def __init__(self, reader, config):
         if not isinstance(reader, Reader):
             raise ValueError('Reader is not valid.')
-        elif not isinstance(config, dict):
+        if not isinstance(config, dict):
             raise ValueError('Config is not a dict.')
         self.reader = reader
         self.config = config
@@ -21,15 +19,18 @@
 
     def run(self):
         if 'current_exec_time' not in self.config:
-            raise KeyError('Current exec time is not in config.')
-        elif 'last_exec_time' not in self.config:
-            raise KeyError('Last exec time is not in config.')
+            self.raise_critical(KeyError, 'Current exec time is not in 
config.')
+        if 'last_exec_time' not in self.config:
+            self.raise_critical(KeyError, 'Last exec time is not in config.')
         now = self.config['current_exec_time']
         last_exec_time = self.config['last_exec_time']
         if not isinstance(now, datetime):
-            raise ValueError('Current exec time is not a date.')
-        elif not isinstance(last_exec_time, datetime):
-            raise ValueError('Last exec time is not a date.')
+            self.raise_critical(ValueError, 'Current exec time is not a date.')
+        if not isinstance(last_exec_time, datetime):
+            self.raise_critical(ValueError, 'Last exec time is not a date.')
+        if last_exec_time > now:
+            self.raise_critical(ValueError, 'Last time exec is greater than 
current exec time.')
+
         for report in self.reader.run():
             try:
                 if self.is_time_to_execute(last_exec_time, now, 
report.frequency):
@@ -39,14 +40,13 @@
                     else:
                         yield report
             except Exception, e:
-                # TODO: log error
-                pass
+                message = ('Report "{report_key}" could not be triaged for 
execution '
+                           'because of error: {error}')
+                logging.error(message.format(report_key=report.key, 
error=str(e)))
 
 
     def is_time_to_execute(self, last_exec_time, now, frequency):
         if last_exec_time:
-            if last_exec_time > now:
-                raise ValueError('Last time exec is greater than now.')
             t1 = self.truncate_date(last_exec_time, frequency)
         else:
             t1 = None
@@ -91,9 +91,14 @@
     def get_all_start_dates(self, first_date, current_date, increment):
         if first_date > current_date:
             raise ValueError('First date is greater than current date.')
-        elif increment.days < 0 or increment.months < 0:
+        if increment.days < 0 or increment.months < 0:
             raise ValueError('Increment is negative.')
         current_start = first_date
         while current_start <= current_date:
             yield current_start
             current_start += increment
+
+
+    def raise_critical(self, error_class, message):
+        logging.critical(message)
+        raise error_class(message)
diff --git a/reportupdater/test/executor_test.py 
b/reportupdater/test/executor_test.py
new file mode 100644
index 0000000..972f0b9
--- /dev/null
+++ b/reportupdater/test/executor_test.py
@@ -0,0 +1,244 @@
+
+from reportupdater.executor import Executor
+from reportupdater.selector import Selector
+from reportupdater.reader import Reader
+from reportupdater.report import Report
+from unittest import TestCase
+from mock import MagicMock
+from datetime import datetime
+import MySQLdb
+
+
+TIMESTAMP_FORMAT = '%Y%m%d%H%M%S'
+
+
+class ConnectionCursorMock(object):
+    def __init__(self, execute_callback, fetchall_callback):
+        self.execute_callback = execute_callback
+        self.fetchall_callback = fetchall_callback
+        self.description = []
+    def execute(self, sql_query):
+        if self.execute_callback:
+            self.execute_callback(sql_query)
+    def fetchall(self):
+        if self.fetchall_callback:
+            return self.fetchall_callback()
+    def close(self):
+        pass
+
+
+class ConnectionMock(object):
+    def __init__(self, execute_callback, fetchall_callback):
+        self.execute_callback = execute_callback
+        self.fetchall_callback = fetchall_callback
+    def cursor(self):
+        return ConnectionCursorMock(self.execute_callback, 
self.fetchall_callback)
+
+
+class ExecutorTest(TestCase):
+
+
+    def setUp(self):
+        self.db_key = 'executor_test'
+        self.db_config = {
+            'host': 'some.host',
+            'port': 12345,
+            'creds_file': '/some/creds/file',
+            'db': 'database'
+        }
+        self.config = {
+            'databases': {
+                self.db_key: self.db_config
+            }
+        }
+        reader = Reader(self.config)
+        selector = Selector(reader, self.config)
+        self.executor = Executor(selector, self.config)
+
+        self.report = Report()
+        self.report.is_timeboxed = True
+        self.report.start = datetime(2015, 1, 1)
+        self.report.end = datetime(2015, 1, 2)
+        self.report.db_key = self.db_key
+        self.report.sql_template = ('SELECT date, value FROM table '
+            'WHERE date >= {from_timestamp} AND date <= {to_timestamp};')
+
+
+    def 
test_instantiate_sql_when_report_is_timeboxed_and_format_raises_error(self):
+        self.report.sql_template = 'SOME sql WITH AN {unknown} placeholder;'
+        with self.assertRaises(ValueError):
+            self.executor.instantiate_sql(self.report)
+
+
+    def test_instantiate_sql_when_report_is_timeboxed(self):
+        result = self.executor.instantiate_sql(self.report)
+        expected = self.report.sql_template.format(
+            from_timestamp=self.report.start.strftime(TIMESTAMP_FORMAT),
+            to_timestamp=self.report.end.strftime(TIMESTAMP_FORMAT)
+        )
+        self.assertEqual(result, expected)
+
+
+    def test_instantiate_sql_when_report_is_not_timeboxed(self):
+        self.report.is_timeboxed = False
+        self.report.sql_template = 'SOME sql CODE;'
+        sql_query = self.executor.instantiate_sql(self.report)
+        self.assertEqual(sql_query, self.report.sql_template)
+
+
+    def test_create_connection_when_db_key_is_not_in_db_config(self):
+        del self.config['databases'][self.db_key]
+        with self.assertRaises(KeyError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_db_config_is_not_a_dict(self):
+        self.config['databases'][self.db_key] = 'not a dict'
+        with self.assertRaises(ValueError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_host_is_not_in_config(self):
+        del self.db_config['host']
+        with self.assertRaises(KeyError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_port_is_not_in_config(self):
+        del self.db_config['port']
+        with self.assertRaises(KeyError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_creds_file_is_not_in_config(self):
+        del self.db_config['creds_file']
+        with self.assertRaises(KeyError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_db_is_not_in_config(self):
+        del self.db_config['db']
+        with self.assertRaises(KeyError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_host_is_not_a_string(self):
+        self.db_config['host'] = ('not', 'a', 'string')
+        with self.assertRaises(ValueError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_port_is_not_an_integer(self):
+        self.db_config['port'] = ('not', 'an', 'integer')
+        with self.assertRaises(ValueError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_creds_file_is_not_a_string(self):
+        self.db_config['creds_file'] = ('not', 'a', 'string')
+        with self.assertRaises(ValueError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_db_is_not_a_string(self):
+        self.db_config['db'] = ('not', 'a', 'string')
+        with self.assertRaises(ValueError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection_when_mysqldb_connect_raises_error(self):
+        MySQLdb.connect = MagicMock(side_effect=Exception())
+        with self.assertRaises(RuntimeError):
+            self.executor.create_connection(self.db_key)
+
+
+    def test_create_connection(self):
+        MySQLdb.connect = MagicMock(return_value='connection')
+        connection = self.executor.create_connection(self.db_key)
+        self.assertEqual(connection, 'connection')
+
+
+    def test_execute_sql_when_mysqldb_execution_raises_error(self):
+        def execute_callback(sql_query):
+            raise Exception()
+        connection = ConnectionMock(execute_callback, None)
+        with self.assertRaises(RuntimeError):
+            self.executor.execute_sql('SOME sql;', connection)
+
+
+    def test_execute_sql_when_first_column_does_not_match_date_format(self):
+        def fetchall_callback():
+            return [
+                ['2015-01-01', '1'],
+                ['bad formated date', '2']
+            ]
+        connection = ConnectionMock(None, fetchall_callback)
+        with self.assertRaises(ValueError):
+            self.executor.execute_sql('SOME sql;', connection)
+
+
+    def test_execute_sql(self):
+        def fetchall_callback():
+            return [
+                ['2015-01-01', '1'],
+                ['2015-01-02', '2']
+            ]
+        connection = ConnectionMock(None, fetchall_callback)
+        result = self.executor.execute_sql('SOME sql;', connection)
+        expected = {
+            'header': [],
+            'data': {
+                datetime(2015, 1, 1): ['2015-01-01', '1'],
+                datetime(2015, 1, 2): ['2015-01-02', '2'],
+            }
+        }
+        self.assertEqual(result, expected)
+
+
+    def test_run_when_databases_is_not_in_config(self):
+        del self.config['databases']
+        with self.assertRaises(KeyError):
+            list(self.executor.run())
+
+
+    def test_run_when_config_databases_is_not_a_dict(self):
+        self.config['databases'] = 'not a dict'
+        with self.assertRaises(ValueError):
+            list(self.executor.run())
+
+
+    def test_run(self):
+        selected = [self.report]
+        self.executor.selector.run = MagicMock(return_value=selected)
+        self.executor.create_connection = MagicMock(return_value='connection')
+        result = {
+            'header': ['some', 'sql', 'header'],
+            'data': {datetime(2015, 1, 1): ['2015-01-01', 'some', 'value']}
+        }
+        self.executor.execute_sql = MagicMock(return_value=result)
+        executed = list(self.executor.run())
+        self.assertEqual(len(executed), 1)
+        report = executed[0]
+        self.assertEqual(report.result, result)
+
+
+    def test_init_when_selector_is_not_a_selector(self):
+        selector = 'not a reader'
+        with self.assertRaises(ValueError):
+            Executor(selector, {})
+
+
+    def test_init_when_config_is_not_a_dict(self):
+        reader = Reader({})
+        selector = Selector(reader, {})
+        with self.assertRaises(ValueError):
+            Executor(selector, None)
+
+
+    def test_init(self):
+        config = {'config': 'dict'}
+        reader = Reader(config)
+        selector = Selector(reader, config)
+        executor = Executor(selector, config)
+        self.assertEqual(executor.selector, selector)
+        self.assertEqual(selector.config, config)
diff --git a/reportupdater/test/reader_test.py 
b/reportupdater/test/reader_test.py
index 9549eda..34ee5ff 100644
--- a/reportupdater/test/reader_test.py
+++ b/reportupdater/test/reader_test.py
@@ -14,23 +14,23 @@
 
 
     def setUp(self):
-        self.default_report_key = 'reader_test'
-        self.default_report_config = {
+        self.report_key = 'reader_test'
+        self.report_config = {
             'starts': '2015-01-01',
             'timeboxed': True,
             'frequency': 'hourly'
         }
-        self.default_config = {
+        self.config = {
             'sql_folder': 'test/fixtures/sql',
             'output_folder': 'test/fixtures/output',
             'reports': {
-                self.default_report_key: self.default_report_config
+                self.report_key: self.report_config
             },
             'defaults': {
                 'db': 'db_key'
             }
         }
-        self.reader = Reader(self.default_config)
+        self.reader = Reader(self.config)
 
 
     def 
test_get_frequency_and_granularity_when_report_frequency_is_not_in_config(self):
@@ -138,7 +138,7 @@
 
     def test_get_db_key(self):
         result = self.reader.get_db_key()
-        expected = self.default_config['defaults']['db']
+        expected = self.config['defaults']['db']
         self.assertEqual(result, expected)
 
 
@@ -169,7 +169,7 @@
 
     def test_get_sql_template(self):
         result = self.reader.get_sql_template('reader_test')
-        sql_folder = self.default_config['sql_folder']
+        sql_folder = self.config['sql_folder']
         report_key = 'reader_test'
         sql_template_path = os.path.join(sql_folder, report_key + '.sql')
         with io.open(sql_template_path, encoding='utf-8') as sql_template_file:
@@ -202,7 +202,7 @@
         self.assertIn('header', result)
         self.assertIn('data', result)
         self.assertIsInstance(result['data'], dict)
-        output_folder = self.default_config['output_folder']
+        output_folder = self.config['output_folder']
         output_file_path = os.path.join(output_folder, report_key + '.csv')
         with io.open(output_file_path, encoding='utf-8') as output_file:
             lines = output_file.readlines()
@@ -219,19 +219,19 @@
     def test_create_report_when_report_key_is_not_a_string(self):
         report_key = ('not', 'a', 'string')
         with self.assertRaises(TypeError):
-            self.reader.create_report(report_key, self.default_report_config)
+            self.reader.create_report(report_key, self.report_config)
 
 
     def test_create_report_when_report_config_is_not_a_dict(self):
         report_config = None
         with self.assertRaises(TypeError):
-            self.reader.create_report(self.default_report_key, report_config)
+            self.reader.create_report(self.report_key, report_config)
 
 
     def test_create_report_when_helper_method_raises_error(self):
         self.reader.get_first_date = MagicMock(side_effect=Exception())
         with self.assertRaises(Exception):
-            self.reader.create_report(self.default_report_key, 
self.default_report_config)
+            self.reader.create_report(self.report_key, self.report_config)
 
 
     def test_create_report(self):
@@ -242,8 +242,8 @@
         self.reader.get_db_key = MagicMock(return_value='db_key')
         self.reader.get_sql_template = MagicMock(return_value='sql_template')
         self.reader.get_previous_result = 
MagicMock(return_value='previous_result')
-        report = self.reader.create_report(self.default_report_key, 
self.default_report_config)
-        self.assertEqual(report.key, self.default_report_key)
+        report = self.reader.create_report(self.report_key, self.report_config)
+        self.assertEqual(report.key, self.report_key)
         self.assertEqual(report.first_date, 'first_date')
         self.assertEqual(report.frequency, 'frequency')
         self.assertEqual(report.granularity, 'granularity')
diff --git a/reportupdater/test/selector_test.py 
b/reportupdater/test/selector_test.py
index 99ad567..b50c32e 100644
--- a/reportupdater/test/selector_test.py
+++ b/reportupdater/test/selector_test.py
@@ -12,19 +12,19 @@
 
 
     def setUp(self):
-        self.default_config = {
+        self.config = {
             'last_exec_time': datetime(2015, 1, 2, 23, 50, 30),
             'current_exec_time': datetime(2015, 1, 3, 1, 20, 30)
         }
-        reader = Reader(self.default_config)
-        self.selector = Selector(reader, self.default_config)
+        reader = Reader(self.config)
+        self.selector = Selector(reader, self.config)
 
-        self.default_report = Report()
-        self.default_report.first_date = datetime(2015, 1, 1)
-        self.default_report.frequency = 'hours'
-        self.default_report.granularity = 'days'
-        self.default_report.is_timeboxed = True
-        self.default_report.previous_result = {'header': None, 'data': {}}
+        self.report = Report()
+        self.report.first_date = datetime(2015, 1, 1)
+        self.report.frequency = 'hours'
+        self.report.granularity = 'days'
+        self.report.is_timeboxed = True
+        self.report.previous_result = {'header': None, 'data': {}}
 
 
     def test_is_time_to_execute_when_last_exec_time_is_none(self):
@@ -33,14 +33,6 @@
         frequency = 'hours'
         is_time = self.selector.is_time_to_execute(last_exec_time, now, 
frequency)
         self.assertTrue(is_time)
-
-
-    def test_is_time_to_execute_when_last_exec_time_is_greater_than_now(self):
-        last_exec_time = datetime(2015, 1, 2)
-        now = datetime(2015, 1, 1)
-        frequency = 'hours'
-        with self.assertRaises(ValueError):
-            self.selector.is_time_to_execute(last_exec_time, now, frequency)
 
 
     def test_is_time_to_execute_when_both_dates_are_in_the_same_hour(self):
@@ -77,7 +69,7 @@
 
     def test_get_interval_reports_when_previous_result_is_empty(self):
         now = datetime(2015, 1, 2)
-        result = list(self.selector.get_interval_reports(self.default_report, 
now))
+        result = list(self.selector.get_interval_reports(self.report, now))
         self.assertEqual(len(result), 2)
         self.assertEqual(result[0].start, datetime(2015, 1, 1))
         self.assertEqual(result[0].end, datetime(2015, 1, 2))
@@ -87,7 +79,7 @@
 
     def test_get_interval_reports_when_previous_result_has_some_dates(self):
         now = datetime(2015, 1, 2)
-        report = self.default_report
+        report = self.report
         report.previous_result = {
             'header': None,
             'data': {
@@ -102,7 +94,7 @@
 
     def test_get_interval_reports_when_previous_result_has_all_dates(self):
         now = datetime(2015, 1, 2)
-        report = self.default_report
+        report = self.report
         report.previous_result = {
             'header': None,
             'data': {
@@ -209,31 +201,38 @@
 
 
     def test_run_when_current_exec_time_is_not_in_config(self):
-        del self.default_config['current_exec_time']
+        del self.config['current_exec_time']
         with self.assertRaises(KeyError):
             list(self.selector.run())
 
 
     def test_run_when_last_exec_time_is_not_in_config(self):
-        del self.default_config['last_exec_time']
+        del self.config['last_exec_time']
         with self.assertRaises(KeyError):
             list(self.selector.run())
 
 
     def test_run_when_current_exec_time_is_not_a_datetime(self):
-        self.default_config['current_exec_time'] = 'invalid date'
+        self.config['current_exec_time'] = 'invalid date'
         with self.assertRaises(ValueError):
             list(self.selector.run())
 
 
     def test_run_when_last_exec_time_is_not_a_datetime(self):
-        self.default_config['last_exec_time'] = 'invalid date'
+        self.config['last_exec_time'] = 'invalid date'
+        with self.assertRaises(ValueError):
+            list(self.selector.run())
+
+
+    def test_run_when_last_exec_time_is_greater_than_current_exec_time(self):
+        self.config['last_exec_time'] = datetime(2015, 1, 2)
+        self.config['current_exec_time'] = datetime(2015, 1, 1)
         with self.assertRaises(ValueError):
             list(self.selector.run())
 
 
     def test_run_when_helper_method_raises_error(self):
-        read = [self.default_report]
+        read = [self.report]
         self.selector.reader.run = MagicMock(return_value=read)
         self.selector.is_time_to_execute = MagicMock(side_effect=Exception())
         selected = list(self.selector.run())
@@ -241,7 +240,7 @@
 
 
     def test_run_when_not_is_time_to_execute(self):
-        read = [self.default_report]
+        read = [self.report]
         self.selector.reader.run = MagicMock(return_value=read)
         self.selector.is_time_to_execute = MagicMock(return_value=False)
         selected = list(self.selector.run())
@@ -249,18 +248,18 @@
 
 
     def test_run_when_not_is_timeboxed(self):
-        self.default_report.is_timeboxed = False
-        read = [self.default_report]
+        self.report.is_timeboxed = False
+        read = [self.report]
         self.selector.reader.run = MagicMock(return_value=read)
         self.selector.is_time_to_execute = MagicMock(return_value=True)
         selected = list(self.selector.run())
         self.assertEqual(len(selected), 1)
-        self.assertEqual(selected[0], self.default_report)
+        self.assertEqual(selected[0], self.report)
 
 
     def test_run_when_is_timeboxed(self):
-        self.default_report.is_timeboxed = True
-        read = [self.default_report]
+        self.report.is_timeboxed = True
+        read = [self.report]
         self.selector.reader.run = MagicMock(return_value=read)
         self.selector.is_time_to_execute = MagicMock(return_value=True)
         self.selector.get_interval_reports = MagicMock(return_value=['report'])
@@ -270,14 +269,14 @@
 
 
     def test_init_when_reader_is_not_a_reader(self):
+        reader = 'not a reader'
         with self.assertRaises(ValueError):
-            reader = 'not a reader'
             Selector(reader, {})
 
 
     def test_init_when_config_is_not_a_dict(self):
+        reader = Reader({})
         with self.assertRaises(ValueError):
-            reader = Reader({})
             Selector(reader, None)
 
 

-- 
To view, visit https://gerrit.wikimedia.org/r/192795
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I31aaed94021d0d46c73ae508d26e699ea1af01f2
Gerrit-PatchSet: 1
Gerrit-Project: analytics/limn-mobile-data
Gerrit-Branch: master
Gerrit-Owner: Mforns <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to