def test_can_use_existing_connection(self): DS = TestDataSource([1, 2, 3], 3) C = DictConnection() R = Reporter(DS, db_connection=C) self.assertEqual(C.get_topic(R.topic_id)["name"], "topic") self.assertEqual(C.get_topic(R.topic_id)["fields"], ["number"])
def test_run_data_tools_topic_not_found(self): C = DictConnection() result = C.get_overview(['invalid_id']) self.assertEqual(result.get('statistics'), []) warnings = result.get('warnings') self.assertEqual(warnings[0], topic_not_found('invalid_id'))
def test_topic_is_validated_on_creation(self): data_tools_d = {"field": "number", "method": "average"} C = DictConnection() with self.assertRaises(ValidationError): C.add_topic("topic", description="description", fields=["number"], data_tools=data_tools_d)
def test_add_data_validations(self): s = ServerHandlers(DictConnection()) self._assert_status(404, s.add_data, None, dict(number=3)) topic_id = self._create_topic(s, dict(name="topic", fields=["number"])) self._assert_status(400, s.add_data, topic_id, dict(letter="a"))
def test_add_data_get_data_and_get_latest_and_get_summary_get_overview(self): s = ServerHandlers(DictConnection()) self._assert_status(404, s.get_latest, None) data_tools = [ {"field":"number", "method":"line"}, ] topic_id = self._create_topic(s, dict(name="topic", fields=["number"], data_tools=data_tools)) self._assert_status(404, s.get_latest, topic_id) response = self._assert_status(200, s.get_data, topic_id, {}) self.assertEqual(len(response), 0) self._assert_status(200, s.add_data, topic_id, dict(number=3)) response = self._assert_status(200, s.get_latest, topic_id) self.assertEqual(response.get("number"), 3) self._assert_status(200, s.add_data, topic_id, dict(number=3)) response = self._assert_status(200, s.get_data, topic_id, {}) self.assertEqual(len(response), 2) self._assert_status(200, s.get_summary, topic_id, {}) for i in range(5): self._assert_status(200, s.add_data, topic_id, dict(number=i)) response = self._assert_status(200, s.get_overview, None, dict(aggregate_to=3)) data = response["statistics"][0]["payload"]["data"]["datasets"][0]["data"] self.assertLessEqual(len(data), 3)
def test_summary_overview_aggregate(self): data_tools = [ { "field": "number", "method": "line" }, ] units_d = {"field": "number", "unit": "scalar"} C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["number"], data_tools=data_tools, units=[units_d]) for i in range(10): C.add_data(topic_id, {"number": i}) result = C.get_overview(aggregate_to=3) data = result["statistics"][0]["payload"]["data"]["datasets"][0][ "data"] self.assertEqual(len(data), 3) result = C.get_summary(topic_id, aggregate_to=3) data = result["statistics"][0]["payload"]["data"]["datasets"][0][ "data"] self.assertEqual(len(data), 3)
def test_get_latest_returns_latest_data_element_for_topic(self): C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["number"]) C.add_data(topic_id, {"number": 3}) C.add_data(topic_id, {"number": 2}) C.add_data(topic_id, {"number": 1}) latest = C.get_latest(topic_id) self.assertEqual(latest["number"], 1)
def test_get_summary_average_ignores_invalid_values(self): data_tools = [{"field": "number", "method": "average"}] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["number"], data_tools=data_tools) C.add_data(topic_id, {"number": 3}) C.add_data(topic_id, {"number": None}) C.add_data(topic_id, {"number": "Not a number"}) summary = C.get_summary(topic_id) self.assertEqual(summary["topic"], "topic") self.assertEqual(summary["description"], "description") value = summary["statistics"][0]["payload"] self.assertAlmostEqual(value["value"], 3.0) self.assertEqual(value["field"], "number") self.assertEqual(value["type"], "average")
def test_overview_and_comparison_include_units(self): data_tools = [ { "field": "number", "method": "latest" }, ] units_d = {"field": "number", "unit": "scalar"} C = DictConnection() for i in range(3): topic_id = C.add_topic("topic_" + str(i), description="description", fields=["number"], data_tools=data_tools, units=[units_d]) C.add_data(topic_id, {"number": i}) overview = C.get_overview() self.assertEqual(overview["topic_names"], ["topic_0", "topic_1", "topic_2"]) self.assertEqual(overview["fields"], ["number"]) self.assertEqual(len(overview["statistics"]), 3) self.assertEqual(overview["statistics"][0]["payload"]["unit"], "scalar")
def test_get_summary_ignores_invalid_fields(self): data_tools = [ { "field": "number", "method": "average" }, { "field": "number", "method": "doughnut" }, ] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["letter"], data_tools=data_tools) C.add_data(topic_id, {"letter": "a"}) summary = C.get_summary(topic_id) self.assertEqual(summary["topic"], "topic") self.assertEqual(summary["description"], "description") self.assertEqual(len(summary["warnings"]), 2) self.assertEqual(len(summary["statistics"]), 0)
def test_topics_backup_saves_dict_to_file(self): filename = f'{uuid4()}.json' C1 = DictConnection(f'/tmp/{filename}') C1.add_topic('Test topic 1') C1.add_topic('Test topic 2') C2 = DictConnection(f'/tmp/{filename}') topics = C2.get_topics() os.remove(f'/tmp/{filename}') self.assertEqual(len(topics), 2)
def test_latest_summary_returns_latest_item(self): data_tools = [{"field": "letter", "method": "latest"}] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["letter"], data_tools=data_tools) C.add_data(topic_id, {"letter": "a"}) C.add_data(topic_id, {"letter": "b"}) C.add_data(topic_id, {"letter": "c"}) summary = C.get_summary(topic_id) value = summary["statistics"][0]["payload"] self.assertEqual(value["field"], "letter") self.assertEqual(value["type"], "latest") self.assertEqual(value["value"], "c")
def _test_run_data_tools_for_many(self, fn, data_tools): topic_ids = [] C = DictConnection() for i in range(3): topic_ids.append( C.add_topic("topic_" + str(i), description="description", fields=["number"], data_tools=data_tools)) C.add_data(topic_ids[-1], {"number": 3}) C.add_data(topic_ids[-1], {"number": 4}) C.add_data(topic_ids[-1], {"number": 2}) if fn == "get_comparison": result = C.get_overview(topic_ids) elif fn == "get_overview": result = C.get_overview() self.assertEqual(result["topic_names"], ["topic_0", "topic_1", "topic_2"]) self.assertEqual(result["fields"], ["number"]) return result
def test_get_summary_produces_summary(self): types = ( "doughnut", "pie", ) data_tools = [dict(field="number", method=_type) for _type in types] + [{ "field": "number", "method": "average" }] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["number"], data_tools=data_tools) C.add_data(topic_id, {"number": 3}) C.add_data(topic_id, {"number": 4}) C.add_data(topic_id, {"number": 2}) summary = C.get_summary(topic_id) self.assertEqual(summary["topic"], "topic") self.assertEqual(summary["description"], "description") statistics = summary["statistics"] value = next(i for i in statistics if i.get("type") == "value").get("payload") self.assertAlmostEqual(value["value"], 3.0) self.assertEqual(value["field"], "number") self.assertEqual(value["type"], "average") charts = [ i.get("payload") for i in statistics if i.get("type") == "chart" ] self.assertEqual(charts, [{ "field": "number", "type": type_, "data": { "datasets": [{ "data": [1, 1, 1], "label": "topic" }], "labels": [2, 3, 4], } } for type_ in types])
def test_aggregate(self): s = ServerHandlers(DictConnection()) topic_id = self._create_topic(s, AGGREGATE_ALWAYS_TOPIC) for data in AGGREGATE_ALWAYS_DATA: data['timestamp'] = isoparse(data['timestamp']).replace(tzinfo=None) self._assert_status(200, s.add_data, topic_id, data) tests = [ (s.get_summary, (topic_id, dict(aggregate_to="5", aggregate_with='sum')), 4), (s.get_summary, (topic_id, dict(aggregate_to="5", aggregate_with='sum', aggregate_always="tRuE")), 2), (s.get_overview, (None, dict(aggregate_to="5", aggregate_with='sum')), 4), (s.get_overview, (None, dict(aggregate_to="5", aggregate_with='sum', aggregate_always="True")), 2), ] for fn, params, count in tests: data = self._assert_status(200, fn, *params) aggregated = data['statistics'][0]['payload']['data']['datasets'][0]['data'] self.assertEqual(len(aggregated), count)
def test_line_visualization_gives_timestamps_in_utc(self): data_tools = [{"field": "number", "method": "line"}] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["number"], data_tools=data_tools) C.add_data(topic_id, {"number": 1}) C.add_data(topic_id, {"number": 2}) C.add_data(topic_id, {"number": 3}) summary = C.get_summary(topic_id) chart = summary["statistics"][0]["payload"] self.assertEqual(chart["field"], "number") self.assertEqual(chart["type"], "line") iso8601z_re = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z" field_to_test = chart["data"]["datasets"][0]["data"][0]["x"] self.assertRegex(field_to_test, iso8601z_re)
def test_overview_ignores_templates(self): data_tools = [ { "field": "number", "method": "line" }, ] units_d = {"field": "number", "unit": "scalar"} C = DictConnection() C.add_topic("Test template", type_str="template", fields=['number'], units=[units_d], data_tools=data_tools) C.add_topic("Test topic", template="Test template") data = C.get_overview() self.assertEqual(len(data["warnings"]), 1) self.assertNotIn(data["warnings"][0], "template")
def test_last_truthy_falsy_summary_returns_correct_timestamp(self): data_tools = [{ "field": "onoff", "method": "last_truthy" }, { "field": "onoff", "method": "last_falsy" }] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["onoff"], data_tools=data_tools) for i in [False, True, False, True]: C.add_data(topic_id, {"onoff": i}) summary = C.get_summary(topic_id) data = C.get_data(topic_id) self.assertEqual(summary["statistics"][0]["payload"]["value"], data[3]["timestamp"]) self.assertEqual(summary["statistics"][1]["payload"]["value"], data[2]["timestamp"])
def test_get_summary_writes_warning_to_output_when_unsupported_method_requested( self): data_tools = [ { "field": "number", "method": "cow" }, { "field": "number", "method": "moose" }, ] C = DictConnection() topic_id = C.add_topic("topic", description="description", fields=["number"], data_tools=data_tools) C.add_data(topic_id, {"number": 3}) summary = C.get_summary(topic_id) self.assertEqual(summary["warnings"], [ 'The requested method "cow" is not supported.', 'The requested method "moose" is not supported.' ])
def setUp(self): self.connection = DictConnection() self.server = generate_app(db_connection=self.connection).test_client()
def test_get_topic(self): s = ServerHandlers(DictConnection()) self._assert_status(404, s.get_topic, None) topic_id = self._create_topic(s, dict(name="topic", fields=["number"])) self._assert_status(200, s.get_topic, topic_id)
def test_add_topic_returns_400_when_topic_name_missing(self): s = ServerHandlers(DictConnection()) self._assert_status(400, s.add_topic, {})
def test_add_topic_returns_400_when_topic_data_is_invalid(self): s = ServerHandlers(DictConnection()) self._assert_status(400, s.add_topic, dict(name="test", fields=123))
def test_add_topic_returns_200_on_success(self): s = ServerHandlers(DictConnection()) self._assert_status(200, s.add_topic, dict(name="topic"))
def setUp(self): self.C = DictConnection()
def test_get_topics_raises_keyerror_on_template_not_found(self): C = DictConnection() C.add_topic('test_template', type_str='template') C.add_topic('topic', template='test_template') C.get_topics() C._dict['topics'] = C._dict['topics'][1:] with self.assertRaises(KeyError): C.get_topics()
class ParallelExecTest(TestCase): def _create_topic(self, name, cmd, db_connection=None): argv = [ *COMMON_ARGS, 'create', '-N', name, '--start-in', '0', '--repeat-for', '0.5', '-c', *cmd ] with patch('sys.argv', argv): main() topics = db_connection.get_topics(template='execution') self.assertEqual(len(topics), 1) topic_d = topics[0] self.assertEqual(topic_d['name'], f'{name} #1') self.assertEqual(topic_d['metadata']['command'], cmd) return topic_d['id'] def _execute(self, name, *args): with patch('sys.argv', [*COMMON_ARGS, 'exec', '-N', name, *args]): main() @patch('builtins.print') @patch('fdbk.builtin.parallel_exec._should_repeat', side_effect=[True, True, True, False]) @patch('fdbk.builtin.parallel_exec.run') @patch('fdbk.builtin.parallel_exec.create_db_connection', return_value=DictConnection()) def test_main(self, create_mock, run_mock, repeat_mock, print_mock): C = create_mock.return_value cmd = ['sleep', '5'] topic_id = self._create_topic(TEST_EXEC_NAME, cmd, C) topics = C.get_topics(template='execution') self.assertEqual(len(topics), 1) topic_d = topics[0] topic_id = topic_d['id'] self.assertEqual(topic_d['name'], f'{TEST_EXEC_NAME} #1') self.assertEqual(topic_d['metadata']['command'], cmd) # Execute process = CompletedProcess(cmd, 0, b'asd') run_mock.return_value = process self._execute(TEST_EXEC_NAME) run_mock.assert_called_with(cmd, stderr=STDOUT, stdout=PIPE) self.assertEqual(len(run_mock.mock_calls), 2) self.assertEqual(len(C.get_data(topic_id)), 2) console = C.get_data(topic_id)[0]["output"] self.assertIn('+ sleep 5', console) @patch('builtins.print') @patch('fdbk.builtin.parallel_exec.create_db_connection', return_value=DictConnection()) def test_main_suffix(self, create_mock, print_mock): C = create_mock.return_value # Create topics for name, repeat_for in [ (TEST_EXEC_NAME, '0'), (TEST_EXEC_NAME, '0.1'), ('Another test', '1'), ]: cmd = ['sleep', '0.1'] with patch('sys.argv', [ *COMMON_ARGS, 'create', '-N', name, '--start-in', '0', '--repeat-for', repeat_for, '-c', *cmd ]): main() topic_ids = [i.get('id') for i in C.get_topics(template='execution')] self.assertEqual(len(topic_ids), 3) self._execute(TEST_EXEC_NAME) self.assertEqual(len(C.get_data(topic_ids[0])), 0) self.assertGreaterEqual(len(C.get_data(topic_ids[1])), 1) self.assertEqual(len(C.get_data(topic_ids[2])), 0) @patch('builtins.exit') @patch('builtins.print') @patch('fdbk.builtin.parallel_exec.create_db_connection') def test_main_create_error(self, create_mock, print_mock, exit_mock): create_mock.return_value.add_topic.side_effect = RuntimeError name = 'Test execution' cmd = ['sleep', '5'] with patch( 'sys.argv', [*COMMON_ARGS, 'create', '-N', name, '--start-in', 0, '-c', *cmd]): main() exit_mock.assert_called_with(TOPIC_CREATION_FAILED) @patch('builtins.print') @patch('fdbk.builtin.parallel_exec.create_db_connection', return_value=DictConnection()) def test_json_console(self, create_mock, print_mock): C = create_mock.return_value cmd = [ executable or 'python', '-c', 'import sys, time;' 'print("out");' 'time.sleep(0.5);' 'print("err", file=sys.stderr);' ] topic_id = self._create_topic(TEST_EXEC_NAME, cmd, C) self._execute(TEST_EXEC_NAME, '--console-as-json') self.assertEqual(len(C.get_data(topic_id)), 1) console = C.get_data(topic_id)[0]['output'] self.assertEqual(len(console), 3) self.assertEqual(shlex.split(console[0]['text']), cmd) for stream, text in [ ('stdout', 'out'), ('stderr', 'err'), ]: msg = next(i for i in console if i['stream'] == stream) self.assertEqual(msg['stream'], stream, f'Stream: {stream}') self.assertEqual(msg['text'], text, f'Stream: {stream}')
def test_get_latest_handles_empty_list(self): C = DictConnection() topic_id = C.add_topic("topic") with self.assertRaises(IndexError): C.get_latest(topic_id)