django-esi 8.0.0a3__py3-none-any.whl → 8.0.0b1__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.0a3.dist-info → django_esi-8.0.0b1.dist-info}/METADATA +3 -2
- {django_esi-8.0.0a3.dist-info → django_esi-8.0.0b1.dist-info}/RECORD +44 -33
- esi/__init__.py +2 -2
- esi/aiopenapi3/plugins.py +100 -3
- esi/clients.py +15 -6
- esi/helpers.py +37 -0
- esi/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- esi/locale/cs_CZ/LC_MESSAGES/django.po +53 -0
- esi/locale/de/LC_MESSAGES/django.mo +0 -0
- esi/locale/de/LC_MESSAGES/django.po +10 -9
- esi/locale/en/LC_MESSAGES/django.mo +0 -0
- esi/locale/en/LC_MESSAGES/django.po +3 -3
- esi/locale/es/LC_MESSAGES/django.mo +0 -0
- esi/locale/es/LC_MESSAGES/django.po +12 -10
- esi/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
- esi/locale/fr_FR/LC_MESSAGES/django.po +18 -10
- esi/locale/it_IT/LC_MESSAGES/django.mo +0 -0
- esi/locale/it_IT/LC_MESSAGES/django.po +12 -10
- esi/locale/ja/LC_MESSAGES/django.mo +0 -0
- esi/locale/ja/LC_MESSAGES/django.po +10 -9
- esi/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
- esi/locale/ko_KR/LC_MESSAGES/django.po +10 -9
- esi/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
- esi/locale/nl_NL/LC_MESSAGES/django.po +53 -0
- esi/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
- esi/locale/pl_PL/LC_MESSAGES/django.po +53 -0
- esi/locale/ru/LC_MESSAGES/django.mo +0 -0
- esi/locale/ru/LC_MESSAGES/django.po +13 -10
- esi/locale/sk/LC_MESSAGES/django.mo +0 -0
- esi/locale/sk/LC_MESSAGES/django.po +55 -0
- esi/locale/uk/LC_MESSAGES/django.mo +0 -0
- esi/locale/uk/LC_MESSAGES/django.po +57 -0
- esi/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- esi/locale/zh_Hans/LC_MESSAGES/django.po +10 -9
- esi/management/commands/generate_esi_stubs.py +11 -31
- esi/models.py +1 -1
- esi/openapi_clients.py +207 -54
- esi/stubs.pyi +395 -395
- esi/tests/__init__.py +3 -3
- esi/tests/test_clients.py +77 -19
- esi/tests/test_openapi.json +264 -0
- esi/tests/test_openapi.py +402 -1
- {django_esi-8.0.0a3.dist-info → django_esi-8.0.0b1.dist-info}/WHEEL +0 -0
- {django_esi-8.0.0a3.dist-info → django_esi-8.0.0b1.dist-info}/licenses/LICENSE +0 -0
esi/tests/test_openapi.py
CHANGED
|
@@ -1,7 +1,272 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from unittest import mock
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
1
4
|
from django.test import TestCase
|
|
2
|
-
from datetime import date
|
|
5
|
+
from datetime import date, timedelta
|
|
3
6
|
|
|
4
7
|
from esi.openapi_clients import ESIClientProvider
|
|
8
|
+
from django.core.cache import cache
|
|
9
|
+
from django.utils import timezone
|
|
10
|
+
from httpx import RequestError, HTTPStatusError
|
|
11
|
+
from esi.exceptions import ESIErrorLimitException, HTTPNotModified
|
|
12
|
+
from esi import app_settings
|
|
13
|
+
from esi import __title__, __url__, __version__
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
from .. import openapi_clients as oc
|
|
17
|
+
|
|
18
|
+
SPEC_PATH = os.path.join(
|
|
19
|
+
os.path.dirname(os.path.abspath(__file__)), "test_openapi.json"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestClientFunctions(TestCase):
|
|
24
|
+
def test_time_to_expiry_valid(self):
|
|
25
|
+
expires = (
|
|
26
|
+
timezone.now() + timedelta(seconds=120)
|
|
27
|
+
).strftime('%a, %d %b %Y %H:%M:%S %Z')
|
|
28
|
+
ttl = oc._time_to_expiry(expires)
|
|
29
|
+
|
|
30
|
+
# this shouldnt take more that 10 seconds
|
|
31
|
+
self.assertGreater(ttl, 110)
|
|
32
|
+
|
|
33
|
+
def test_time_to_expiry_invalid(self):
|
|
34
|
+
# invalid format returns 0
|
|
35
|
+
self.assertEqual(oc._time_to_expiry("not-a-date"), 0)
|
|
36
|
+
|
|
37
|
+
def test_httpx_exceptions_valids(self):
|
|
38
|
+
self.assertTrue(
|
|
39
|
+
oc._httpx_exceptions(
|
|
40
|
+
RequestError("Bad Request")
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
response = MagicMock(status_code=502)
|
|
45
|
+
exc = HTTPStatusError("msg", request=None, response=response)
|
|
46
|
+
|
|
47
|
+
self.assertTrue(
|
|
48
|
+
oc._httpx_exceptions(exc)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
response.status_code = 400
|
|
52
|
+
exc = HTTPStatusError("msg", request=None, response=response)
|
|
53
|
+
|
|
54
|
+
self.assertFalse(
|
|
55
|
+
oc._httpx_exceptions(exc)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self.assertFalse(
|
|
59
|
+
oc._httpx_exceptions(
|
|
60
|
+
ESIErrorLimitException(reset=10)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def test_httpx_exceptions_invalid(self):
|
|
65
|
+
self.assertFalse(
|
|
66
|
+
oc._httpx_exceptions(
|
|
67
|
+
"this is not an exception!"
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class BuildUserAgentTests(TestCase):
|
|
73
|
+
app_name = "TestApp"
|
|
74
|
+
app_ver = "1.2.3"
|
|
75
|
+
app_url = "https://tests.pass"
|
|
76
|
+
|
|
77
|
+
def test_build_user_agent_with_url(self):
|
|
78
|
+
ua = oc._build_user_agent(self.app_name, self.app_ver, self.app_url)
|
|
79
|
+
|
|
80
|
+
expected_app_name = "TestApp"
|
|
81
|
+
expected_title = 'DjangoEsi'
|
|
82
|
+
|
|
83
|
+
self.assertEqual(
|
|
84
|
+
(
|
|
85
|
+
f"{expected_app_name}/{self.app_ver} "
|
|
86
|
+
f"({app_settings.ESI_USER_CONTACT_EMAIL}{f'; +{self.app_url})'} "
|
|
87
|
+
f"{expected_title}/{__version__} (+{__url__})"
|
|
88
|
+
),
|
|
89
|
+
ua
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def test_enforce_pascal_case_for_ua_appname_with_space(self):
|
|
93
|
+
"""
|
|
94
|
+
Test that the application name is converted to PascalCase in the User-Agent string when it contains spaces.
|
|
95
|
+
|
|
96
|
+
:return:
|
|
97
|
+
:rtype:
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
ua = oc._build_user_agent("test app", self.app_ver, self.app_url)
|
|
101
|
+
|
|
102
|
+
expected_app_name = "TestApp"
|
|
103
|
+
expected_title = 'DjangoEsi'
|
|
104
|
+
|
|
105
|
+
self.assertEqual(
|
|
106
|
+
(
|
|
107
|
+
f"{expected_app_name}/{self.app_ver} "
|
|
108
|
+
f"({app_settings.ESI_USER_CONTACT_EMAIL}{f'; +{self.app_url})'} "
|
|
109
|
+
f"{expected_title}/{__version__} (+{__url__})"
|
|
110
|
+
),
|
|
111
|
+
ua
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def test_enforce_pascal_case_for_ua_appname_with_hyphen(self):
|
|
115
|
+
"""
|
|
116
|
+
Test that the application name is converted to PascalCase in the User-Agent string when it contains hyphens.
|
|
117
|
+
|
|
118
|
+
:return:
|
|
119
|
+
:rtype:
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
ua = oc._build_user_agent("test-app", self.app_ver, self.app_url)
|
|
123
|
+
|
|
124
|
+
expected_app_name = "TestApp"
|
|
125
|
+
expected_title = 'DjangoEsi'
|
|
126
|
+
|
|
127
|
+
self.assertEqual(
|
|
128
|
+
(
|
|
129
|
+
f"{expected_app_name}/{self.app_ver} "
|
|
130
|
+
f"({app_settings.ESI_USER_CONTACT_EMAIL}{f'; +{self.app_url})'} "
|
|
131
|
+
f"{expected_title}/{__version__} (+{__url__})"
|
|
132
|
+
),
|
|
133
|
+
ua
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def test_build_user_agent_without_url(self):
|
|
137
|
+
ua = oc._build_user_agent(self.app_name, self.app_ver)
|
|
138
|
+
|
|
139
|
+
expected_app_name = "TestApp"
|
|
140
|
+
expected_title = 'DjangoEsi'
|
|
141
|
+
|
|
142
|
+
self.assertEqual(
|
|
143
|
+
(
|
|
144
|
+
f"{expected_app_name}/{self.app_ver} "
|
|
145
|
+
f"({app_settings.ESI_USER_CONTACT_EMAIL}) "
|
|
146
|
+
f"{expected_title}/{__version__} (+{__url__})"
|
|
147
|
+
),
|
|
148
|
+
ua
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class BaseEsiOperationTests(TestCase):
|
|
153
|
+
def setUp(self):
|
|
154
|
+
self.page_param = MagicMock()
|
|
155
|
+
self.page_param.name = "page"
|
|
156
|
+
|
|
157
|
+
self.after_param = MagicMock()
|
|
158
|
+
self.after_param.name = "after"
|
|
159
|
+
|
|
160
|
+
self.before_param = MagicMock()
|
|
161
|
+
self.before_param.name = "before"
|
|
162
|
+
|
|
163
|
+
self.data_param = MagicMock()
|
|
164
|
+
self.data_param.name = "data"
|
|
165
|
+
|
|
166
|
+
self.lang_param = MagicMock()
|
|
167
|
+
self.lang_param.name = "Accept-Language"
|
|
168
|
+
|
|
169
|
+
self.body_param = MagicMock()
|
|
170
|
+
self.body_param.name = "body"
|
|
171
|
+
|
|
172
|
+
self.fake_op = MagicMock()
|
|
173
|
+
self.fake_op.parameters = [
|
|
174
|
+
self.data_param,
|
|
175
|
+
self.lang_param
|
|
176
|
+
]
|
|
177
|
+
self.fake_op.tags = ["test"]
|
|
178
|
+
self.fake_op.operationId = "fake_op"
|
|
179
|
+
self.api = MagicMock(app_name="TestApp")
|
|
180
|
+
self.op = oc.BaseEsiOperation(
|
|
181
|
+
("GET", "/fake_op", self.fake_op, {}),
|
|
182
|
+
self.api
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def test_non_unique_kwargs(self):
|
|
186
|
+
op_1 = self.op(data="bar")
|
|
187
|
+
key_1 = op_1._cache_key()
|
|
188
|
+
op_2 = self.op(data="foo")
|
|
189
|
+
key_2 = op_2._cache_key()
|
|
190
|
+
self.assertNotEqual(key_1, key_2)
|
|
191
|
+
|
|
192
|
+
def test_unique_kwargs(self):
|
|
193
|
+
op_1 = self.op(data="foo")
|
|
194
|
+
key_1 = op_1._cache_key()
|
|
195
|
+
op_2 = self.op(data="foo")
|
|
196
|
+
key_2 = op_2._cache_key()
|
|
197
|
+
self.assertEqual(key_1, key_2)
|
|
198
|
+
|
|
199
|
+
def test_extract_body(self):
|
|
200
|
+
test_body = "something somethng something..."
|
|
201
|
+
op = self.op(body=test_body)
|
|
202
|
+
body = op._extract_body_param()
|
|
203
|
+
self.assertEqual(test_body, body)
|
|
204
|
+
|
|
205
|
+
def test_extract_body_exception(self):
|
|
206
|
+
test_body = "something somethng something..."
|
|
207
|
+
self.fake_op.requestBody = False
|
|
208
|
+
op = self.op(body=test_body)
|
|
209
|
+
with self.assertRaises(ValueError):
|
|
210
|
+
op._extract_body_param()
|
|
211
|
+
|
|
212
|
+
def test_extract_token(self):
|
|
213
|
+
test_tkn = {"token": "token model goes here"}
|
|
214
|
+
op = self.op(token=test_tkn)
|
|
215
|
+
token = op._extract_token_param()
|
|
216
|
+
self.assertEqual(test_tkn, token)
|
|
217
|
+
|
|
218
|
+
def test_extract_token_exception_no_token_needed(self):
|
|
219
|
+
self.op._kwargs = {"token": "token"}
|
|
220
|
+
self.fake_op.security = None
|
|
221
|
+
with self.assertRaises(ValueError):
|
|
222
|
+
self.op._extract_token_param()
|
|
223
|
+
|
|
224
|
+
def test_not_page_or_cursor_param(self):
|
|
225
|
+
self.assertFalse(self.op._has_page_param())
|
|
226
|
+
self.assertFalse(self.op._has_cursor_param())
|
|
227
|
+
|
|
228
|
+
def test_has_page_param(self):
|
|
229
|
+
self.fake_op.parameters += [self.page_param]
|
|
230
|
+
op = self.op()
|
|
231
|
+
self.assertTrue(op._has_page_param())
|
|
232
|
+
|
|
233
|
+
def test_has_cursor_params(self):
|
|
234
|
+
self.fake_op.parameters = [self.after_param]
|
|
235
|
+
op = self.op()
|
|
236
|
+
self.assertTrue(op._has_cursor_param())
|
|
237
|
+
|
|
238
|
+
self.fake_op.parameters = [self.before_param]
|
|
239
|
+
op = self.op()
|
|
240
|
+
self.assertTrue(op._has_cursor_param())
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class EsiOperationTests(TestCase):
|
|
244
|
+
def setUp(self):
|
|
245
|
+
self.op_mock = MagicMock()
|
|
246
|
+
self.op_mock.parameters = []
|
|
247
|
+
self.op_mock.tags = ["tag"]
|
|
248
|
+
self.op_mock.operationId = "opid"
|
|
249
|
+
|
|
250
|
+
self.api_mock = MagicMock()
|
|
251
|
+
self.api_mock.app_name = "TestApp"
|
|
252
|
+
|
|
253
|
+
self.op = oc.EsiOperation(
|
|
254
|
+
(
|
|
255
|
+
"GET",
|
|
256
|
+
"/url",
|
|
257
|
+
self.op_mock,
|
|
258
|
+
{}
|
|
259
|
+
),
|
|
260
|
+
self.api_mock
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
@patch.object(oc.EsiOperation, "_make_request")
|
|
264
|
+
def test_result_and_results(self, mock_make_request):
|
|
265
|
+
data = {"data": "stuff"}
|
|
266
|
+
mock_resp = MagicMock(status_code=200, headers={"Expires": "Wed, 1 July 2099 11:00:00 GMT"})
|
|
267
|
+
mock_make_request.return_value = ({"Expires": "date"}, data, mock_resp)
|
|
268
|
+
data_resp = self.op(foo="bar").result()
|
|
269
|
+
self.assertEqual(data, data_resp)
|
|
5
270
|
|
|
6
271
|
|
|
7
272
|
class TestOpenapiClientProvider(TestCase):
|
|
@@ -12,3 +277,139 @@ class TestOpenapiClientProvider(TestCase):
|
|
|
12
277
|
|
|
13
278
|
self.assertEqual("2024-01-01", ESIClientProvider._date_to_string(testdate_1))
|
|
14
279
|
self.assertEqual("2025-08-26", ESIClientProvider._date_to_string(testdate_2))
|
|
280
|
+
|
|
281
|
+
@patch.object(httpx.Client, "send")
|
|
282
|
+
def test_ua(self, send: MagicMock):
|
|
283
|
+
app_name = "TestsApp"
|
|
284
|
+
app_ver = "1.2.3"
|
|
285
|
+
app_url = "https://tests.pass"
|
|
286
|
+
esi = ESIClientProvider(
|
|
287
|
+
ua_appname=app_name,
|
|
288
|
+
ua_url=app_url,
|
|
289
|
+
ua_version=app_ver,
|
|
290
|
+
compatibility_date="2020-01-01",
|
|
291
|
+
tags=["Status"],
|
|
292
|
+
spec_file=SPEC_PATH
|
|
293
|
+
)
|
|
294
|
+
cache.clear()
|
|
295
|
+
|
|
296
|
+
send.return_value = httpx.Response(
|
|
297
|
+
200,
|
|
298
|
+
json={
|
|
299
|
+
"players": 1234,
|
|
300
|
+
"server_version": "1234",
|
|
301
|
+
"start_time": "2029-09-19T11:02:08Z"
|
|
302
|
+
},
|
|
303
|
+
request=httpx.Request("GET", "test"),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
status = esi.client.Status.GetStatus().result()
|
|
307
|
+
call_args, call_kwargs = send.call_args
|
|
308
|
+
|
|
309
|
+
expected_app_name = "TestsApp"
|
|
310
|
+
expected_title = 'DjangoEsi'
|
|
311
|
+
|
|
312
|
+
self.assertEqual(
|
|
313
|
+
call_args[0].headers["user-agent"],
|
|
314
|
+
(
|
|
315
|
+
f"{expected_app_name}/{app_ver} "
|
|
316
|
+
f"({app_settings.ESI_USER_CONTACT_EMAIL}{f'; +{app_url})'} "
|
|
317
|
+
f"{expected_title}/{__version__} (+{__url__})"
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
self.assertEqual(status.players, 1234)
|
|
321
|
+
|
|
322
|
+
@patch.object(httpx.Client, "send")
|
|
323
|
+
def test_etag_hit_cached(self, send: MagicMock):
|
|
324
|
+
app_name = "TestsApp"
|
|
325
|
+
app_ver = "1.2.3"
|
|
326
|
+
app_url = "https://tests.pass"
|
|
327
|
+
etag = "'123456789abcdef123456789abcdef'"
|
|
328
|
+
esi = ESIClientProvider(
|
|
329
|
+
ua_appname=app_name,
|
|
330
|
+
ua_url=app_url,
|
|
331
|
+
ua_version=app_ver,
|
|
332
|
+
compatibility_date="2020-01-01",
|
|
333
|
+
tags=["Status"],
|
|
334
|
+
spec_file=SPEC_PATH
|
|
335
|
+
)
|
|
336
|
+
cache.clear()
|
|
337
|
+
|
|
338
|
+
expires = (
|
|
339
|
+
timezone.now() + timedelta(minutes=5)
|
|
340
|
+
).strftime('%a, %d %b %Y %H:%M:%S %Z')
|
|
341
|
+
|
|
342
|
+
send.return_value = httpx.Response(
|
|
343
|
+
200,
|
|
344
|
+
json={
|
|
345
|
+
"players": 1234,
|
|
346
|
+
"server_version": "1234",
|
|
347
|
+
"start_time": "2029-09-19T11:02:08Z"
|
|
348
|
+
},
|
|
349
|
+
headers={
|
|
350
|
+
"etag": etag,
|
|
351
|
+
"expires": expires
|
|
352
|
+
},
|
|
353
|
+
request=httpx.Request(
|
|
354
|
+
"GET",
|
|
355
|
+
"test",
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
esi.client.Status.GetStatus().result()
|
|
360
|
+
|
|
361
|
+
with self.assertRaises(HTTPNotModified):
|
|
362
|
+
esi.client.Status.GetStatus().result()
|
|
363
|
+
|
|
364
|
+
@patch.object(httpx.Client, "send")
|
|
365
|
+
def test_etag_hit_external(self, send: MagicMock):
|
|
366
|
+
app_name = "TestsApp"
|
|
367
|
+
app_ver = "1.2.3"
|
|
368
|
+
app_url = "https://tests.pass"
|
|
369
|
+
etag = "'123456789abcdef123456789abcdef'"
|
|
370
|
+
esi = ESIClientProvider(
|
|
371
|
+
ua_appname=app_name,
|
|
372
|
+
ua_url=app_url,
|
|
373
|
+
ua_version=app_ver,
|
|
374
|
+
compatibility_date="2020-01-01",
|
|
375
|
+
tags=["Status"],
|
|
376
|
+
spec_file=SPEC_PATH
|
|
377
|
+
)
|
|
378
|
+
cache.clear()
|
|
379
|
+
expires = (
|
|
380
|
+
timezone.now() + timedelta(minutes=5)
|
|
381
|
+
).strftime('%a, %d %b %Y %H:%M:%S %Z')
|
|
382
|
+
|
|
383
|
+
send.return_value = httpx.Response(
|
|
384
|
+
200,
|
|
385
|
+
json={
|
|
386
|
+
"players": 1234,
|
|
387
|
+
"server_version": "1234",
|
|
388
|
+
"start_time": "2029-09-19T11:02:08Z"
|
|
389
|
+
},
|
|
390
|
+
headers={
|
|
391
|
+
"etag": etag,
|
|
392
|
+
"expires": expires
|
|
393
|
+
},
|
|
394
|
+
request=httpx.Request(
|
|
395
|
+
"GET",
|
|
396
|
+
"test",
|
|
397
|
+
),
|
|
398
|
+
)
|
|
399
|
+
esi.client.Status.GetStatus().result()
|
|
400
|
+
|
|
401
|
+
cache.delete(esi.client.Status.GetStatus()._cache_key())
|
|
402
|
+
|
|
403
|
+
send.return_value = httpx.Response(
|
|
404
|
+
304,
|
|
405
|
+
headers={
|
|
406
|
+
"etag": etag,
|
|
407
|
+
"expires": expires
|
|
408
|
+
},
|
|
409
|
+
request=httpx.Request(
|
|
410
|
+
"GET",
|
|
411
|
+
"test",
|
|
412
|
+
),
|
|
413
|
+
)
|
|
414
|
+
with self.assertRaises(HTTPNotModified):
|
|
415
|
+
esi.client.Status.GetStatus().result()
|
|
File without changes
|
|
File without changes
|