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