X-Git-Url: https://git.immae.eu/?p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git;a=blobdiff_plain;f=tests%2Ftest_market.py;h=93164803b2ed03ff2d7614762eab4249088af3d8;hp=e3482b8ad328c2bf917c73b5d77e6731bec6521d;hb=512972fa1df14df4e208a1182096b1c51b5d38d1;hpb=c5ca26b83ca9f120fb39f1e61265216342f8a4db diff --git a/tests/test_market.py b/tests/test_market.py index e3482b8..9316480 100644 --- a/tests/test_market.py +++ b/tests/test_market.py @@ -1,5 +1,5 @@ from .helper import * -import market, store, portfolio +import market, store, portfolio, dbs import datetime @unittest.skipUnless("unit" in limits, "Unit skipped") @@ -130,19 +130,20 @@ class MarketTest(WebMockTestCase): @mock.patch.object(market.Market, "get_ticker") @mock.patch.object(market.TradeStore, "compute_trades") def test_prepare_trades(self, compute_trades, get_ticker, repartition): - repartition.return_value = { - "XEM": (D("0.75"), "long"), - "BTC": (D("0.25"), "long"), - } - def _get_ticker(c1, c2): - if c1 == "USDT" and c2 == "BTC": - return { "average": D("0.0001") } - if c1 == "XVG" and c2 == "BTC": - return { "average": D("0.000001") } - self.fail("Should be called with {}, {}".format(c1, c2)) - get_ticker.side_effect = _get_ticker - - with mock.patch("market.ReportStore"): + with self.subTest(available_balance_only=False),\ + mock.patch("market.ReportStore"): + def _get_ticker(c1, c2): + if c1 == "USDT" and c2 == "BTC": + return { "average": D("0.0001") } + if c1 == "XVG" and c2 == "BTC": + return { "average": D("0.000001") } + self.fail("Should not be called with {}, {}".format(c1, c2)) + get_ticker.side_effect = _get_ticker + + repartition.return_value = { + "XEM": (D("0.75"), "long"), + "BTC": (D("0.25"), "long"), + } m = market.Market(self.ccxt, self.market_args()) self.ccxt.fetch_all_balances.return_value = { "USDT": { @@ -171,9 +172,119 @@ class MarketTest(WebMockTestCase): self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) m.report.log_stage.assert_called_once_with("prepare_trades", base_currency='BTC', compute_value='average', - liquidity='medium', only=None, repartition=None) - m.report.log_balances.assert_called_once_with(tag="tag") + available_balance_only=False, liquidity='medium', + only=None, repartition=None) + m.report.log_balances.assert_called_once_with(tag="tag", checkpoint=None) + + compute_trades.reset_mock() + with self.subTest(available_balance_only=True),\ + mock.patch("market.ReportStore"): + def _get_ticker(c1, c2): + if c1 == "ZRC" and c2 == "BTC": + return { "average": D("0.0001") } + if c1 == "DOGE" and c2 == "BTC": + return { "average": D("0.000001") } + if c1 == "ETH" and c2 == "BTC": + return { "average": D("0.1") } + if c1 == "FOO" and c2 == "BTC": + return { "average": D("0.1") } + self.fail("Should not be called with {}, {}".format(c1, c2)) + get_ticker.side_effect = _get_ticker + + repartition.return_value = { + "DOGE": (D("0.20"), "short"), + "BTC": (D("0.20"), "long"), + "ETH": (D("0.20"), "long"), + "XMR": (D("0.20"), "long"), + "FOO": (D("0.20"), "long"), + } + m = market.Market(self.ccxt, self.market_args()) + self.ccxt.fetch_all_balances.return_value = { + "ZRC": { + "exchange_free": D("2.0"), + "exchange_used": D("0.0"), + "exchange_total": D("2.0"), + "total": D("2.0") + }, + "DOGE": { + "exchange_free": D("5.0"), + "exchange_used": D("0.0"), + "exchange_total": D("5.0"), + "total": D("5.0") + }, + "BTC": { + "exchange_free": D("0.065"), + "exchange_used": D("0.02"), + "exchange_total": D("0.085"), + "margin_available": D("0.035"), + "margin_in_position": D("0.01"), + "margin_total": D("0.045"), + "total": D("0.13") + }, + "ETH": { + "exchange_free": D("1.0"), + "exchange_used": D("0.0"), + "exchange_total": D("1.0"), + "total": D("1.0") + }, + "FOO": { + "exchange_free": D("0.1"), + "exchange_used": D("0.0"), + "exchange_total": D("0.1"), + "total": D("0.1"), + }, + } + + m.balances.fetch_balances(tag="tag") + m.prepare_trades(available_balance_only=True) + compute_trades.assert_called_once() + + call = compute_trades.call_args[0] + values_in_base = call[0] + new_repartition = call[1] + + self.assertEqual(portfolio.Amount("BTC", "-0.025"), + new_repartition["DOGE"] - values_in_base["DOGE"]) + self.assertEqual(0, + new_repartition["ETH"] - values_in_base["ETH"]) + self.assertIsNone(new_repartition.get("ZRC")) + self.assertEqual(portfolio.Amount("BTC", "0.025"), + new_repartition["XMR"]) + self.assertEqual(portfolio.Amount("BTC", "0.015"), + new_repartition["FOO"] - values_in_base["FOO"]) + + compute_trades.reset_mock() + with self.subTest(available_balance_only=True, balance=0),\ + mock.patch("market.ReportStore"): + def _get_ticker(c1, c2): + if c1 == "ETH" and c2 == "BTC": + return { "average": D("0.1") } + self.fail("Should not be called with {}, {}".format(c1, c2)) + get_ticker.side_effect = _get_ticker + + repartition.return_value = { + "BTC": (D("0.5"), "long"), + "ETH": (D("0.5"), "long"), + } + m = market.Market(self.ccxt, self.market_args()) + self.ccxt.fetch_all_balances.return_value = { + "ETH": { + "exchange_free": D("1.0"), + "exchange_used": D("0.0"), + "exchange_total": D("1.0"), + "total": D("1.0") + }, + } + + m.balances.fetch_balances(tag="tag") + m.prepare_trades(available_balance_only=True) + compute_trades.assert_called_once() + call = compute_trades.call_args[0] + values_in_base = call[0] + new_repartition = call[1] + + self.assertEqual(new_repartition["ETH"], values_in_base["ETH"]) @mock.patch.object(market.time, "sleep") @mock.patch.object(market.TradeStore, "all_orders") @@ -280,12 +391,50 @@ class MarketTest(WebMockTestCase): order_mock2.trade = trade_mock trade_mock.tick_actions_recreate.return_value = "tick1" + new_order_mock = mock.Mock() + trade_mock.prepare_order.return_value = new_order_mock + + m.follow_orders() + + trade_mock.tick_actions_recreate.assert_called_once_with(2) + trade_mock.prepare_order.assert_called_once_with(compute_value="tick1") + m.report.log_error.assert_called_once_with("follow_orders", message=mock.ANY) + m.report.log_order.assert_called_with(order_mock2, 2, new_order=new_order_mock) + new_order_mock.run.assert_called_once_with() + + with self.subTest("disappearing order no action to do"), \ + mock.patch("market.ReportStore"): + all_orders.reset_mock() + m = market.Market(self.ccxt, self.market_args()) + + order_mock1 = mock.Mock() + order_mock2 = mock.Mock() + all_orders.side_effect = [ + [order_mock1, order_mock2], + [order_mock1, order_mock2], + + [order_mock1, order_mock2], + [order_mock1, order_mock2], + + [] + ] + + order_mock1.get_status.side_effect = ["open", "closed"] + order_mock2.get_status.side_effect = ["open", "error_disappeared"] + + order_mock1.trade = mock.Mock() + trade_mock = mock.Mock() + order_mock2.trade = trade_mock + + trade_mock.tick_actions_recreate.return_value = "tick1" + trade_mock.prepare_order.return_value = None m.follow_orders() trade_mock.tick_actions_recreate.assert_called_once_with(2) trade_mock.prepare_order.assert_called_once_with(compute_value="tick1") m.report.log_error.assert_called_once_with("follow_orders", message=mock.ANY) + m.report.log_order.assert_called_with(order_mock2, 2, finished=True) @mock.patch.object(market.BalanceStore, "fetch_balances") def test_move_balance(self, fetch_balances): @@ -494,13 +643,11 @@ class MarketTest(WebMockTestCase): self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") - @mock.patch.object(market, "psycopg2") - def test_store_database_report(self, psycopg2): - connect_mock = mock.Mock() + @mock.patch.object(dbs, "psql") + def test_store_database_report(self, psql): cursor_mock = mock.MagicMock() - connect_mock.cursor.return_value = cursor_mock - psycopg2.connect.return_value = connect_mock + psql.cursor.return_value = cursor_mock m = market.Market(self.ccxt, self.market_args(), pg_config={"config": "pg_config"}, user_id=1) cursor_mock.fetchone.return_value = [42] @@ -512,7 +659,7 @@ class MarketTest(WebMockTestCase): ("date2", "type2", "payload2"), ] m.store_database_report(datetime.datetime(2018, 3, 24)) - connect_mock.assert_has_calls([ + psql.assert_has_calls([ mock.call.cursor(), 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)), mock.call.cursor().fetchone(), @@ -520,21 +667,16 @@ class MarketTest(WebMockTestCase): mock.call.cursor().execute('INSERT INTO report_lines("date", "report_id", "type", "payload") VALUES (%s, %s, %s, %s);', ('date2', 42, 'type2', 'payload2')), mock.call.commit(), mock.call.cursor().close(), - mock.call.close() ]) - connect_mock.reset_mock() with self.subTest(error=True),\ mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: - psycopg2.connect.side_effect = Exception("Bouh") + psql.cursor.side_effect = Exception("Bouh") m.store_database_report(datetime.datetime(2018, 3, 24)) self.assertEqual(stdout_mock.getvalue(), "impossible to store report to database: Exception; Bouh\n") - @mock.patch.object(market, "redis") + @mock.patch.object(dbs, "redis") def test_store_redis_report(self, redis): - connect_mock = mock.Mock() - redis.Redis.return_value = connect_mock - m = market.Market(self.ccxt, self.market_args(), redis_config={"config": "redis_config"}, market_id=1) @@ -545,27 +687,32 @@ class MarketTest(WebMockTestCase): ("type2", "payload2"), ] m.store_redis_report(datetime.datetime(2018, 3, 24)) - connect_mock.assert_has_calls([ + redis.assert_has_calls([ mock.call.set("/cryptoportfolio/1/2018-03-24T00:00:00/type1", "payload1", ex=31*24*60*60), mock.call.set("/cryptoportfolio/1/latest/type1", "payload1"), mock.call.set("/cryptoportfolio/1/2018-03-24T00:00:00/type2", "payload2", ex=31*24*60*60), mock.call.set("/cryptoportfolio/1/latest/type2", "payload2"), + mock.call.set("/cryptoportfolio/1/latest/date", "2018-03-24T00:00:00"), ]) - connect_mock.reset_mock() + redis.reset_mock() with self.subTest(error=True),\ mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: - redis.Redis.side_effect = Exception("Bouh") + redis.set.side_effect = Exception("Bouh") m.store_redis_report(datetime.datetime(2018, 3, 24)) self.assertEqual(stdout_mock.getvalue(), "impossible to store report to redis: Exception; Bouh\n") def test_store_report(self): m = market.Market(self.ccxt, self.market_args(report_db=False), user_id=1) - with self.subTest(file=None, pg_config=None),\ + with self.subTest(file=None, pg_connected=None),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_file_report") as file_report: + psql.return_value = False + redis.return_value = False m.store_report() report.merge.assert_called_with(store.Portfolio.report) @@ -575,13 +722,16 @@ class MarketTest(WebMockTestCase): report.reset_mock() m = market.Market(self.ccxt, self.market_args(report_db=False, report_path="present"), user_id=1) - with self.subTest(file="present", pg_config=None),\ + with self.subTest(file="present", pg_connected=None),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = False + redis.return_value = False time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -593,13 +743,16 @@ class MarketTest(WebMockTestCase): report.reset_mock() m = market.Market(self.ccxt, self.market_args(report_db=True, report_path="present"), user_id=1) - with self.subTest(file="present", pg_config=None, report_db=True),\ + with self.subTest(file="present", pg_connected=None, report_db=True),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = False + redis.return_value = False time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -610,14 +763,17 @@ class MarketTest(WebMockTestCase): redis_report.assert_not_called() report.reset_mock() - m = market.Market(self.ccxt, self.market_args(report_db=True), pg_config="present", user_id=1) - with self.subTest(file=None, pg_config="present"),\ + m = market.Market(self.ccxt, self.market_args(report_db=True), user_id=1) + with self.subTest(file=None, pg_connected=True),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = True + redis.return_value = False time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -629,14 +785,17 @@ class MarketTest(WebMockTestCase): report.reset_mock() m = market.Market(self.ccxt, self.market_args(report_db=True, report_path="present"), - pg_config="pg_config", user_id=1) - with self.subTest(file="present", pg_config="present"),\ + user_id=1) + with self.subTest(file="present", pg_connected=True),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = True + redis.return_value = False time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -648,14 +807,17 @@ class MarketTest(WebMockTestCase): report.reset_mock() m = market.Market(self.ccxt, self.market_args(report_redis=False), - redis_config="redis_config", user_id=1) - with self.subTest(redis_config="present", report_redis=False),\ + user_id=1) + with self.subTest(redis_connected=True, report_redis=False),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = False + redis.return_value = True time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -664,13 +826,16 @@ class MarketTest(WebMockTestCase): report.reset_mock() m = market.Market(self.ccxt, self.market_args(report_redis=True), user_id=1) - with self.subTest(redis_config="absent", report_redis=True),\ + with self.subTest(redis_connected=False, report_redis=True),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = False + redis.return_value = False time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -678,14 +843,17 @@ class MarketTest(WebMockTestCase): report.reset_mock() m = market.Market(self.ccxt, self.market_args(report_redis=True), - redis_config="redis_config", user_id=1) - with self.subTest(redis_config="present", report_redis=True),\ + user_id=1) + with self.subTest(redis_connected=True, report_redis=True),\ + mock.patch.object(dbs, "psql_connected") as psql,\ + mock.patch.object(dbs, "redis_connected") as redis,\ mock.patch.object(m, "report") as report,\ mock.patch.object(m, "store_file_report") as file_report,\ mock.patch.object(m, "store_redis_report") as redis_report,\ mock.patch.object(m, "store_database_report") as db_report,\ mock.patch.object(market.datetime, "datetime") as time_mock: - + psql.return_value = False + redis.return_value = True time_mock.now.return_value = datetime.datetime(2018, 2, 25) m.store_report() @@ -715,7 +883,7 @@ class MarketTest(WebMockTestCase): @mock.patch("market.ReportStore.log_error") @mock.patch("market.Market.store_report") def test_process(self, store_report, log_error, process): - m = market.Market(self.ccxt, self.market_args()) + m = market.Market(self.ccxt, self.market_args(), options={"foo": "bar"}) with self.subTest(actions=[], before=False, after=False): m.process([]) @@ -729,7 +897,7 @@ class MarketTest(WebMockTestCase): with self.subTest(before=True, after=False): m.process(["foo"], before=True) - process.assert_called_once_with("foo", steps="before") + process.assert_called_once_with("foo", options={"foo": "bar"}, steps="before") store_report.assert_called_once() log_error.assert_not_called() @@ -739,7 +907,7 @@ class MarketTest(WebMockTestCase): with self.subTest(before=False, after=True): m.process(["sell_all"], after=True) - process.assert_called_once_with("sell_all", steps="after") + process.assert_called_once_with("sell_all", options={"foo": "bar"}, steps="after") store_report.assert_called_once() log_error.assert_not_called() @@ -749,7 +917,7 @@ class MarketTest(WebMockTestCase): with self.subTest(before=False, after=False): m.process(["foo"]) - process.assert_called_once_with("foo", steps="all") + process.assert_called_once_with("foo", options={"foo": "bar"}, steps="all") store_report.assert_called_once() log_error.assert_not_called() @@ -759,10 +927,21 @@ class MarketTest(WebMockTestCase): with self.subTest(before=True, after=True): m.process(["sell_all"], before=True, after=True) - process.assert_called_once_with("sell_all", steps="all") + process.assert_called_once_with("sell_all", options={"foo": "bar"}, steps="all") store_report.assert_called_once() log_error.assert_not_called() + process.reset_mock() + log_error.reset_mock() + store_report.reset_mock() + with self.subTest(authentication_error=True): + m.ccxt.check_required_credentials.side_effect = market.ccxt.AuthenticationError + + m.process(["some_action"], before=True) + log_error.assert_called_with("market_authentication", message="Impossible to authenticate to market") + store_report.assert_called_once() + + m.ccxt.check_required_credentials.side_effect = True process.reset_mock() log_error.reset_mock() store_report.reset_mock() @@ -770,7 +949,7 @@ class MarketTest(WebMockTestCase): process.side_effect = Exception("bouh") m.process(["some_action"], before=True) - log_error.assert_called_with("market_process", exception=mock.ANY) + log_error.assert_called_with("market_process", exception=mock.ANY, message=mock.ANY) store_report.assert_called_once() @@ -831,7 +1010,7 @@ class ProcessorTest(WebMockTestCase): with self.subTest("nominal case"): processor = market.Processor(self.m) - processor.process("sell_all", foo="bar") + processor.process("sell_all", options="bar") self.assertEqual(3, process_step.call_count) steps = list(map(lambda x: x[1][1]["name"], process_step.mock_calls)) @@ -839,12 +1018,12 @@ class ProcessorTest(WebMockTestCase): kwargs = list(map(lambda x: x[1][2], process_step.mock_calls)) self.assertEqual(["all_sell", "wait", "all_buy"], steps) self.assertEqual(["sell_all", "sell_all", "sell_all"], scenario_names) - self.assertEqual([{"foo":"bar"}, {"foo":"bar"}, {"foo":"bar"}], kwargs) + self.assertEqual(["bar", "bar", "bar"], kwargs) process_step.reset_mock() processor.process("sell_needed", steps=["before", "after"]) - self.assertEqual(3, process_step.call_count) + self.assertEqual(4, process_step.call_count) def test_method_arguments(self): ccxt = mock.Mock(spec=market.ccxt.poloniexE) @@ -854,11 +1033,11 @@ class ProcessorTest(WebMockTestCase): method, arguments = processor.method_arguments("wait_for_recent") self.assertEqual(market.Portfolio.wait_for_recent, method) - self.assertEqual(["delta", "poll"], arguments) + self.assertEqual(["delta"], arguments) method, arguments = processor.method_arguments("prepare_trades") self.assertEqual(m.prepare_trades, method) - self.assertEqual(['base_currency', 'liquidity', 'compute_value', 'repartition', 'only'], arguments) + self.assertEqual(['base_currency', 'liquidity', 'compute_value', 'repartition', 'only', 'available_balance_only'], arguments) method, arguments = processor.method_arguments("prepare_orders") self.assertEqual(m.trades.prepare_orders, method) @@ -878,31 +1057,32 @@ class ProcessorTest(WebMockTestCase): method, arguments = processor.method_arguments("print_tickers") self.assertEqual(m.print_tickers, method) + method, arguments = processor.method_arguments("fetch_balances") + self.assertEqual(m.balances.fetch_balances, method) + def test_process_step(self): processor = market.Processor(self.m) with mock.patch.object(processor, "run_action") as run_action: - step = processor.scenarios["sell_needed"][1] + step = processor.scenarios["sell_needed"][2] processor.process_step("foo", step, {"foo":"bar"}) self.m.report.log_stage.assert_has_calls([ - mock.call("process_foo__1_sell_begin"), - mock.call("process_foo__1_sell_end"), - ]) - self.m.balances.fetch_balances.assert_has_calls([ - mock.call(tag="process_foo__1_sell_begin"), - mock.call(tag="process_foo__1_sell_end"), + mock.call("process_foo__2_sell_begin"), + mock.call("process_foo__2_sell_end"), ]) - self.assertEqual(5, run_action.call_count) + self.assertEqual(7, run_action.call_count) run_action.assert_has_calls([ + mock.call('fetch_balances', {}, {'foo': 'bar', 'tag': 'process_foo__2_sell_begin'}), mock.call('prepare_trades', {}, {'foo': 'bar'}), mock.call('prepare_orders', {'only': 'dispose', 'compute_value': 'average'}, {'foo': 'bar'}), mock.call('run_orders', {}, {'foo': 'bar'}), mock.call('follow_orders', {}, {'foo': 'bar'}), mock.call('close_trades', {}, {'foo': 'bar'}), + mock.call('fetch_balances', {}, {'foo': 'bar', 'tag': 'process_foo__2_sell_end'}), ]) self.m.reset_mock() @@ -910,7 +1090,36 @@ class ProcessorTest(WebMockTestCase): step = processor.scenarios["sell_needed"][0] processor.process_step("foo", step, {"foo":"bar"}) - self.m.balances.fetch_balances.assert_not_called() + + self.m.report.log_stage.assert_has_calls([ + mock.call("process_foo__0_print_balances_begin"), + mock.call("process_foo__0_print_balances_end"), + ]) + + self.assertEqual(1, run_action.call_count) + run_action.assert_has_calls([ + mock.call('fetch_balances', + {'checkpoint': 'end', 'log_tickers': True, 'add_usdt': True, 'add_portfolio': True}, + {'foo': 'bar', 'tag': 'process_foo__0_print_balances_begin'}), + ]) + + self.m.reset_mock() + with mock.patch.object(processor, "run_action") as run_action: + step = processor.scenarios["sell_needed"][1] + + processor.process_step("foo", step, {"foo":"bar"}) + self.assertEqual(1, run_action.call_count) + + self.m.reset_mock() + with mock.patch.object(processor, "run_action") as run_action: + step = processor.scenarios["print_balances"][0] + + processor.process_step("foo", step, {"foo":"bar"}) + run_action.assert_has_calls([ + mock.call('fetch_balances', + {'log_tickers': True, 'add_usdt': True, 'add_portfolio': True}, + {'foo': 'bar', 'tag': 'process_foo__1_print_balances_begin'}), + ]) def test_parse_args(self): processor = market.Processor(self.m)