diff options
Diffstat (limited to 'tests/test_main.py')
-rw-r--r-- | tests/test_main.py | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..06fc84e --- /dev/null +++ b/tests/test_main.py | |||
@@ -0,0 +1,291 @@ | |||
1 | from .helper import * | ||
2 | import main, market | ||
3 | |||
4 | class MainTest(WebMockTestCase): | ||
5 | def test_make_order(self): | ||
6 | self.m.get_ticker.return_value = { | ||
7 | "inverted": False, | ||
8 | "average": D("0.1"), | ||
9 | "bid": D("0.09"), | ||
10 | "ask": D("0.11"), | ||
11 | } | ||
12 | |||
13 | with self.subTest(description="nominal case"): | ||
14 | main.make_order(self.m, 10, "ETH") | ||
15 | |||
16 | self.m.report.log_stage.assert_has_calls([ | ||
17 | mock.call("make_order_begin"), | ||
18 | mock.call("make_order_end"), | ||
19 | ]) | ||
20 | self.m.balances.fetch_balances.assert_has_calls([ | ||
21 | mock.call(tag="make_order_begin"), | ||
22 | mock.call(tag="make_order_end"), | ||
23 | ]) | ||
24 | self.m.trades.all.append.assert_called_once() | ||
25 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
26 | self.assertEqual(False, trade.orders[0].close_if_possible) | ||
27 | self.assertEqual(0, trade.value_from) | ||
28 | self.assertEqual("ETH", trade.currency) | ||
29 | self.assertEqual("BTC", trade.base_currency) | ||
30 | self.m.report.log_orders.assert_called_once_with([trade.orders[0]], None, "average") | ||
31 | self.m.trades.run_orders.assert_called_once_with() | ||
32 | self.m.follow_orders.assert_called_once_with() | ||
33 | |||
34 | order = trade.orders[0] | ||
35 | self.assertEqual(D("0.10"), order.rate) | ||
36 | |||
37 | self.m.reset_mock() | ||
38 | with self.subTest(compute_value="default"): | ||
39 | main.make_order(self.m, 10, "ETH", action="dispose", | ||
40 | compute_value="ask") | ||
41 | |||
42 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
43 | order = trade.orders[0] | ||
44 | self.assertEqual(D("0.11"), order.rate) | ||
45 | |||
46 | self.m.reset_mock() | ||
47 | with self.subTest(follow=False): | ||
48 | result = main.make_order(self.m, 10, "ETH", follow=False) | ||
49 | |||
50 | self.m.report.log_stage.assert_has_calls([ | ||
51 | mock.call("make_order_begin"), | ||
52 | mock.call("make_order_end_not_followed"), | ||
53 | ]) | ||
54 | self.m.balances.fetch_balances.assert_called_once_with(tag="make_order_begin") | ||
55 | |||
56 | self.m.trades.all.append.assert_called_once() | ||
57 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
58 | self.assertEqual(0, trade.value_from) | ||
59 | self.assertEqual("ETH", trade.currency) | ||
60 | self.assertEqual("BTC", trade.base_currency) | ||
61 | self.m.report.log_orders.assert_called_once_with([trade.orders[0]], None, "average") | ||
62 | self.m.trades.run_orders.assert_called_once_with() | ||
63 | self.m.follow_orders.assert_not_called() | ||
64 | self.assertEqual(trade.orders[0], result) | ||
65 | |||
66 | self.m.reset_mock() | ||
67 | with self.subTest(base_currency="USDT"): | ||
68 | main.make_order(self.m, 1, "BTC", base_currency="USDT") | ||
69 | |||
70 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
71 | self.assertEqual("BTC", trade.currency) | ||
72 | self.assertEqual("USDT", trade.base_currency) | ||
73 | |||
74 | self.m.reset_mock() | ||
75 | with self.subTest(close_if_possible=True): | ||
76 | main.make_order(self.m, 10, "ETH", close_if_possible=True) | ||
77 | |||
78 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
79 | self.assertEqual(True, trade.orders[0].close_if_possible) | ||
80 | |||
81 | self.m.reset_mock() | ||
82 | with self.subTest(action="dispose"): | ||
83 | main.make_order(self.m, 10, "ETH", action="dispose") | ||
84 | |||
85 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
86 | self.assertEqual(0, trade.value_to) | ||
87 | self.assertEqual(1, trade.value_from.value) | ||
88 | self.assertEqual("ETH", trade.currency) | ||
89 | self.assertEqual("BTC", trade.base_currency) | ||
90 | |||
91 | self.m.reset_mock() | ||
92 | with self.subTest(compute_value="default"): | ||
93 | main.make_order(self.m, 10, "ETH", action="dispose", | ||
94 | compute_value="bid") | ||
95 | |||
96 | trade = self.m.trades.all.append.mock_calls[0][1][0] | ||
97 | self.assertEqual(D("0.9"), trade.value_from.value) | ||
98 | |||
99 | def test_get_user_market(self): | ||
100 | with mock.patch("main.fetch_markets") as main_fetch_markets,\ | ||
101 | mock.patch("main.parse_args") as main_parse_args,\ | ||
102 | mock.patch("main.parse_config") as main_parse_config: | ||
103 | with self.subTest(debug=False): | ||
104 | main_parse_args.return_value = self.market_args() | ||
105 | main_parse_config.return_value = "pg_config" | ||
106 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3)] | ||
107 | m = main.get_user_market("config_path.ini", 1) | ||
108 | |||
109 | self.assertIsInstance(m, market.Market) | ||
110 | self.assertFalse(m.debug) | ||
111 | main_parse_args.assert_called_once_with(["--config", "config_path.ini"]) | ||
112 | |||
113 | main_parse_args.reset_mock() | ||
114 | with self.subTest(debug=True): | ||
115 | main_parse_args.return_value = self.market_args(debug=True) | ||
116 | main_parse_config.return_value = "pg_config" | ||
117 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3)] | ||
118 | m = main.get_user_market("config_path.ini", 1, debug=True) | ||
119 | |||
120 | self.assertIsInstance(m, market.Market) | ||
121 | self.assertTrue(m.debug) | ||
122 | main_parse_args.assert_called_once_with(["--config", "config_path.ini", "--debug"]) | ||
123 | |||
124 | def test_process(self): | ||
125 | with mock.patch("market.Market") as market_mock,\ | ||
126 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
127 | |||
128 | args_mock = mock.Mock() | ||
129 | args_mock.action = "action" | ||
130 | args_mock.config = "config" | ||
131 | args_mock.user = "user" | ||
132 | args_mock.debug = "debug" | ||
133 | args_mock.before = "before" | ||
134 | args_mock.after = "after" | ||
135 | self.assertEqual("", stdout_mock.getvalue()) | ||
136 | |||
137 | main.process("config", 3, 1, args_mock, "pg_config") | ||
138 | |||
139 | market_mock.from_config.assert_has_calls([ | ||
140 | mock.call("config", args_mock, pg_config="pg_config", market_id=3, user_id=1), | ||
141 | mock.call().process("action", before="before", after="after"), | ||
142 | ]) | ||
143 | |||
144 | with self.subTest(exception=True): | ||
145 | market_mock.from_config.side_effect = Exception("boo") | ||
146 | main.process(3, "config", 1, args_mock, "pg_config") | ||
147 | self.assertEqual("Exception: boo\n", stdout_mock.getvalue()) | ||
148 | |||
149 | def test_main(self): | ||
150 | with self.subTest(parallel=False): | ||
151 | with mock.patch("main.parse_args") as parse_args,\ | ||
152 | mock.patch("main.parse_config") as parse_config,\ | ||
153 | mock.patch("main.fetch_markets") as fetch_markets,\ | ||
154 | mock.patch("main.process") as process: | ||
155 | |||
156 | args_mock = mock.Mock() | ||
157 | args_mock.parallel = False | ||
158 | args_mock.user = "user" | ||
159 | parse_args.return_value = args_mock | ||
160 | |||
161 | parse_config.return_value = "pg_config" | ||
162 | |||
163 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] | ||
164 | |||
165 | main.main(["Foo", "Bar"]) | ||
166 | |||
167 | parse_args.assert_called_with(["Foo", "Bar"]) | ||
168 | parse_config.assert_called_with(args_mock) | ||
169 | fetch_markets.assert_called_with("pg_config", "user") | ||
170 | |||
171 | self.assertEqual(2, process.call_count) | ||
172 | process.assert_has_calls([ | ||
173 | mock.call("config1", 3, 1, args_mock, "pg_config"), | ||
174 | mock.call("config2", 1, 2, args_mock, "pg_config"), | ||
175 | ]) | ||
176 | with self.subTest(parallel=True): | ||
177 | with mock.patch("main.parse_args") as parse_args,\ | ||
178 | mock.patch("main.parse_config") as parse_config,\ | ||
179 | mock.patch("main.fetch_markets") as fetch_markets,\ | ||
180 | mock.patch("main.process") as process,\ | ||
181 | mock.patch("store.Portfolio.start_worker") as start,\ | ||
182 | mock.patch("store.Portfolio.stop_worker") as stop: | ||
183 | |||
184 | args_mock = mock.Mock() | ||
185 | args_mock.parallel = True | ||
186 | args_mock.user = "user" | ||
187 | parse_args.return_value = args_mock | ||
188 | |||
189 | parse_config.return_value = "pg_config" | ||
190 | |||
191 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] | ||
192 | |||
193 | main.main(["Foo", "Bar"]) | ||
194 | |||
195 | parse_args.assert_called_with(["Foo", "Bar"]) | ||
196 | parse_config.assert_called_with(args_mock) | ||
197 | fetch_markets.assert_called_with("pg_config", "user") | ||
198 | |||
199 | stop.assert_called_once_with() | ||
200 | start.assert_called_once_with() | ||
201 | self.assertEqual(2, process.call_count) | ||
202 | process.assert_has_calls([ | ||
203 | mock.call.__bool__(), | ||
204 | mock.call("config1", 3, 1, args_mock, "pg_config"), | ||
205 | mock.call.__bool__(), | ||
206 | mock.call("config2", 1, 2, args_mock, "pg_config"), | ||
207 | ]) | ||
208 | |||
209 | @mock.patch.object(main.sys, "exit") | ||
210 | @mock.patch("main.os") | ||
211 | def test_parse_config(self, os, exit): | ||
212 | with self.subTest(report_path=None): | ||
213 | args = main.configargparse.Namespace(**{ | ||
214 | "db_host": "host", | ||
215 | "db_port": "port", | ||
216 | "db_user": "user", | ||
217 | "db_password": "password", | ||
218 | "db_database": "database", | ||
219 | "report_path": None, | ||
220 | }) | ||
221 | |||
222 | result = main.parse_config(args) | ||
223 | self.assertEqual({ "host": "host", "port": "port", "user": | ||
224 | "user", "password": "password", "database": "database" | ||
225 | }, result) | ||
226 | with self.assertRaises(AttributeError): | ||
227 | args.db_password | ||
228 | |||
229 | with self.subTest(report_path="present"): | ||
230 | args = main.configargparse.Namespace(**{ | ||
231 | "db_host": "host", | ||
232 | "db_port": "port", | ||
233 | "db_user": "user", | ||
234 | "db_password": "password", | ||
235 | "db_database": "database", | ||
236 | "report_path": "report_path", | ||
237 | }) | ||
238 | |||
239 | os.path.exists.return_value = False | ||
240 | |||
241 | result = main.parse_config(args) | ||
242 | |||
243 | os.path.exists.assert_called_once_with("report_path") | ||
244 | os.makedirs.assert_called_once_with("report_path") | ||
245 | |||
246 | def test_parse_args(self): | ||
247 | with self.subTest(config="config.ini"): | ||
248 | args = main.parse_args([]) | ||
249 | self.assertEqual("config.ini", args.config) | ||
250 | self.assertFalse(args.before) | ||
251 | self.assertFalse(args.after) | ||
252 | self.assertFalse(args.debug) | ||
253 | |||
254 | args = main.parse_args(["--before", "--after", "--debug"]) | ||
255 | self.assertTrue(args.before) | ||
256 | self.assertTrue(args.after) | ||
257 | self.assertTrue(args.debug) | ||
258 | |||
259 | with self.subTest(config="inexistant"), \ | ||
260 | self.assertRaises(SystemExit), \ | ||
261 | mock.patch('sys.stderr', new_callable=StringIO) as stdout_mock: | ||
262 | args = main.parse_args(["--config", "foo.bar"]) | ||
263 | |||
264 | @mock.patch.object(main, "psycopg2") | ||
265 | def test_fetch_markets(self, psycopg2): | ||
266 | connect_mock = mock.Mock() | ||
267 | cursor_mock = mock.MagicMock() | ||
268 | cursor_mock.__iter__.return_value = ["row_1", "row_2"] | ||
269 | |||
270 | connect_mock.cursor.return_value = cursor_mock | ||
271 | psycopg2.connect.return_value = connect_mock | ||
272 | |||
273 | with self.subTest(user=None): | ||
274 | rows = list(main.fetch_markets({"foo": "bar"}, None)) | ||
275 | |||
276 | psycopg2.connect.assert_called_once_with(foo="bar") | ||
277 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id FROM market_configs") | ||
278 | |||
279 | self.assertEqual(["row_1", "row_2"], rows) | ||
280 | |||
281 | psycopg2.connect.reset_mock() | ||
282 | cursor_mock.execute.reset_mock() | ||
283 | with self.subTest(user=1): | ||
284 | rows = list(main.fetch_markets({"foo": "bar"}, 1)) | ||
285 | |||
286 | psycopg2.connect.assert_called_once_with(foo="bar") | ||
287 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id FROM market_configs WHERE user_id = %s", 1) | ||
288 | |||
289 | self.assertEqual(["row_1", "row_2"], rows) | ||
290 | |||
291 | |||