4 @unittest.skipUnless("unit" in limits
, "Unit skipped")
5 class MainTest(WebMockTestCase
):
6 def test_make_order(self
):
7 self
.m
.get_ticker
.return_value
= {
14 with self
.subTest(description
="nominal case"):
15 main
.make_order(self
.m
, 10, "ETH")
17 self
.m
.report
.log_stage
.assert_has_calls([
18 mock
.call("make_order_begin"),
19 mock
.call("make_order_end"),
21 self
.m
.balances
.fetch_balances
.assert_has_calls([
22 mock
.call(tag
="make_order_begin"),
23 mock
.call(tag
="make_order_end"),
25 self
.m
.trades
.all
.append
.assert_called_once()
26 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
27 self
.assertEqual(False, trade
.orders
[0].close_if_possible
)
28 self
.assertEqual(0, trade
.value_from
)
29 self
.assertEqual("ETH", trade
.currency
)
30 self
.assertEqual("BTC", trade
.base_currency
)
31 self
.m
.report
.log_orders
.assert_called_once_with([trade
.orders
[0]], None, "average")
32 self
.m
.trades
.run_orders
.assert_called_once_with()
33 self
.m
.follow_orders
.assert_called_once_with()
35 order
= trade
.orders
[0]
36 self
.assertEqual(D("0.10"), order
.rate
)
39 with self
.subTest(compute_value
="default"):
40 main
.make_order(self
.m
, 10, "ETH", action
="dispose",
43 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
44 order
= trade
.orders
[0]
45 self
.assertEqual(D("0.11"), order
.rate
)
48 with self
.subTest(follow
=False):
49 result
= main
.make_order(self
.m
, 10, "ETH", follow
=False)
51 self
.m
.report
.log_stage
.assert_has_calls([
52 mock
.call("make_order_begin"),
53 mock
.call("make_order_end_not_followed"),
55 self
.m
.balances
.fetch_balances
.assert_called_once_with(tag
="make_order_begin")
57 self
.m
.trades
.all
.append
.assert_called_once()
58 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
59 self
.assertEqual(0, trade
.value_from
)
60 self
.assertEqual("ETH", trade
.currency
)
61 self
.assertEqual("BTC", trade
.base_currency
)
62 self
.m
.report
.log_orders
.assert_called_once_with([trade
.orders
[0]], None, "average")
63 self
.m
.trades
.run_orders
.assert_called_once_with()
64 self
.m
.follow_orders
.assert_not_called()
65 self
.assertEqual(trade
.orders
[0], result
)
68 with self
.subTest(base_currency
="USDT"):
69 main
.make_order(self
.m
, 1, "BTC", base_currency
="USDT")
71 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
72 self
.assertEqual("BTC", trade
.currency
)
73 self
.assertEqual("USDT", trade
.base_currency
)
76 with self
.subTest(close_if_possible
=True):
77 main
.make_order(self
.m
, 10, "ETH", close_if_possible
=True)
79 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
80 self
.assertEqual(True, trade
.orders
[0].close_if_possible
)
83 with self
.subTest(action
="dispose"):
84 main
.make_order(self
.m
, 10, "ETH", action
="dispose")
86 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
87 self
.assertEqual(0, trade
.value_to
)
88 self
.assertEqual(1, trade
.value_from
.value
)
89 self
.assertEqual("ETH", trade
.currency
)
90 self
.assertEqual("BTC", trade
.base_currency
)
93 with self
.subTest(compute_value
="default"):
94 main
.make_order(self
.m
, 10, "ETH", action
="dispose",
97 trade
= self
.m
.trades
.all
.append
.mock_calls
[0][1][0]
98 self
.assertEqual(D("0.9"), trade
.value_from
.value
)
100 def test_get_user_market(self
):
101 with mock
.patch("main.fetch_markets") as main_fetch_markets
,\
102 mock
.patch("main.parse_args") as main_parse_args
,\
103 mock
.patch("main.parse_config") as main_parse_config
:
104 with self
.subTest(debug
=False):
105 main_parse_args
.return_value
= self
.market_args()
106 main_parse_config
.return_value
= "pg_config"
107 main_fetch_markets
.return_value
= [(1, {"key": "market_config"}
, 3)]
108 m
= main
.get_user_market("config_path.ini", 1)
110 self
.assertIsInstance(m
, market
.Market
)
111 self
.assertFalse(m
.debug
)
112 main_parse_args
.assert_called_once_with(["--config", "config_path.ini"])
114 main_parse_args
.reset_mock()
115 with self
.subTest(debug
=True):
116 main_parse_args
.return_value
= self
.market_args(debug
=True)
117 main_parse_config
.return_value
= "pg_config"
118 main_fetch_markets
.return_value
= [(1, {"key": "market_config"}
, 3)]
119 m
= main
.get_user_market("config_path.ini", 1, debug
=True)
121 self
.assertIsInstance(m
, market
.Market
)
122 self
.assertTrue(m
.debug
)
123 main_parse_args
.assert_called_once_with(["--config", "config_path.ini", "--debug"])
125 def test_process(self
):
126 with mock
.patch("market.Market") as market_mock
,\
127 mock
.patch('sys.stdout', new_callable
=StringIO
) as stdout_mock
:
129 args_mock
= mock
.Mock()
130 args_mock
.action
= "action"
131 args_mock
.config
= "config"
132 args_mock
.user
= "user"
133 args_mock
.debug
= "debug"
134 args_mock
.before
= "before"
135 args_mock
.after
= "after"
136 self
.assertEqual("", stdout_mock
.getvalue())
138 main
.process("config", 3, 1, args_mock
, "pg_config", "redis_config")
140 market_mock
.from_config
.assert_has_calls([
141 mock
.call("config", args_mock
, pg_config
="pg_config", redis_config
="redis_config", market_id
=3, user_id
=1),
142 mock
.call().process("action", before
="before", after
="after"),
145 with self
.subTest(exception
=True):
146 market_mock
.from_config
.side_effect
= Exception("boo")
147 main
.process(3, "config", 1, args_mock
, "pg_config", "redis_config")
148 self
.assertEqual("Exception: boo\n", stdout_mock
.getvalue())
151 with self
.subTest(parallel
=False):
152 with mock
.patch("main.parse_args") as parse_args
,\
153 mock
.patch("main.parse_config") as parse_config
,\
154 mock
.patch("main.fetch_markets") as fetch_markets
,\
155 mock
.patch("main.process") as process
:
157 args_mock
= mock
.Mock()
158 args_mock
.parallel
= False
159 args_mock
.user
= "user"
160 parse_args
.return_value
= args_mock
162 parse_config
.return_value
= ["pg_config", "redis_config"]
164 fetch_markets
.return_value
= [[3, "config1", 1], [1, "config2", 2]]
166 main
.main(["Foo", "Bar"])
168 parse_args
.assert_called_with(["Foo", "Bar"])
169 parse_config
.assert_called_with(args_mock
)
170 fetch_markets
.assert_called_with("pg_config", "user")
172 self
.assertEqual(2, process
.call_count
)
173 process
.assert_has_calls([
174 mock
.call("config1", 3, 1, args_mock
, "pg_config", "redis_config"),
175 mock
.call("config2", 1, 2, args_mock
, "pg_config", "redis_config"),
177 with self
.subTest(parallel
=True):
178 with mock
.patch("main.parse_args") as parse_args
,\
179 mock
.patch("main.parse_config") as parse_config
,\
180 mock
.patch("main.fetch_markets") as fetch_markets
,\
181 mock
.patch("main.process") as process
,\
182 mock
.patch("store.Portfolio.start_worker") as start
,\
183 mock
.patch("store.Portfolio.stop_worker") as stop
:
185 args_mock
= mock
.Mock()
186 args_mock
.parallel
= True
187 args_mock
.user
= "user"
188 parse_args
.return_value
= args_mock
190 parse_config
.return_value
= ["pg_config", "redis_config"]
192 fetch_markets
.return_value
= [[3, "config1", 1], [1, "config2", 2]]
194 main
.main(["Foo", "Bar"])
196 parse_args
.assert_called_with(["Foo", "Bar"])
197 parse_config
.assert_called_with(args_mock
)
198 fetch_markets
.assert_called_with("pg_config", "user")
200 stop
.assert_called_once_with()
201 start
.assert_called_once_with()
202 self
.assertEqual(2, process
.call_count
)
203 process
.assert_has_calls([
204 mock
.call
.__bool
__(),
205 mock
.call("config1", 3, 1, args_mock
, "pg_config", "redis_config"),
206 mock
.call
.__bool
__(),
207 mock
.call("config2", 1, 2, args_mock
, "pg_config", "redis_config"),
209 with self
.subTest(quiet
=True):
210 with mock
.patch("main.parse_args") as parse_args
,\
211 mock
.patch("main.parse_config") as parse_config
,\
212 mock
.patch("main.fetch_markets") as fetch_markets
,\
213 mock
.patch("store.Portfolio.report") as report
,\
214 mock
.patch("main.process") as process
:
216 args_mock
= mock
.Mock()
217 args_mock
.parallel
= False
218 args_mock
.quiet
= True
219 args_mock
.user
= "user"
220 parse_args
.return_value
= args_mock
222 parse_config
.return_value
= ["pg_config", "redis_config"]
224 fetch_markets
.return_value
= [[3, "config1", 1], [1, "config2", 2]]
226 main
.main(["Foo", "Bar"])
228 report
.set_verbose
.assert_called_once_with(False)
230 with self
.subTest(quiet
=False):
231 with mock
.patch("main.parse_args") as parse_args
,\
232 mock
.patch("main.parse_config") as parse_config
,\
233 mock
.patch("main.fetch_markets") as fetch_markets
,\
234 mock
.patch("store.Portfolio.report") as report
,\
235 mock
.patch("main.process") as process
:
237 args_mock
= mock
.Mock()
238 args_mock
.parallel
= False
239 args_mock
.quiet
= False
240 args_mock
.user
= "user"
241 parse_args
.return_value
= args_mock
243 parse_config
.return_value
= ["pg_config", "redis_config"]
245 fetch_markets
.return_value
= [[3, "config1", 1], [1, "config2", 2]]
247 main
.main(["Foo", "Bar"])
249 report
.set_verbose
.assert_called_once_with(True)
252 @mock.patch.object(main
.sys
, "exit")
253 @mock.patch("main.os")
254 def test_parse_config(self
, os
, exit
):
255 with self
.subTest(report_path
=None):
256 args
= main
.configargparse
.Namespace(**{
260 "db_password": "password",
261 "db_database": "database",
262 "redis_host": "rhost",
263 "redis_port": "rport",
264 "redis_database": "rdb",
268 db_config
, redis_config
= main
.parse_config(args
)
269 self
.assertEqual({ "host": "host", "port": "port", "user":
270 "user", "password": "password", "database": "database"
272 self
.assertEqual({ "host": "rhost", "port": "rport", "db":
273 "rdb"}, redis_config
)
275 with self
.assertRaises(AttributeError):
277 with self
.assertRaises(AttributeError):
280 with self
.subTest(redis_host
="socket"):
281 args
= main
.configargparse
.Namespace(**{
285 "db_password": "password",
286 "db_database": "database",
287 "redis_host": "/run/foo",
288 "redis_port": "rport",
289 "redis_database": "rdb",
293 db_config
, redis_config
= main
.parse_config(args
)
294 self
.assertEqual({ "unix_socket_path": "/run/foo", "db": "rdb"}
, redis_config
)
296 with self
.subTest(report_path
="present"):
297 args
= main
.configargparse
.Namespace(**{
301 "db_password": "password",
302 "db_database": "database",
303 "redis_host": "rhost",
304 "redis_port": "rport",
305 "redis_database": "rdb",
306 "report_path": "report_path",
309 os
.path
.exists
.return_value
= False
311 result
= main
.parse_config(args
)
313 os
.path
.exists
.assert_called_once_with("report_path")
314 os
.makedirs
.assert_called_once_with("report_path")
316 def test_parse_args(self
):
317 with self
.subTest(config
="config.ini"):
318 args
= main
.parse_args([])
319 self
.assertEqual("config.ini", args
.config
)
320 self
.assertFalse(args
.before
)
321 self
.assertFalse(args
.after
)
322 self
.assertFalse(args
.debug
)
324 args
= main
.parse_args(["--before", "--after", "--debug"])
325 self
.assertTrue(args
.before
)
326 self
.assertTrue(args
.after
)
327 self
.assertTrue(args
.debug
)
329 with self
.subTest(config
="inexistant"), \
330 self
.assertRaises(SystemExit), \
331 mock
.patch('sys.stderr', new_callable
=StringIO
) as stdout_mock
:
332 args
= main
.parse_args(["--config", "foo.bar"])
334 @mock.patch.object(main
, "psycopg2")
335 def test_fetch_markets(self
, psycopg2
):
336 connect_mock
= mock
.Mock()
337 cursor_mock
= mock
.MagicMock()
338 cursor_mock
.__iter
__.return_value
= ["row_1", "row_2"]
340 connect_mock
.cursor
.return_value
= cursor_mock
341 psycopg2
.connect
.return_value
= connect_mock
343 with self
.subTest(user
=None):
344 rows
= list(main
.fetch_markets({"foo": "bar"}
, None))
346 psycopg2
.connect
.assert_called_once_with(foo
="bar")
347 cursor_mock
.execute
.assert_called_once_with("SELECT id,config,user_id FROM market_configs")
349 self
.assertEqual(["row_1", "row_2"], rows
)
351 psycopg2
.connect
.reset_mock()
352 cursor_mock
.execute
.reset_mock()
353 with self
.subTest(user
=1):
354 rows
= list(main
.fetch_markets({"foo": "bar"}
, 1))
356 psycopg2
.connect
.assert_called_once_with(foo
="bar")
357 cursor_mock
.execute
.assert_called_once_with("SELECT id,config,user_id FROM market_configs WHERE user_id = %s", 1)
359 self
.assertEqual(["row_1", "row_2"], rows
)