django-esi 8.0.0a4__py3-none-any.whl → 8.0.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-esi might be problematic. Click here for more details.
- {django_esi-8.0.0a4.dist-info → django_esi-8.0.0b2.dist-info}/METADATA +5 -3
- {django_esi-8.0.0a4.dist-info → django_esi-8.0.0b2.dist-info}/RECORD +48 -47
- esi/__init__.py +1 -1
- esi/aiopenapi3/plugins.py +99 -3
- esi/clients.py +56 -7
- esi/decorators.py +26 -10
- esi/exceptions.py +7 -3
- esi/helpers.py +38 -0
- esi/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- esi/locale/cs_CZ/LC_MESSAGES/django.po +2 -2
- esi/locale/de/LC_MESSAGES/django.mo +0 -0
- esi/locale/de/LC_MESSAGES/django.po +2 -2
- esi/locale/en/LC_MESSAGES/django.mo +0 -0
- esi/locale/en/LC_MESSAGES/django.po +2 -2
- esi/locale/es/LC_MESSAGES/django.mo +0 -0
- esi/locale/es/LC_MESSAGES/django.po +2 -2
- esi/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
- esi/locale/fr_FR/LC_MESSAGES/django.po +2 -2
- esi/locale/it_IT/LC_MESSAGES/django.mo +0 -0
- esi/locale/it_IT/LC_MESSAGES/django.po +2 -2
- esi/locale/ja/LC_MESSAGES/django.mo +0 -0
- esi/locale/ja/LC_MESSAGES/django.po +2 -2
- esi/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
- esi/locale/ko_KR/LC_MESSAGES/django.po +2 -2
- esi/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
- esi/locale/nl_NL/LC_MESSAGES/django.po +2 -2
- esi/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
- esi/locale/pl_PL/LC_MESSAGES/django.po +2 -2
- esi/locale/ru/LC_MESSAGES/django.mo +0 -0
- esi/locale/ru/LC_MESSAGES/django.po +2 -2
- esi/locale/sk/LC_MESSAGES/django.mo +0 -0
- esi/locale/sk/LC_MESSAGES/django.po +2 -2
- esi/locale/uk/LC_MESSAGES/django.mo +0 -0
- esi/locale/uk/LC_MESSAGES/django.po +2 -2
- esi/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- esi/locale/zh_Hans/LC_MESSAGES/django.po +2 -2
- esi/managers.pyi +3 -0
- esi/openapi_clients.py +188 -44
- esi/rate_limiting.py +50 -21
- esi/signals.py +21 -0
- esi/stubs.pyi +9 -9
- esi/tests/__init__.py +33 -11
- esi/tests/test_clients.py +77 -19
- esi/tests/test_decorators.py +61 -1
- esi/tests/test_openapi.json +65 -2
- esi/tests/test_openapi.py +512 -18
- {django_esi-8.0.0a4.dist-info → django_esi-8.0.0b2.dist-info}/WHEEL +0 -0
- {django_esi-8.0.0a4.dist-info → django_esi-8.0.0b2.dist-info}/licenses/LICENSE +0 -0
esi/stubs.pyi
CHANGED
|
@@ -2100,18 +2100,18 @@ class GetUniverseSchematicsSchematicIdOperation(EsiOperation):
|
|
|
2100
2100
|
...
|
|
2101
2101
|
|
|
2102
2102
|
|
|
2103
|
-
class
|
|
2103
|
+
class PostRouteOperation(EsiOperation):
|
|
2104
2104
|
"""EsiOperation, use result(), results() or results_localized()"""
|
|
2105
|
-
def result(self, use_etag: bool = True, return_response: bool = False, force_refresh: bool = False, use_cache: bool = True, **extra) ->
|
|
2106
|
-
"""
|
|
2105
|
+
def result(self, use_etag: bool = True, return_response: bool = False, force_refresh: bool = False, use_cache: bool = True, **extra) -> Any:
|
|
2106
|
+
"""Calculate the systems between the given origin and destination."""
|
|
2107
2107
|
...
|
|
2108
2108
|
|
|
2109
|
-
def results(self, use_etag: bool = True, return_response: bool = False, force_refresh: bool = False, use_cache: bool = True, **extra) -> list[
|
|
2110
|
-
"""
|
|
2109
|
+
def results(self, use_etag: bool = True, return_response: bool = False, force_refresh: bool = False, use_cache: bool = True, **extra) -> list[Any]:
|
|
2110
|
+
"""Calculate the systems between the given origin and destination."""
|
|
2111
2111
|
...
|
|
2112
2112
|
|
|
2113
|
-
def results_localized(self, languages: list[str] | str | None = None, **extra) -> dict[str, list[
|
|
2114
|
-
"""
|
|
2113
|
+
def results_localized(self, languages: list[str] | str | None = None, **extra) -> dict[str, list[Any]]:
|
|
2114
|
+
"""Calculate the systems between the given origin and destination."""
|
|
2115
2115
|
...
|
|
2116
2116
|
|
|
2117
2117
|
|
|
@@ -3581,8 +3581,8 @@ class ESIClientStub:
|
|
|
3581
3581
|
Planetary_Interaction: _Planetary_Interaction = _Planetary_Interaction()
|
|
3582
3582
|
|
|
3583
3583
|
class _Routes:
|
|
3584
|
-
def
|
|
3585
|
-
"""
|
|
3584
|
+
def PostRoute(self, body: dict[str, Any], origin_system_id: int, destination_system_id: int, Accept_Language: str | None = ..., If_None_Match: str | None = ..., X_Compatibility_Date: str | None = ..., X_Tenant: str | None = ..., **kwargs: Any) -> PostRouteOperation:
|
|
3585
|
+
"""Calculate the systems between the given origin and destination."""
|
|
3586
3586
|
...
|
|
3587
3587
|
|
|
3588
3588
|
|
esi/tests/__init__.py
CHANGED
|
@@ -23,10 +23,10 @@ def _generate_token(
|
|
|
23
23
|
expires_in: int = 1200,
|
|
24
24
|
sso_version: int = 2
|
|
25
25
|
) -> dict:
|
|
26
|
-
|
|
26
|
+
import datetime as dt
|
|
27
27
|
|
|
28
28
|
if timestamp_dt is None:
|
|
29
|
-
timestamp_dt = datetime.
|
|
29
|
+
timestamp_dt = dt.datetime.now(dt.timezone.utc)
|
|
30
30
|
if scopes is None:
|
|
31
31
|
scopes = [
|
|
32
32
|
'esi-mail.read_mail.v1',
|
|
@@ -40,7 +40,7 @@ def _generate_token(
|
|
|
40
40
|
'timestamp': int(timestamp_dt.timestamp()),
|
|
41
41
|
'character_id': character_id,
|
|
42
42
|
'name': character_name,
|
|
43
|
-
'ExpiresOn': _dt_eveformat(timestamp_dt + timedelta(seconds=expires_in)),
|
|
43
|
+
'ExpiresOn': _dt_eveformat(timestamp_dt + dt.timedelta(seconds=expires_in)),
|
|
44
44
|
'scp': scopes,
|
|
45
45
|
'token_type': 'character',
|
|
46
46
|
'owner': character_owner_hash,
|
|
@@ -84,6 +84,32 @@ class SocketAccessError(Exception):
|
|
|
84
84
|
"""Error raised when a test script accesses the network"""
|
|
85
85
|
|
|
86
86
|
|
|
87
|
+
class GuardedSocket(socket.socket):
|
|
88
|
+
"""A socket subclass that only allows loopback/localhost."""
|
|
89
|
+
|
|
90
|
+
def _address_is_loopback(self, address):
|
|
91
|
+
|
|
92
|
+
host = None
|
|
93
|
+
if isinstance(address, tuple):
|
|
94
|
+
host = address[0]
|
|
95
|
+
else:
|
|
96
|
+
host = address
|
|
97
|
+
|
|
98
|
+
if isinstance(host, bytes):
|
|
99
|
+
host = host.decode()
|
|
100
|
+
|
|
101
|
+
# quick allow by obvious names
|
|
102
|
+
if host in ("localhost", "127.0.0.1", "::1"):
|
|
103
|
+
return True
|
|
104
|
+
else:
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
def connect(self, address):
|
|
108
|
+
if not self._address_is_loopback(address):
|
|
109
|
+
raise SocketAccessError(f"Attempt to connect to non-localhost address: {address!r}")
|
|
110
|
+
return super().connect(address)
|
|
111
|
+
|
|
112
|
+
|
|
87
113
|
class NoSocketsTestCase(TestCase):
|
|
88
114
|
"""Variation of Django's TestCase class that prevents any network use.
|
|
89
115
|
|
|
@@ -98,15 +124,11 @@ class NoSocketsTestCase(TestCase):
|
|
|
98
124
|
"""
|
|
99
125
|
@classmethod
|
|
100
126
|
def setUpClass(cls):
|
|
101
|
-
cls.
|
|
102
|
-
socket.socket =
|
|
127
|
+
cls._socket_original = socket.socket
|
|
128
|
+
socket.socket = GuardedSocket
|
|
103
129
|
return super().setUpClass()
|
|
104
130
|
|
|
105
131
|
@classmethod
|
|
106
132
|
def tearDownClass(cls):
|
|
107
|
-
socket.socket = cls.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@staticmethod
|
|
111
|
-
def guard(*args, **kwargs):
|
|
112
|
-
raise SocketAccessError("Attempted to access network")
|
|
133
|
+
socket.socket = cls._socket_original
|
|
134
|
+
super().tearDownClass()
|
esi/tests/test_clients.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import datetime
|
|
2
2
|
import os
|
|
3
3
|
from unittest.mock import patch, Mock
|
|
4
4
|
import json
|
|
@@ -49,7 +49,7 @@ def _load_json_file(path):
|
|
|
49
49
|
|
|
50
50
|
class MockResultFuture:
|
|
51
51
|
def __init__(self):
|
|
52
|
-
dt = datetime.
|
|
52
|
+
dt = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=60)
|
|
53
53
|
self.headers = {"Expires": dt.strftime("%a, %d %b %Y %H:%M:%S %Z")}
|
|
54
54
|
self.status_code = 200
|
|
55
55
|
self.text = "dummy"
|
|
@@ -57,7 +57,7 @@ class MockResultFuture:
|
|
|
57
57
|
|
|
58
58
|
class MockResultPast:
|
|
59
59
|
def __init__(self):
|
|
60
|
-
dt = datetime.
|
|
60
|
+
dt = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=60)
|
|
61
61
|
self.headers = {"Expires": dt.strftime("%a, %d %b %Y %H:%M:%S %Z")}
|
|
62
62
|
|
|
63
63
|
|
|
@@ -198,7 +198,7 @@ class TestTokenAuthenticator(NoSocketsTestCase):
|
|
|
198
198
|
request.headers = dict()
|
|
199
199
|
request.params = dict()
|
|
200
200
|
|
|
201
|
-
self.token.created -= timedelta(121)
|
|
201
|
+
self.token.created -= datetime.timedelta(121)
|
|
202
202
|
|
|
203
203
|
x = TokenAuthenticator(token=self.token)
|
|
204
204
|
request2 = x.apply(request)
|
|
@@ -212,7 +212,7 @@ class TestTokenAuthenticator(NoSocketsTestCase):
|
|
|
212
212
|
request.headers = dict()
|
|
213
213
|
request.params = dict()
|
|
214
214
|
|
|
215
|
-
self.token.created -= timedelta(121)
|
|
215
|
+
self.token.created -= datetime.timedelta(121)
|
|
216
216
|
self.token.refresh_token = None
|
|
217
217
|
|
|
218
218
|
x = TokenAuthenticator(token=self.token)
|
|
@@ -733,7 +733,10 @@ class TestClientResult2(NoSocketsTestCase):
|
|
|
733
733
|
# then
|
|
734
734
|
self.assertTrue(requests_mocker.called)
|
|
735
735
|
request = requests_mocker.last_request
|
|
736
|
-
|
|
736
|
+
|
|
737
|
+
expected_title = 'DjangoEsi'
|
|
738
|
+
|
|
739
|
+
self.assertEqual(request._request.headers["User-Agent"], f"{expected_title}/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
737
740
|
|
|
738
741
|
def test_existing_headers(self, requests_mocker):
|
|
739
742
|
# given
|
|
@@ -743,7 +746,10 @@ class TestClientResult2(NoSocketsTestCase):
|
|
|
743
746
|
# then
|
|
744
747
|
self.assertTrue(requests_mocker.called)
|
|
745
748
|
request = requests_mocker.last_request
|
|
746
|
-
|
|
749
|
+
|
|
750
|
+
expected_title = 'DjangoEsi'
|
|
751
|
+
|
|
752
|
+
self.assertEqual(request._request.headers["User-Agent"], f"{expected_title}/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
747
753
|
|
|
748
754
|
|
|
749
755
|
class TestRequestsClientPlus(NoSocketsTestCase):
|
|
@@ -809,7 +815,9 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
809
815
|
# when
|
|
810
816
|
operation = client.Status.get_status()
|
|
811
817
|
# then
|
|
812
|
-
|
|
818
|
+
expected_app_name = "MyApp"
|
|
819
|
+
expected_title = 'DjangoEsi'
|
|
820
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_title}/1.0.0 (None; +https://gitlab.com/allianceauth/django-esi)")
|
|
813
821
|
|
|
814
822
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
815
823
|
def test_defaults_email(self, requests_mocker) -> None:
|
|
@@ -821,7 +829,9 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
821
829
|
# when
|
|
822
830
|
operation = client.Status.get_status()
|
|
823
831
|
# then
|
|
824
|
-
|
|
832
|
+
expected_app_name = "MyApp"
|
|
833
|
+
expected_title = 'DjangoEsi'
|
|
834
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_title}/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
825
835
|
|
|
826
836
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", None)
|
|
827
837
|
def test_app_text(self, requests_mocker) -> None:
|
|
@@ -835,7 +845,9 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
835
845
|
# when
|
|
836
846
|
operation = client.Status.get_status()
|
|
837
847
|
# then
|
|
838
|
-
|
|
848
|
+
expected_app_name = "MyApp"
|
|
849
|
+
expected_title = 'DjangoEsi'
|
|
850
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"my-app v1.0.0 (None) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
839
851
|
|
|
840
852
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
841
853
|
def test_app_text_with_email(self, requests_mocker):
|
|
@@ -848,7 +860,9 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
848
860
|
# when
|
|
849
861
|
operation = client.Status.get_status()
|
|
850
862
|
# then
|
|
851
|
-
|
|
863
|
+
expected_app_name = "MyApp"
|
|
864
|
+
expected_title = 'DjangoEsi'
|
|
865
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"my-app v1.0.0 (email@example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
852
866
|
|
|
853
867
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
854
868
|
def test_ua_generator(self, requests_mocker):
|
|
@@ -859,7 +873,41 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
859
873
|
# when
|
|
860
874
|
operation = client.Status.get_status()
|
|
861
875
|
# then
|
|
862
|
-
|
|
876
|
+
expected_app_name = "MyApp"
|
|
877
|
+
expected_title = 'DjangoEsi'
|
|
878
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_app_name}/1.0.0 (email@example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
879
|
+
|
|
880
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
881
|
+
def test_ua_generator_for_appname_with_spaces(self, requests_mocker):
|
|
882
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
883
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
884
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
885
|
+
client = esi_client_factory(ua_appname="My App", ua_version="1.0.0")
|
|
886
|
+
|
|
887
|
+
# when
|
|
888
|
+
operation = client.Status.get_status()
|
|
889
|
+
|
|
890
|
+
# then
|
|
891
|
+
expected_app_name = "MyApp"
|
|
892
|
+
expected_title = 'DjangoEsi'
|
|
893
|
+
|
|
894
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_app_name}/1.0.0 (email@example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
895
|
+
|
|
896
|
+
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
897
|
+
def test_ua_generator_for_appname_with_hyphens(self, requests_mocker):
|
|
898
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/_latest/swagger.json", json=self.spec)
|
|
899
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/latest/swagger.json", json=self.spec)
|
|
900
|
+
requests_mocker.register_uri("GET", url="https://esi.evetech.net/v1/status/", json=self.status_response)
|
|
901
|
+
client = esi_client_factory(ua_appname="My-App", ua_version="1.0.0")
|
|
902
|
+
|
|
903
|
+
# when
|
|
904
|
+
operation = client.Status.get_status()
|
|
905
|
+
|
|
906
|
+
# then
|
|
907
|
+
expected_app_name = "MyApp"
|
|
908
|
+
expected_title = 'DjangoEsi'
|
|
909
|
+
|
|
910
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_app_name}/1.0.0 (email@example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
863
911
|
|
|
864
912
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
865
913
|
def test_ua_generator_with_url(self, requests_mocker):
|
|
@@ -870,7 +918,9 @@ class TestEsiClientFactoryAppText(NoSocketsTestCase):
|
|
|
870
918
|
# when
|
|
871
919
|
operation = client.Status.get_status()
|
|
872
920
|
# then
|
|
873
|
-
|
|
921
|
+
expected_app_name = "MyApp"
|
|
922
|
+
expected_title = 'DjangoEsi'
|
|
923
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_app_name}/1.0.0 (email@example.com; +https://example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
874
924
|
|
|
875
925
|
@patch(MODULE_PATH + ".__title__", "Django-ESI")
|
|
876
926
|
@patch(MODULE_PATH + ".__version__", "1.0.0")
|
|
@@ -897,7 +947,8 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
897
947
|
# when
|
|
898
948
|
operation = client.Status.get_status()
|
|
899
949
|
# then
|
|
900
|
-
|
|
950
|
+
expected_title = 'DjangoEsi'
|
|
951
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_title}/1.0.0 (None; +https://gitlab.com/allianceauth/django-esi)")
|
|
901
952
|
|
|
902
953
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
903
954
|
def test_defaults_email(self, requests_mocker) -> None:
|
|
@@ -909,7 +960,8 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
909
960
|
# when
|
|
910
961
|
operation = client.Status.get_status()
|
|
911
962
|
# then
|
|
912
|
-
|
|
963
|
+
expected_title = 'DjangoEsi'
|
|
964
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_title}/1.0.0 (email@example.com; +https://gitlab.com/allianceauth/django-esi)")
|
|
913
965
|
|
|
914
966
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", None)
|
|
915
967
|
def test_app_text(self, requests_mocker) -> None:
|
|
@@ -923,7 +975,8 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
923
975
|
# when
|
|
924
976
|
operation = client.Status.get_status()
|
|
925
977
|
# then
|
|
926
|
-
|
|
978
|
+
expected_title = 'DjangoEsi'
|
|
979
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"my-app v1.0.0 (None) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
927
980
|
|
|
928
981
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
929
982
|
def test_app_text_with_email(self, requests_mocker):
|
|
@@ -936,7 +989,8 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
936
989
|
# when
|
|
937
990
|
operation = client.Status.get_status()
|
|
938
991
|
# then
|
|
939
|
-
|
|
992
|
+
expected_title = 'DjangoEsi'
|
|
993
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"my-app v1.0.0 (email@example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)",)
|
|
940
994
|
|
|
941
995
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
942
996
|
def test_ua_generator(self, requests_mocker):
|
|
@@ -947,7 +1001,9 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
947
1001
|
# when
|
|
948
1002
|
operation = client.Status.get_status()
|
|
949
1003
|
# then
|
|
950
|
-
|
|
1004
|
+
expected_app_name = "MyApp"
|
|
1005
|
+
expected_title = 'DjangoEsi'
|
|
1006
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_app_name}/1.0.0 (email@example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
|
951
1007
|
|
|
952
1008
|
@patch(MODULE_PATH + ".app_settings.ESI_USER_CONTACT_EMAIL", "email@example.com")
|
|
953
1009
|
def test_ua_generator_with_url(self, requests_mocker):
|
|
@@ -958,4 +1014,6 @@ class TestEsiClientProviderAppText(NoSocketsTestCase):
|
|
|
958
1014
|
# when
|
|
959
1015
|
operation = client.Status.get_status()
|
|
960
1016
|
# then
|
|
961
|
-
|
|
1017
|
+
expected_app_name = "MyApp"
|
|
1018
|
+
expected_title = 'DjangoEsi'
|
|
1019
|
+
self.assertEqual(operation.future.request.headers["User-Agent"], f"{expected_app_name}/1.0.0 (email@example.com; +https://example.com) {expected_title}/1.0.0 (+https://gitlab.com/allianceauth/django-esi)")
|
esi/tests/test_decorators.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""unit tests for esi decorators"""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from time import time
|
|
4
5
|
from unittest.mock import patch, Mock
|
|
5
6
|
|
|
7
|
+
from django.core.cache import cache
|
|
6
8
|
from django.contrib.auth.models import User
|
|
7
9
|
from django.contrib.auth.views import redirect_to_login
|
|
8
10
|
from django.contrib.sessions.middleware import SessionMiddleware
|
|
@@ -10,8 +12,10 @@ from django.http import HttpResponse
|
|
|
10
12
|
from django.test import TestCase, RequestFactory
|
|
11
13
|
|
|
12
14
|
from . import _generate_token, _store_as_Token
|
|
15
|
+
from ..rate_limiting import ESIRateLimitBucket, ESIRateLimits
|
|
16
|
+
from ..exceptions import ESIBucketLimitException
|
|
13
17
|
from ..decorators import (
|
|
14
|
-
_check_callback, tokens_required, token_required, single_use_token
|
|
18
|
+
_check_callback, esi_rate_limiter_bucketed, tokens_required, token_required, single_use_token, wait_for_esi_errorlimit_reset
|
|
15
19
|
)
|
|
16
20
|
from ..models import Token, CallbackRedirect
|
|
17
21
|
|
|
@@ -516,3 +520,59 @@ class TestSingleUseTokenRequired(TestCase):
|
|
|
516
520
|
response,
|
|
517
521
|
self.token
|
|
518
522
|
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class TestESIRateLimitDecorator(TestCase):
|
|
526
|
+
|
|
527
|
+
def setUp(self):
|
|
528
|
+
self.bucket = ESIRateLimitBucket(
|
|
529
|
+
"test-bucket",
|
|
530
|
+
1,
|
|
531
|
+
5
|
|
532
|
+
)
|
|
533
|
+
cache.clear()
|
|
534
|
+
|
|
535
|
+
def test_raise(self):
|
|
536
|
+
@esi_rate_limiter_bucketed(bucket=self.bucket)
|
|
537
|
+
def my_func():
|
|
538
|
+
return "Pass"
|
|
539
|
+
|
|
540
|
+
_t = time()
|
|
541
|
+
my_func()
|
|
542
|
+
self.assertLess(time() - _t, 1)
|
|
543
|
+
_t = time()
|
|
544
|
+
with self.assertRaises(ESIBucketLimitException):
|
|
545
|
+
my_func()
|
|
546
|
+
|
|
547
|
+
def test_sleep(self):
|
|
548
|
+
@esi_rate_limiter_bucketed(bucket=self.bucket, raise_on_limit=False)
|
|
549
|
+
def my_func():
|
|
550
|
+
return "Pass"
|
|
551
|
+
|
|
552
|
+
_t = time()
|
|
553
|
+
my_func()
|
|
554
|
+
self.assertLess(time() - _t, 1)
|
|
555
|
+
_t = time()
|
|
556
|
+
my_func()
|
|
557
|
+
duration = time() - _t
|
|
558
|
+
self.assertGreater(duration, 5)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
class TestESIErrorLimitDecorator(TestCase):
|
|
562
|
+
|
|
563
|
+
def setUp(self):
|
|
564
|
+
cache.clear()
|
|
565
|
+
|
|
566
|
+
def test_sleep(self):
|
|
567
|
+
@wait_for_esi_errorlimit_reset()
|
|
568
|
+
def my_func():
|
|
569
|
+
return "Pass"
|
|
570
|
+
|
|
571
|
+
_t = time()
|
|
572
|
+
my_func()
|
|
573
|
+
self.assertLess(time() - _t, 1)
|
|
574
|
+
cache.set("esi_error_limit_reset", 5, timeout=5)
|
|
575
|
+
_t = time()
|
|
576
|
+
my_func()
|
|
577
|
+
duration = time() - _t
|
|
578
|
+
self.assertGreater(duration, 5)
|
esi/tests/test_openapi.json
CHANGED
|
@@ -122,7 +122,12 @@
|
|
|
122
122
|
}
|
|
123
123
|
},
|
|
124
124
|
"type": "object"
|
|
125
|
+
},
|
|
126
|
+
"UniverseTypesGet": {
|
|
127
|
+
"items": { "format": "int64", "type": "integer" },
|
|
128
|
+
"type": "array"
|
|
125
129
|
}
|
|
130
|
+
|
|
126
131
|
},
|
|
127
132
|
"securitySchemes": {
|
|
128
133
|
"OAuth2": {
|
|
@@ -217,6 +222,58 @@
|
|
|
217
222
|
},
|
|
218
223
|
"openapi": "3.1.0",
|
|
219
224
|
"paths": {
|
|
225
|
+
"/universe/types": {
|
|
226
|
+
"get": {
|
|
227
|
+
"description": "Get a list of type ids\n\nThis route expires daily at 11:05",
|
|
228
|
+
"operationId": "GetUniverseTypes",
|
|
229
|
+
"parameters": [
|
|
230
|
+
{
|
|
231
|
+
"in": "query",
|
|
232
|
+
"name": "page",
|
|
233
|
+
"schema": {
|
|
234
|
+
"description": "Which page of results to return.",
|
|
235
|
+
"format": "int32",
|
|
236
|
+
"minimum": 1,
|
|
237
|
+
"type": "integer"
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{ "$ref": "#/components/parameters/AcceptLanguage" },
|
|
241
|
+
{ "$ref": "#/components/parameters/IfNoneMatch" },
|
|
242
|
+
{ "$ref": "#/components/parameters/CompatibilityDate" },
|
|
243
|
+
{ "$ref": "#/components/parameters/Tenant" }
|
|
244
|
+
],
|
|
245
|
+
"responses": {
|
|
246
|
+
"200": {
|
|
247
|
+
"content": {
|
|
248
|
+
"application/json": {
|
|
249
|
+
"schema": { "$ref": "#/components/schemas/UniverseTypesGet" }
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"description": "OK",
|
|
253
|
+
"headers": {
|
|
254
|
+
"Cache-Control": { "$ref": "#/components/headers/CacheControl" },
|
|
255
|
+
"ETag": { "$ref": "#/components/headers/ETag" },
|
|
256
|
+
"Last-Modified": { "$ref": "#/components/headers/LastModified" },
|
|
257
|
+
"X-Pages": {
|
|
258
|
+
"description": "The total number of pages in the result set.",
|
|
259
|
+
"schema": { "format": "int64", "type": "integer" }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"default": {
|
|
264
|
+
"content": {
|
|
265
|
+
"application/json": {
|
|
266
|
+
"schema": { "$ref": "#/components/schemas/Error" }
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
"description": "Error"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
"summary": "Get types",
|
|
273
|
+
"tags": ["Universe"],
|
|
274
|
+
"x-compatibility-date": "2020-01-01"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
220
277
|
"/status": {
|
|
221
278
|
"get": {
|
|
222
279
|
"description": "EVE Server status",
|
|
@@ -253,12 +310,18 @@
|
|
|
253
310
|
"summary": "Retrieve the uptime and player counts",
|
|
254
311
|
"tags": ["Status"],
|
|
255
312
|
"x-cache-age": 30,
|
|
256
|
-
"x-compatibility-date": "2020-01-01"
|
|
313
|
+
"x-compatibility-date": "2020-01-01",
|
|
314
|
+
"x-rate-limit": {
|
|
315
|
+
"group": "status",
|
|
316
|
+
"max-tokens": 600,
|
|
317
|
+
"window-size": "15m"
|
|
318
|
+
}
|
|
257
319
|
}
|
|
258
320
|
}
|
|
259
321
|
},
|
|
260
322
|
"servers": [{ "url": "https://esi.evetech.net" }],
|
|
261
323
|
"tags": [
|
|
262
|
-
{ "name": "Status" }
|
|
324
|
+
{ "name": "Status" },
|
|
325
|
+
{ "name": "Universe" }
|
|
263
326
|
]
|
|
264
327
|
}
|