diff options
-rw-r--r-- | test.py | 17 | ||||
-rw-r--r-- | tests/acceptance.py (renamed from test_acceptance.py) | 119 | ||||
-rw-r--r-- | tests/helper.py | 12 | ||||
-rw-r--r-- | tests/test_acceptance.py | 25 | ||||
-rw-r--r-- | tests/test_ccxt_wrapper.py | 3 | ||||
-rw-r--r-- | tests/test_main.py | 1 | ||||
-rw-r--r-- | tests/test_market.py | 2 | ||||
-rw-r--r-- | tests/test_portfolio.py | 6 | ||||
-rw-r--r-- | tests/test_store.py | 6 |
9 files changed, 123 insertions, 68 deletions
@@ -1,10 +1,17 @@ | |||
1 | import unittest | 1 | import unittest |
2 | from tests.acceptance import TimeMock | ||
2 | 3 | ||
3 | from tests.test_ccxt_wrapper import * | 4 | from tests.helper import limits |
4 | from tests.test_main import * | 5 | |
5 | from tests.test_market import * | 6 | if "unit" in limits: |
6 | from tests.test_store import * | 7 | from tests.test_ccxt_wrapper import * |
7 | from tests.test_portfolio import * | 8 | from tests.test_main import * |
9 | from tests.test_market import * | ||
10 | from tests.test_store import * | ||
11 | from tests.test_portfolio import * | ||
12 | |||
13 | if "acceptance" in limits: | ||
14 | from tests.test_acceptance import * | ||
8 | 15 | ||
9 | if __name__ == '__main__': | 16 | if __name__ == '__main__': |
10 | unittest.main() | 17 | unittest.main() |
diff --git a/test_acceptance.py b/tests/acceptance.py index 3633928..66014ca 100644 --- a/test_acceptance.py +++ b/tests/acceptance.py | |||
@@ -2,7 +2,6 @@ import requests | |||
2 | import requests_mock | 2 | import requests_mock |
3 | import sys, os | 3 | import sys, os |
4 | import time, datetime | 4 | import time, datetime |
5 | import unittest | ||
6 | from unittest import mock | 5 | from unittest import mock |
7 | from ssl import SSLError | 6 | from ssl import SSLError |
8 | from decimal import Decimal | 7 | from decimal import Decimal |
@@ -11,8 +10,56 @@ import psycopg2 | |||
11 | from io import StringIO | 10 | from io import StringIO |
12 | import re | 11 | import re |
13 | import functools | 12 | import functools |
14 | import glob | 13 | import threading |
15 | 14 | ||
15 | class TimeMock: | ||
16 | delta = {} | ||
17 | delta_init = 0 | ||
18 | true_time = time.time | ||
19 | true_sleep = time.sleep | ||
20 | time_patch = None | ||
21 | datetime_patch = None | ||
22 | |||
23 | @classmethod | ||
24 | def travel(cls, start_date): | ||
25 | cls.delta = {} | ||
26 | cls.delta_init = (datetime.datetime.now() - start_date).total_seconds() | ||
27 | |||
28 | @classmethod | ||
29 | def start(cls): | ||
30 | cls.delta = {} | ||
31 | cls.delta_init = 0 | ||
32 | |||
33 | class fake_datetime(datetime.datetime): | ||
34 | @classmethod | ||
35 | def now(cls, tz=None): | ||
36 | if tz is None: | ||
37 | return cls.fromtimestamp(time.time()) | ||
38 | else: | ||
39 | return tz.fromutc(cls.utcfromtimestamp(time.time()).replace(tzinfo=tz)) | ||
40 | |||
41 | cls.time_patch = mock.patch.multiple(time, time=cls.fake_time, sleep=cls.fake_sleep) | ||
42 | cls.datetime_patch = mock.patch.multiple(datetime, datetime=fake_datetime) | ||
43 | cls.time_patch.start() | ||
44 | cls.datetime_patch.start() | ||
45 | |||
46 | @classmethod | ||
47 | def stop(cls): | ||
48 | cls.delta = {} | ||
49 | cls.delta_init = 0 | ||
50 | |||
51 | @classmethod | ||
52 | def fake_time(cls): | ||
53 | cls.delta.setdefault(threading.current_thread(), cls.delta_init) | ||
54 | return cls.true_time() - cls.delta[threading.current_thread()] | ||
55 | |||
56 | @classmethod | ||
57 | def fake_sleep(cls, duration): | ||
58 | cls.delta.setdefault(threading.current_thread(), cls.delta_init) | ||
59 | cls.delta[threading.current_thread()] -= float(duration) | ||
60 | cls.true_sleep(min(float(duration), 0.1)) | ||
61 | |||
62 | TimeMock.start() | ||
16 | import main | 63 | import main |
17 | 64 | ||
18 | class FileMock: | 65 | class FileMock: |
@@ -51,7 +98,6 @@ class FileMock: | |||
51 | if not line.startswith("[Worker] "): | 98 | if not line.startswith("[Worker] "): |
52 | self.tester.fail("« {} » not found in log file {}".format(line, split_logs)) | 99 | self.tester.fail("« {} » not found in log file {}".format(line, split_logs)) |
53 | # Le fichier de log est écrit | 100 | # Le fichier de log est écrit |
54 | # Le fichier de log est printed uniquement si non quiet | ||
55 | # Le rapport est écrit si pertinent | 101 | # Le rapport est écrit si pertinent |
56 | # Le rapport contient le bon nombre de lignes | 102 | # Le rapport contient le bon nombre de lignes |
57 | 103 | ||
@@ -142,11 +188,16 @@ class RequestsMock: | |||
142 | def stop(self): | 188 | def stop(self): |
143 | self.mocker.stop() | 189 | self.mocker.stop() |
144 | 190 | ||
191 | lazy_calls = [ | ||
192 | "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json", | ||
193 | "https://poloniex.com/public?command=returnTicker", | ||
194 | ] | ||
145 | def check_calls(self): | 195 | def check_calls(self): |
146 | self.tester.assertEqual([], self.error_calls) | 196 | self.tester.assertEqual([], self.error_calls) |
147 | for (method, url), elements in self.mocks.items(): | 197 | for (method, url), elements in self.mocks.items(): |
148 | for market_id, element in elements.items(): | 198 | for market_id, element in elements.items(): |
149 | self.tester.assertEqual(0, len(element), "Missing calls to {} {}, market_id {}".format(method, url, market_id)) | 199 | if url not in self.lazy_calls: |
200 | self.tester.assertEqual(0, len(element), "Missing calls to {} {}, market_id {}".format(method, url, market_id)) | ||
150 | 201 | ||
151 | def clean_body(self, body): | 202 | def clean_body(self, body): |
152 | if body is None: | 203 | if body is None: |
@@ -168,6 +219,8 @@ def callback(self, elements, request, context): | |||
168 | elif element["response"] is not None: | 219 | elif element["response"] is not None: |
169 | self.last_https[element["date"]] = element["response"] | 220 | self.last_https[element["date"]] = element["response"] |
170 | 221 | ||
222 | time.sleep(element.get("duration", 0)) | ||
223 | |||
171 | assert self.clean_body(request.body) == \ | 224 | assert self.clean_body(request.body) == \ |
172 | self.clean_body(element["body"]), "Body does not match" | 225 | self.clean_body(element["body"]), "Body does not match" |
173 | context.status_code = element["status"] | 226 | context.status_code = element["status"] |
@@ -202,45 +255,6 @@ class GlobalVariablesMock: | |||
202 | pass | 255 | pass |
203 | 256 | ||
204 | 257 | ||
205 | class TimeMock: | ||
206 | delta = 0 | ||
207 | true_time = time.time | ||
208 | true_sleep = time.sleep | ||
209 | time_patch = None | ||
210 | datetime_patch = None | ||
211 | |||
212 | @classmethod | ||
213 | def start(cls, start_date): | ||
214 | cls.delta = (datetime.datetime.now() - start_date).total_seconds() | ||
215 | |||
216 | class fake_datetime(datetime.datetime): | ||
217 | @classmethod | ||
218 | def now(cls, tz=None): | ||
219 | if tz is None: | ||
220 | return cls.fromtimestamp(time.time()) | ||
221 | else: | ||
222 | return tz.fromutc(cls.utcfromtimestamp(time.time()).replace(tzinfo=tz)) | ||
223 | |||
224 | cls.time_patch = mock.patch.multiple(time, time=cls.fake_time, sleep=cls.fake_sleep) | ||
225 | cls.datetime_patch = mock.patch.multiple(datetime, datetime=fake_datetime) | ||
226 | cls.time_patch.start() | ||
227 | cls.datetime_patch.start() | ||
228 | |||
229 | @classmethod | ||
230 | def stop(cls): | ||
231 | cls.delta = 0 | ||
232 | cls.datetime_patch.stop() | ||
233 | cls.time_patch.stop() | ||
234 | |||
235 | @classmethod | ||
236 | def fake_time(cls): | ||
237 | return cls.true_time() - cls.delta | ||
238 | |||
239 | @classmethod | ||
240 | def fake_sleep(cls, duration): | ||
241 | cls.delta -= duration | ||
242 | cls.true_sleep(0.2) | ||
243 | |||
244 | class AcceptanceTestCase(): | 258 | class AcceptanceTestCase(): |
245 | def parse_file(self, report_file): | 259 | def parse_file(self, report_file): |
246 | with open(report_file, "rb") as f: | 260 | with open(report_file, "rb") as f: |
@@ -327,7 +341,7 @@ class AcceptanceTestCase(): | |||
327 | self.requests_mock.start() | 341 | self.requests_mock.start() |
328 | self.file_mock.start() | 342 | self.file_mock.start() |
329 | self.global_variables_mock.start() | 343 | self.global_variables_mock.start() |
330 | TimeMock.start(self.start_date) | 344 | TimeMock.travel(self.start_date) |
331 | 345 | ||
332 | def base_test(self): | 346 | def base_test(self): |
333 | main.main(self.config) | 347 | main.main(self.config) |
@@ -342,20 +356,3 @@ class AcceptanceTestCase(): | |||
342 | self.requests_mock.stop() | 356 | self.requests_mock.stop() |
343 | self.database_mock.stop() | 357 | self.database_mock.stop() |
344 | 358 | ||
345 | for dirfile in glob.glob("tests/acceptance/**/*/", recursive=True): | ||
346 | json_files = glob.glob("{}/*.json".format(dirfile)) | ||
347 | log_files = glob.glob("{}/*.log".format(dirfile)) | ||
348 | if len(json_files) > 0: | ||
349 | name = dirfile.replace("tests/acceptance/", "").replace("/", "_")[0:-1] | ||
350 | cname = "".join(list(map(lambda x: x.capitalize(), name.split("_")))) | ||
351 | |||
352 | globals()[cname] = type(cname, | ||
353 | (AcceptanceTestCase,unittest.TestCase), { | ||
354 | "log_files": log_files, | ||
355 | "files": json_files, | ||
356 | "test_{}".format(name): AcceptanceTestCase.base_test | ||
357 | }) | ||
358 | |||
359 | if __name__ == '__main__': | ||
360 | unittest.main() | ||
361 | |||
diff --git a/tests/helper.py b/tests/helper.py index 4548b16..b85bf3a 100644 --- a/tests/helper.py +++ b/tests/helper.py | |||
@@ -6,9 +6,19 @@ import requests_mock | |||
6 | from io import StringIO | 6 | from io import StringIO |
7 | import portfolio, market, main, store | 7 | import portfolio, market, main, store |
8 | 8 | ||
9 | __all__ = ["unittest", "WebMockTestCase", "mock", "D", | 9 | __all__ = ["limits", "unittest", "WebMockTestCase", "mock", "D", |
10 | "StringIO"] | 10 | "StringIO"] |
11 | 11 | ||
12 | limits = ["acceptance", "unit"] | ||
13 | for test_type in limits: | ||
14 | if "--no{}".format(test_type) in sys.argv: | ||
15 | sys.argv.remove("--no{}".format(test_type)) | ||
16 | limits.remove(test_type) | ||
17 | if "--only{}".format(test_type) in sys.argv: | ||
18 | sys.argv.remove("--only{}".format(test_type)) | ||
19 | limits = [test_type] | ||
20 | break | ||
21 | |||
12 | class WebMockTestCase(unittest.TestCase): | 22 | class WebMockTestCase(unittest.TestCase): |
13 | import time | 23 | import time |
14 | 24 | ||
diff --git a/tests/test_acceptance.py b/tests/test_acceptance.py new file mode 100644 index 0000000..77a6cca --- /dev/null +++ b/tests/test_acceptance.py | |||
@@ -0,0 +1,25 @@ | |||
1 | from .helper import limits | ||
2 | from tests.acceptance import AcceptanceTestCase | ||
3 | |||
4 | import unittest | ||
5 | import glob | ||
6 | |||
7 | __all__ = [] | ||
8 | |||
9 | for dirfile in glob.glob("tests/acceptance/**/*/", recursive=True): | ||
10 | json_files = glob.glob("{}/*.json".format(dirfile)) | ||
11 | log_files = glob.glob("{}/*.log".format(dirfile)) | ||
12 | if len(json_files) > 0: | ||
13 | name = dirfile.replace("tests/acceptance/", "").replace("/", "_")[0:-1] | ||
14 | cname = "".join(list(map(lambda x: x.capitalize(), name.split("_")))) | ||
15 | |||
16 | globals()[cname] = unittest.skipUnless("acceptance" in limits, "Acceptance skipped")( | ||
17 | type(cname, (AcceptanceTestCase, unittest.TestCase), { | ||
18 | "log_files": log_files, | ||
19 | "files": json_files, | ||
20 | "test_{}".format(name): AcceptanceTestCase.base_test | ||
21 | }) | ||
22 | ) | ||
23 | __all__.append(cname) | ||
24 | |||
25 | |||
diff --git a/tests/test_ccxt_wrapper.py b/tests/test_ccxt_wrapper.py index 18feab3..10e334d 100644 --- a/tests/test_ccxt_wrapper.py +++ b/tests/test_ccxt_wrapper.py | |||
@@ -1,7 +1,8 @@ | |||
1 | from .helper import unittest, mock, D | 1 | from .helper import limits, unittest, mock, D |
2 | import requests_mock | 2 | import requests_mock |
3 | import market | 3 | import market |
4 | 4 | ||
5 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
5 | class poloniexETest(unittest.TestCase): | 6 | class poloniexETest(unittest.TestCase): |
6 | def setUp(self): | 7 | def setUp(self): |
7 | super().setUp() | 8 | super().setUp() |
diff --git a/tests/test_main.py b/tests/test_main.py index cee89ce..d2f8029 100644 --- a/tests/test_main.py +++ b/tests/test_main.py | |||
@@ -1,6 +1,7 @@ | |||
1 | from .helper import * | 1 | from .helper import * |
2 | import main, market | 2 | import main, market |
3 | 3 | ||
4 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
4 | class MainTest(WebMockTestCase): | 5 | class MainTest(WebMockTestCase): |
5 | def test_make_order(self): | 6 | def test_make_order(self): |
6 | self.m.get_ticker.return_value = { | 7 | self.m.get_ticker.return_value = { |
diff --git a/tests/test_market.py b/tests/test_market.py index fd23162..14b23b5 100644 --- a/tests/test_market.py +++ b/tests/test_market.py | |||
@@ -2,6 +2,7 @@ from .helper import * | |||
2 | import market, store, portfolio | 2 | import market, store, portfolio |
3 | import datetime | 3 | import datetime |
4 | 4 | ||
5 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
5 | class MarketTest(WebMockTestCase): | 6 | class MarketTest(WebMockTestCase): |
6 | def setUp(self): | 7 | def setUp(self): |
7 | super().setUp() | 8 | super().setUp() |
@@ -729,6 +730,7 @@ class MarketTest(WebMockTestCase): | |||
729 | store_report.assert_called_once() | 730 | store_report.assert_called_once() |
730 | 731 | ||
731 | 732 | ||
733 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
732 | class ProcessorTest(WebMockTestCase): | 734 | class ProcessorTest(WebMockTestCase): |
733 | def test_values(self): | 735 | def test_values(self): |
734 | processor = market.Processor(self.m) | 736 | processor = market.Processor(self.m) |
diff --git a/tests/test_portfolio.py b/tests/test_portfolio.py index 14dc995..4d78996 100644 --- a/tests/test_portfolio.py +++ b/tests/test_portfolio.py | |||
@@ -2,6 +2,7 @@ from .helper import * | |||
2 | import portfolio | 2 | import portfolio |
3 | import datetime | 3 | import datetime |
4 | 4 | ||
5 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
5 | class ComputationTest(WebMockTestCase): | 6 | class ComputationTest(WebMockTestCase): |
6 | def test_compute_value(self): | 7 | def test_compute_value(self): |
7 | compute = mock.Mock() | 8 | compute = mock.Mock() |
@@ -25,6 +26,7 @@ class ComputationTest(WebMockTestCase): | |||
25 | portfolio.Computation.compute_value("foo", "bid", compute_value="test") | 26 | portfolio.Computation.compute_value("foo", "bid", compute_value="test") |
26 | compute.assert_called_with("foo", "bid") | 27 | compute.assert_called_with("foo", "bid") |
27 | 28 | ||
29 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
28 | class TradeTest(WebMockTestCase): | 30 | class TradeTest(WebMockTestCase): |
29 | 31 | ||
30 | def test_values_assertion(self): | 32 | def test_values_assertion(self): |
@@ -609,6 +611,7 @@ class TradeTest(WebMockTestCase): | |||
609 | self.assertEqual("ETH", as_json["currency"]) | 611 | self.assertEqual("ETH", as_json["currency"]) |
610 | self.assertEqual("BTC", as_json["base_currency"]) | 612 | self.assertEqual("BTC", as_json["base_currency"]) |
611 | 613 | ||
614 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
612 | class BalanceTest(WebMockTestCase): | 615 | class BalanceTest(WebMockTestCase): |
613 | def test_values(self): | 616 | def test_values(self): |
614 | balance = portfolio.Balance("BTC", { | 617 | balance = portfolio.Balance("BTC", { |
@@ -684,6 +687,7 @@ class BalanceTest(WebMockTestCase): | |||
684 | self.assertEqual(D(0), as_json["margin_available"]) | 687 | self.assertEqual(D(0), as_json["margin_available"]) |
685 | self.assertEqual(D(0), as_json["margin_borrowed"]) | 688 | self.assertEqual(D(0), as_json["margin_borrowed"]) |
686 | 689 | ||
690 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
687 | class OrderTest(WebMockTestCase): | 691 | class OrderTest(WebMockTestCase): |
688 | def test_values(self): | 692 | def test_values(self): |
689 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | 693 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), |
@@ -1745,6 +1749,7 @@ class OrderTest(WebMockTestCase): | |||
1745 | result = order.retrieve_order() | 1749 | result = order.retrieve_order() |
1746 | self.assertFalse(result) | 1750 | self.assertFalse(result) |
1747 | 1751 | ||
1752 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
1748 | class MouvementTest(WebMockTestCase): | 1753 | class MouvementTest(WebMockTestCase): |
1749 | def test_values(self): | 1754 | def test_values(self): |
1750 | mouvement = portfolio.Mouvement("ETH", "BTC", { | 1755 | mouvement = portfolio.Mouvement("ETH", "BTC", { |
@@ -1802,6 +1807,7 @@ class MouvementTest(WebMockTestCase): | |||
1802 | self.assertEqual("BTC", as_json["base_currency"]) | 1807 | self.assertEqual("BTC", as_json["base_currency"]) |
1803 | self.assertEqual("ETH", as_json["currency"]) | 1808 | self.assertEqual("ETH", as_json["currency"]) |
1804 | 1809 | ||
1810 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
1805 | class AmountTest(WebMockTestCase): | 1811 | class AmountTest(WebMockTestCase): |
1806 | def test_values(self): | 1812 | def test_values(self): |
1807 | amount = portfolio.Amount("BTC", "0.65") | 1813 | amount = portfolio.Amount("BTC", "0.65") |
diff --git a/tests/test_store.py b/tests/test_store.py index e281adb..ffd2645 100644 --- a/tests/test_store.py +++ b/tests/test_store.py | |||
@@ -4,12 +4,14 @@ import datetime | |||
4 | import threading | 4 | import threading |
5 | import market, portfolio, store | 5 | import market, portfolio, store |
6 | 6 | ||
7 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
7 | class NoopLockTest(unittest.TestCase): | 8 | class NoopLockTest(unittest.TestCase): |
8 | def test_with(self): | 9 | def test_with(self): |
9 | noop_lock = store.NoopLock() | 10 | noop_lock = store.NoopLock() |
10 | with noop_lock: | 11 | with noop_lock: |
11 | self.assertTrue(True) | 12 | self.assertTrue(True) |
12 | 13 | ||
14 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
13 | class LockedVarTest(unittest.TestCase): | 15 | class LockedVarTest(unittest.TestCase): |
14 | 16 | ||
15 | def test_values(self): | 17 | def test_values(self): |
@@ -61,6 +63,7 @@ class LockedVarTest(unittest.TestCase): | |||
61 | thread3.join() | 63 | thread3.join() |
62 | self.assertEqual("Bar", locked_var.get()[0:3]) | 64 | self.assertEqual("Bar", locked_var.get()[0:3]) |
63 | 65 | ||
66 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
64 | class TradeStoreTest(WebMockTestCase): | 67 | class TradeStoreTest(WebMockTestCase): |
65 | def test_compute_trades(self): | 68 | def test_compute_trades(self): |
66 | self.m.balances.currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"] | 69 | self.m.balances.currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"] |
@@ -285,6 +288,7 @@ class TradeStoreTest(WebMockTestCase): | |||
285 | 288 | ||
286 | self.assertEqual([trade_mock1, trade_mock2], trade_store.pending) | 289 | self.assertEqual([trade_mock1, trade_mock2], trade_store.pending) |
287 | 290 | ||
291 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
288 | class BalanceStoreTest(WebMockTestCase): | 292 | class BalanceStoreTest(WebMockTestCase): |
289 | def setUp(self): | 293 | def setUp(self): |
290 | super().setUp() | 294 | super().setUp() |
@@ -437,6 +441,7 @@ class BalanceStoreTest(WebMockTestCase): | |||
437 | self.assertEqual(1, as_json["BTC"]) | 441 | self.assertEqual(1, as_json["BTC"]) |
438 | self.assertEqual(2, as_json["ETH"]) | 442 | self.assertEqual(2, as_json["ETH"]) |
439 | 443 | ||
444 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
440 | class ReportStoreTest(WebMockTestCase): | 445 | class ReportStoreTest(WebMockTestCase): |
441 | def test_add_log(self): | 446 | def test_add_log(self): |
442 | with self.subTest(market=self.m): | 447 | with self.subTest(market=self.m): |
@@ -997,6 +1002,7 @@ class ReportStoreTest(WebMockTestCase): | |||
997 | 'action': 'Hey' | 1002 | 'action': 'Hey' |
998 | }) | 1003 | }) |
999 | 1004 | ||
1005 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
1000 | class PortfolioTest(WebMockTestCase): | 1006 | class PortfolioTest(WebMockTestCase): |
1001 | def setUp(self): | 1007 | def setUp(self): |
1002 | super().setUp() | 1008 | super().setUp() |