aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test.py17
-rw-r--r--tests/acceptance.py (renamed from test_acceptance.py)119
-rw-r--r--tests/helper.py12
-rw-r--r--tests/test_acceptance.py25
-rw-r--r--tests/test_ccxt_wrapper.py3
-rw-r--r--tests/test_main.py1
-rw-r--r--tests/test_market.py2
-rw-r--r--tests/test_portfolio.py6
-rw-r--r--tests/test_store.py6
9 files changed, 123 insertions, 68 deletions
diff --git a/test.py b/test.py
index 8b9d35b..d7743b2 100644
--- a/test.py
+++ b/test.py
@@ -1,10 +1,17 @@
1import unittest 1import unittest
2from tests.acceptance import TimeMock
2 3
3from tests.test_ccxt_wrapper import * 4from tests.helper import limits
4from tests.test_main import * 5
5from tests.test_market import * 6if "unit" in limits:
6from tests.test_store import * 7 from tests.test_ccxt_wrapper import *
7from 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
13if "acceptance" in limits:
14 from tests.test_acceptance import *
8 15
9if __name__ == '__main__': 16if __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
2import requests_mock 2import requests_mock
3import sys, os 3import sys, os
4import time, datetime 4import time, datetime
5import unittest
6from unittest import mock 5from unittest import mock
7from ssl import SSLError 6from ssl import SSLError
8from decimal import Decimal 7from decimal import Decimal
@@ -11,8 +10,56 @@ import psycopg2
11from io import StringIO 10from io import StringIO
12import re 11import re
13import functools 12import functools
14import glob 13import threading
15 14
15class 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
62TimeMock.start()
16import main 63import main
17 64
18class FileMock: 65class 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
205class 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
244class AcceptanceTestCase(): 258class 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
345for 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
359if __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
6from io import StringIO 6from io import StringIO
7import portfolio, market, main, store 7import 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
12limits = ["acceptance", "unit"]
13for 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
12class WebMockTestCase(unittest.TestCase): 22class 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 @@
1from .helper import limits
2from tests.acceptance import AcceptanceTestCase
3
4import unittest
5import glob
6
7__all__ = []
8
9for 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 @@
1from .helper import unittest, mock, D 1from .helper import limits, unittest, mock, D
2import requests_mock 2import requests_mock
3import market 3import market
4 4
5@unittest.skipUnless("unit" in limits, "Unit skipped")
5class poloniexETest(unittest.TestCase): 6class 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 @@
1from .helper import * 1from .helper import *
2import main, market 2import main, market
3 3
4@unittest.skipUnless("unit" in limits, "Unit skipped")
4class MainTest(WebMockTestCase): 5class 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 *
2import market, store, portfolio 2import market, store, portfolio
3import datetime 3import datetime
4 4
5@unittest.skipUnless("unit" in limits, "Unit skipped")
5class MarketTest(WebMockTestCase): 6class 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")
732class ProcessorTest(WebMockTestCase): 734class 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 *
2import portfolio 2import portfolio
3import datetime 3import datetime
4 4
5@unittest.skipUnless("unit" in limits, "Unit skipped")
5class ComputationTest(WebMockTestCase): 6class 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")
28class TradeTest(WebMockTestCase): 30class 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")
612class BalanceTest(WebMockTestCase): 615class 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")
687class OrderTest(WebMockTestCase): 691class 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")
1748class MouvementTest(WebMockTestCase): 1753class 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")
1805class AmountTest(WebMockTestCase): 1811class 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
4import threading 4import threading
5import market, portfolio, store 5import market, portfolio, store
6 6
7@unittest.skipUnless("unit" in limits, "Unit skipped")
7class NoopLockTest(unittest.TestCase): 8class 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")
13class LockedVarTest(unittest.TestCase): 15class 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")
64class TradeStoreTest(WebMockTestCase): 67class 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")
288class BalanceStoreTest(WebMockTestCase): 292class 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")
440class ReportStoreTest(WebMockTestCase): 445class 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")
1000class PortfolioTest(WebMockTestCase): 1006class PortfolioTest(WebMockTestCase):
1001 def setUp(self): 1007 def setUp(self):
1002 super().setUp() 1008 super().setUp()