diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-03-23 01:11:34 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-03-24 10:39:35 +0100 |
commit | b4e0ba0b0aa84550d0b06338b59557c3050798c9 (patch) | |
tree | ec3f6c1d6166e6d3598ecd630aa64853f8842828 /test.py | |
parent | 07fa7a4bf8f7a6f799120fb9a5965a09bea6c38e (diff) | |
download | Trader-b4e0ba0b0aa84550d0b06338b59557c3050798c9.tar.gz Trader-b4e0ba0b0aa84550d0b06338b59557c3050798c9.tar.zst Trader-b4e0ba0b0aa84550d0b06338b59557c3050798c9.zip |
Store reports to database
Fixes https://git.immae.eu/mantisbt/view.php?id=57
Diffstat (limited to 'test.py')
-rw-r--r-- | test.py | 165 |
1 files changed, 134 insertions, 31 deletions
@@ -1386,18 +1386,7 @@ class MarketTest(WebMockTestCase): | |||
1386 | self.ccxt.transfer_balance.assert_any_call("USDT", 100, "exchange", "margin") | 1386 | self.ccxt.transfer_balance.assert_any_call("USDT", 100, "exchange", "margin") |
1387 | self.ccxt.transfer_balance.assert_any_call("ETC", 5, "margin", "exchange") | 1387 | self.ccxt.transfer_balance.assert_any_call("ETC", 5, "margin", "exchange") |
1388 | 1388 | ||
1389 | def test_store_report(self): | 1389 | def test_store_file_report(self): |
1390 | |||
1391 | file_open = mock.mock_open() | ||
1392 | m = market.Market(self.ccxt, self.market_args(), user_id=1) | ||
1393 | with self.subTest(file=None),\ | ||
1394 | mock.patch.object(m, "report") as report,\ | ||
1395 | mock.patch("market.open", file_open): | ||
1396 | m.store_report() | ||
1397 | report.merge.assert_called_with(store.Portfolio.report) | ||
1398 | file_open.assert_not_called() | ||
1399 | |||
1400 | report.reset_mock() | ||
1401 | file_open = mock.mock_open() | 1390 | file_open = mock.mock_open() |
1402 | m = market.Market(self.ccxt, self.market_args(), report_path="present", user_id=1) | 1391 | m = market.Market(self.ccxt, self.market_args(), report_path="present", user_id=1) |
1403 | with self.subTest(file="present"),\ | 1392 | with self.subTest(file="present"),\ |
@@ -1405,20 +1394,16 @@ class MarketTest(WebMockTestCase): | |||
1405 | mock.patch.object(m, "report") as report,\ | 1394 | mock.patch.object(m, "report") as report,\ |
1406 | mock.patch.object(market, "datetime") as time_mock: | 1395 | mock.patch.object(market, "datetime") as time_mock: |
1407 | 1396 | ||
1408 | time_mock.now.return_value = datetime.datetime(2018, 2, 25) | ||
1409 | report.print_logs = [[time_mock.now(), "Foo"], [time_mock.now(), "Bar"]] | 1397 | report.print_logs = [[time_mock.now(), "Foo"], [time_mock.now(), "Bar"]] |
1410 | report.to_json.return_value = "json_content" | 1398 | report.to_json.return_value = "json_content" |
1411 | 1399 | ||
1412 | m.store_report() | 1400 | m.store_file_report(datetime.datetime(2018, 2, 25)) |
1413 | 1401 | ||
1414 | file_open.assert_any_call("present/2018-02-25T00:00:00_1.json", "w") | 1402 | file_open.assert_any_call("present/2018-02-25T00:00:00_1.json", "w") |
1415 | file_open.assert_any_call("present/2018-02-25T00:00:00_1.log", "w") | 1403 | file_open.assert_any_call("present/2018-02-25T00:00:00_1.log", "w") |
1416 | file_open().write.assert_any_call("json_content") | 1404 | file_open().write.assert_any_call("json_content") |
1417 | file_open().write.assert_any_call("Foo\nBar") | 1405 | file_open().write.assert_any_call("Foo\nBar") |
1418 | m.report.to_json.assert_called_once_with() | 1406 | m.report.to_json.assert_called_once_with() |
1419 | report.merge.assert_called_with(store.Portfolio.report) | ||
1420 | |||
1421 | report.reset_mock() | ||
1422 | 1407 | ||
1423 | m = market.Market(self.ccxt, self.market_args(), report_path="error", user_id=1) | 1408 | m = market.Market(self.ccxt, self.market_args(), report_path="error", user_id=1) |
1424 | with self.subTest(file="error"),\ | 1409 | with self.subTest(file="error"),\ |
@@ -1427,10 +1412,106 @@ class MarketTest(WebMockTestCase): | |||
1427 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1412 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: |
1428 | file_open.side_effect = FileNotFoundError | 1413 | file_open.side_effect = FileNotFoundError |
1429 | 1414 | ||
1415 | m.store_file_report(datetime.datetime(2018, 2, 25)) | ||
1416 | |||
1417 | self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") | ||
1418 | |||
1419 | @mock.patch.object(market, "psycopg2") | ||
1420 | def test_store_database_report(self, psycopg2): | ||
1421 | connect_mock = mock.Mock() | ||
1422 | cursor_mock = mock.MagicMock() | ||
1423 | |||
1424 | connect_mock.cursor.return_value = cursor_mock | ||
1425 | psycopg2.connect.return_value = connect_mock | ||
1426 | m = market.Market(self.ccxt, self.market_args(), | ||
1427 | pg_config={"config": "pg_config"}, user_id=1) | ||
1428 | cursor_mock.fetchone.return_value = [42] | ||
1429 | |||
1430 | with self.subTest(error=False),\ | ||
1431 | mock.patch.object(m, "report") as report: | ||
1432 | report.to_json_array.return_value = [ | ||
1433 | ("date1", "type1", "payload1"), | ||
1434 | ("date2", "type2", "payload2"), | ||
1435 | ] | ||
1436 | m.store_database_report(datetime.datetime(2018, 3, 24)) | ||
1437 | connect_mock.assert_has_calls([ | ||
1438 | mock.call.cursor(), | ||
1439 | mock.call.cursor().execute('INSERT INTO reports("date", "market_config_id", "debug") VALUES (%s, %s, %s) RETURNING id;', (datetime.datetime(2018, 3, 24), None, False)), | ||
1440 | mock.call.cursor().fetchone(), | ||
1441 | mock.call.cursor().execute('INSERT INTO report_lines("date", "report_id", "type", "payload") VALUES (%s, %s, %s, %s);', ('date1', 42, 'type1', 'payload1')), | ||
1442 | mock.call.cursor().execute('INSERT INTO report_lines("date", "report_id", "type", "payload") VALUES (%s, %s, %s, %s);', ('date2', 42, 'type2', 'payload2')), | ||
1443 | mock.call.commit(), | ||
1444 | mock.call.cursor().close(), | ||
1445 | mock.call.close() | ||
1446 | ]) | ||
1447 | |||
1448 | connect_mock.reset_mock() | ||
1449 | with self.subTest(error=True),\ | ||
1450 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
1451 | psycopg2.connect.side_effect = Exception("Bouh") | ||
1452 | m.store_database_report(datetime.datetime(2018, 3, 24)) | ||
1453 | self.assertEqual(stdout_mock.getvalue(), "impossible to store report to database: Exception; Bouh\n") | ||
1454 | |||
1455 | def test_store_report(self): | ||
1456 | m = market.Market(self.ccxt, self.market_args(), user_id=1) | ||
1457 | with self.subTest(file=None, pg_config=None),\ | ||
1458 | mock.patch.object(m, "report") as report,\ | ||
1459 | mock.patch.object(m, "store_database_report") as db_report,\ | ||
1460 | mock.patch.object(m, "store_file_report") as file_report: | ||
1461 | m.store_report() | ||
1462 | report.merge.assert_called_with(store.Portfolio.report) | ||
1463 | |||
1464 | file_report.assert_not_called() | ||
1465 | db_report.assert_not_called() | ||
1466 | |||
1467 | report.reset_mock() | ||
1468 | m = market.Market(self.ccxt, self.market_args(), report_path="present", user_id=1) | ||
1469 | with self.subTest(file="present", pg_config=None),\ | ||
1470 | mock.patch.object(m, "report") as report,\ | ||
1471 | mock.patch.object(m, "store_file_report") as file_report,\ | ||
1472 | mock.patch.object(m, "store_database_report") as db_report,\ | ||
1473 | mock.patch.object(market, "datetime") as time_mock: | ||
1474 | |||
1475 | time_mock.now.return_value = datetime.datetime(2018, 2, 25) | ||
1476 | |||
1430 | m.store_report() | 1477 | m.store_report() |
1431 | 1478 | ||
1432 | report.merge.assert_called_with(store.Portfolio.report) | 1479 | report.merge.assert_called_with(store.Portfolio.report) |
1433 | self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") | 1480 | file_report.assert_called_once_with(datetime.datetime(2018, 2, 25)) |
1481 | db_report.assert_not_called() | ||
1482 | |||
1483 | report.reset_mock() | ||
1484 | m = market.Market(self.ccxt, self.market_args(), pg_config="present", user_id=1) | ||
1485 | with self.subTest(file=None, pg_config="present"),\ | ||
1486 | mock.patch.object(m, "report") as report,\ | ||
1487 | mock.patch.object(m, "store_file_report") as file_report,\ | ||
1488 | mock.patch.object(m, "store_database_report") as db_report,\ | ||
1489 | mock.patch.object(market, "datetime") as time_mock: | ||
1490 | |||
1491 | time_mock.now.return_value = datetime.datetime(2018, 2, 25) | ||
1492 | |||
1493 | m.store_report() | ||
1494 | |||
1495 | report.merge.assert_called_with(store.Portfolio.report) | ||
1496 | file_report.assert_not_called() | ||
1497 | db_report.assert_called_once_with(datetime.datetime(2018, 2, 25)) | ||
1498 | |||
1499 | report.reset_mock() | ||
1500 | m = market.Market(self.ccxt, self.market_args(), | ||
1501 | pg_config="pg_config", report_path="present", user_id=1) | ||
1502 | with self.subTest(file="present", pg_config="present"),\ | ||
1503 | mock.patch.object(m, "report") as report,\ | ||
1504 | mock.patch.object(m, "store_file_report") as file_report,\ | ||
1505 | mock.patch.object(m, "store_database_report") as db_report,\ | ||
1506 | mock.patch.object(market, "datetime") as time_mock: | ||
1507 | |||
1508 | time_mock.now.return_value = datetime.datetime(2018, 2, 25) | ||
1509 | |||
1510 | m.store_report() | ||
1511 | |||
1512 | report.merge.assert_called_with(store.Portfolio.report) | ||
1513 | file_report.assert_called_once_with(datetime.datetime(2018, 2, 25)) | ||
1514 | db_report.assert_called_once_with(datetime.datetime(2018, 2, 25)) | ||
1434 | 1515 | ||
1435 | def test_print_orders(self): | 1516 | def test_print_orders(self): |
1436 | m = market.Market(self.ccxt, self.market_args()) | 1517 | m = market.Market(self.ccxt, self.market_args()) |
@@ -3050,6 +3131,14 @@ class ReportStoreTest(WebMockTestCase): | |||
3050 | report_store.print_log(portfolio.Amount("BTC", 1)) | 3131 | report_store.print_log(portfolio.Amount("BTC", 1)) |
3051 | self.assertEqual(stdout_mock.getvalue(), "") | 3132 | self.assertEqual(stdout_mock.getvalue(), "") |
3052 | 3133 | ||
3134 | def test_default_json_serial(self): | ||
3135 | report_store = market.ReportStore(self.m) | ||
3136 | |||
3137 | self.assertEqual("2018-02-24T00:00:00", | ||
3138 | report_store.default_json_serial(portfolio.datetime(2018, 2, 24))) | ||
3139 | self.assertEqual("1.00000000 BTC", | ||
3140 | report_store.default_json_serial(portfolio.Amount("BTC", 1))) | ||
3141 | |||
3053 | def test_to_json(self): | 3142 | def test_to_json(self): |
3054 | report_store = market.ReportStore(self.m) | 3143 | report_store = market.ReportStore(self.m) |
3055 | report_store.logs.append({"foo": "bar"}) | 3144 | report_store.logs.append({"foo": "bar"}) |
@@ -3059,6 +3148,20 @@ class ReportStoreTest(WebMockTestCase): | |||
3059 | report_store.logs.append({"amount": portfolio.Amount("BTC", 1)}) | 3148 | report_store.logs.append({"amount": portfolio.Amount("BTC", 1)}) |
3060 | self.assertEqual('[\n {\n "foo": "bar"\n },\n {\n "date": "2018-02-24T00:00:00"\n },\n {\n "amount": "1.00000000 BTC"\n }\n]', report_store.to_json()) | 3149 | self.assertEqual('[\n {\n "foo": "bar"\n },\n {\n "date": "2018-02-24T00:00:00"\n },\n {\n "amount": "1.00000000 BTC"\n }\n]', report_store.to_json()) |
3061 | 3150 | ||
3151 | def test_to_json_array(self): | ||
3152 | report_store = market.ReportStore(self.m) | ||
3153 | report_store.logs.append({ | ||
3154 | "date": "date1", "type": "type1", "foo": "bar", "bla": "bla" | ||
3155 | }) | ||
3156 | report_store.logs.append({ | ||
3157 | "date": "date2", "type": "type2", "foo": "bar", "bla": "bla" | ||
3158 | }) | ||
3159 | logs = list(report_store.to_json_array()) | ||
3160 | |||
3161 | self.assertEqual(2, len(logs)) | ||
3162 | self.assertEqual(("date1", "type1", '{\n "foo": "bar",\n "bla": "bla"\n}'), logs[0]) | ||
3163 | self.assertEqual(("date2", "type2", '{\n "foo": "bar",\n "bla": "bla"\n}'), logs[1]) | ||
3164 | |||
3062 | @mock.patch.object(market.ReportStore, "print_log") | 3165 | @mock.patch.object(market.ReportStore, "print_log") |
3063 | @mock.patch.object(market.ReportStore, "add_log") | 3166 | @mock.patch.object(market.ReportStore, "add_log") |
3064 | def test_log_stage(self, add_log, print_log): | 3167 | def test_log_stage(self, add_log, print_log): |
@@ -3552,7 +3655,7 @@ class MainTest(WebMockTestCase): | |||
3552 | mock.patch("main.parse_config") as main_parse_config: | 3655 | mock.patch("main.parse_config") as main_parse_config: |
3553 | with self.subTest(debug=False): | 3656 | with self.subTest(debug=False): |
3554 | main_parse_config.return_value = ["pg_config", "report_path"] | 3657 | main_parse_config.return_value = ["pg_config", "report_path"] |
3555 | main_fetch_markets.return_value = [({"key": "market_config"},)] | 3658 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3)] |
3556 | m = main.get_user_market("config_path.ini", 1) | 3659 | m = main.get_user_market("config_path.ini", 1) |
3557 | 3660 | ||
3558 | self.assertIsInstance(m, market.Market) | 3661 | self.assertIsInstance(m, market.Market) |
@@ -3560,7 +3663,7 @@ class MainTest(WebMockTestCase): | |||
3560 | 3663 | ||
3561 | with self.subTest(debug=True): | 3664 | with self.subTest(debug=True): |
3562 | main_parse_config.return_value = ["pg_config", "report_path"] | 3665 | main_parse_config.return_value = ["pg_config", "report_path"] |
3563 | main_fetch_markets.return_value = [({"key": "market_config"},)] | 3666 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3)] |
3564 | m = main.get_user_market("config_path.ini", 1, debug=True) | 3667 | m = main.get_user_market("config_path.ini", 1, debug=True) |
3565 | 3668 | ||
3566 | self.assertIsInstance(m, market.Market) | 3669 | self.assertIsInstance(m, market.Market) |
@@ -3579,16 +3682,16 @@ class MainTest(WebMockTestCase): | |||
3579 | args_mock.after = "after" | 3682 | args_mock.after = "after" |
3580 | self.assertEqual("", stdout_mock.getvalue()) | 3683 | self.assertEqual("", stdout_mock.getvalue()) |
3581 | 3684 | ||
3582 | main.process("config", 1, "report_path", args_mock) | 3685 | main.process(3, "config", 1, "report_path", args_mock, "pg_config") |
3583 | 3686 | ||
3584 | market_mock.from_config.assert_has_calls([ | 3687 | market_mock.from_config.assert_has_calls([ |
3585 | mock.call("config", args_mock, user_id=1, report_path="report_path"), | 3688 | mock.call("config", args_mock, pg_config="pg_config", market_id=3, user_id=1, report_path="report_path"), |
3586 | mock.call().process("action", before="before", after="after"), | 3689 | mock.call().process("action", before="before", after="after"), |
3587 | ]) | 3690 | ]) |
3588 | 3691 | ||
3589 | with self.subTest(exception=True): | 3692 | with self.subTest(exception=True): |
3590 | market_mock.from_config.side_effect = Exception("boo") | 3693 | market_mock.from_config.side_effect = Exception("boo") |
3591 | main.process("config", 1, "report_path", args_mock) | 3694 | main.process(3, "config", 1, "report_path", args_mock, "pg_config") |
3592 | self.assertEqual("Exception: boo\n", stdout_mock.getvalue()) | 3695 | self.assertEqual("Exception: boo\n", stdout_mock.getvalue()) |
3593 | 3696 | ||
3594 | def test_main(self): | 3697 | def test_main(self): |
@@ -3606,7 +3709,7 @@ class MainTest(WebMockTestCase): | |||
3606 | 3709 | ||
3607 | parse_config.return_value = ["pg_config", "report_path"] | 3710 | parse_config.return_value = ["pg_config", "report_path"] |
3608 | 3711 | ||
3609 | fetch_markets.return_value = [["config1", 1], ["config2", 2]] | 3712 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] |
3610 | 3713 | ||
3611 | main.main(["Foo", "Bar"]) | 3714 | main.main(["Foo", "Bar"]) |
3612 | 3715 | ||
@@ -3616,8 +3719,8 @@ class MainTest(WebMockTestCase): | |||
3616 | 3719 | ||
3617 | self.assertEqual(2, process.call_count) | 3720 | self.assertEqual(2, process.call_count) |
3618 | process.assert_has_calls([ | 3721 | process.assert_has_calls([ |
3619 | mock.call("config1", 1, "report_path", args_mock), | 3722 | mock.call(3, "config1", 1, "report_path", args_mock, "pg_config"), |
3620 | mock.call("config2", 2, "report_path", args_mock), | 3723 | mock.call(1, "config2", 2, "report_path", args_mock, "pg_config"), |
3621 | ]) | 3724 | ]) |
3622 | with self.subTest(parallel=True): | 3725 | with self.subTest(parallel=True): |
3623 | with mock.patch("main.parse_args") as parse_args,\ | 3726 | with mock.patch("main.parse_args") as parse_args,\ |
@@ -3634,7 +3737,7 @@ class MainTest(WebMockTestCase): | |||
3634 | 3737 | ||
3635 | parse_config.return_value = ["pg_config", "report_path"] | 3738 | parse_config.return_value = ["pg_config", "report_path"] |
3636 | 3739 | ||
3637 | fetch_markets.return_value = [["config1", 1], ["config2", 2]] | 3740 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] |
3638 | 3741 | ||
3639 | main.main(["Foo", "Bar"]) | 3742 | main.main(["Foo", "Bar"]) |
3640 | 3743 | ||
@@ -3646,9 +3749,9 @@ class MainTest(WebMockTestCase): | |||
3646 | self.assertEqual(2, process.call_count) | 3749 | self.assertEqual(2, process.call_count) |
3647 | process.assert_has_calls([ | 3750 | process.assert_has_calls([ |
3648 | mock.call.__bool__(), | 3751 | mock.call.__bool__(), |
3649 | mock.call("config1", 1, "report_path", args_mock), | 3752 | mock.call(3, "config1", 1, "report_path", args_mock, "pg_config"), |
3650 | mock.call.__bool__(), | 3753 | mock.call.__bool__(), |
3651 | mock.call("config2", 2, "report_path", args_mock), | 3754 | mock.call(1, "config2", 2, "report_path", args_mock, "pg_config"), |
3652 | ]) | 3755 | ]) |
3653 | 3756 | ||
3654 | @mock.patch.object(main.sys, "exit") | 3757 | @mock.patch.object(main.sys, "exit") |
@@ -3734,7 +3837,7 @@ class MainTest(WebMockTestCase): | |||
3734 | rows = list(main.fetch_markets({"foo": "bar"}, None)) | 3837 | rows = list(main.fetch_markets({"foo": "bar"}, None)) |
3735 | 3838 | ||
3736 | psycopg2.connect.assert_called_once_with(foo="bar") | 3839 | psycopg2.connect.assert_called_once_with(foo="bar") |
3737 | cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs") | 3840 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id FROM market_configs") |
3738 | 3841 | ||
3739 | self.assertEqual(["row_1", "row_2"], rows) | 3842 | self.assertEqual(["row_1", "row_2"], rows) |
3740 | 3843 | ||
@@ -3744,7 +3847,7 @@ class MainTest(WebMockTestCase): | |||
3744 | rows = list(main.fetch_markets({"foo": "bar"}, 1)) | 3847 | rows = list(main.fetch_markets({"foo": "bar"}, 1)) |
3745 | 3848 | ||
3746 | psycopg2.connect.assert_called_once_with(foo="bar") | 3849 | psycopg2.connect.assert_called_once_with(foo="bar") |
3747 | cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs WHERE user_id = %s", 1) | 3850 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id FROM market_configs WHERE user_id = %s", 1) |
3748 | 3851 | ||
3749 | self.assertEqual(["row_1", "row_2"], rows) | 3852 | self.assertEqual(["row_1", "row_2"], rows) |
3750 | 3853 | ||