django-esi 8.1.0__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.
Files changed (100) hide show
  1. django_esi-8.1.0.dist-info/METADATA +93 -0
  2. django_esi-8.1.0.dist-info/RECORD +100 -0
  3. django_esi-8.1.0.dist-info/WHEEL +4 -0
  4. django_esi-8.1.0.dist-info/licenses/LICENSE +674 -0
  5. esi/__init__.py +7 -0
  6. esi/admin.py +42 -0
  7. esi/aiopenapi3/client.py +79 -0
  8. esi/aiopenapi3/plugins.py +224 -0
  9. esi/app_settings.py +112 -0
  10. esi/apps.py +11 -0
  11. esi/checks.py +56 -0
  12. esi/clients.py +657 -0
  13. esi/decorators.py +271 -0
  14. esi/errors.py +22 -0
  15. esi/exceptions.py +51 -0
  16. esi/helpers.py +63 -0
  17. esi/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  18. esi/locale/cs_CZ/LC_MESSAGES/django.po +53 -0
  19. esi/locale/de/LC_MESSAGES/django.mo +0 -0
  20. esi/locale/de/LC_MESSAGES/django.po +58 -0
  21. esi/locale/en/LC_MESSAGES/django.mo +0 -0
  22. esi/locale/en/LC_MESSAGES/django.po +54 -0
  23. esi/locale/es/LC_MESSAGES/django.mo +0 -0
  24. esi/locale/es/LC_MESSAGES/django.po +59 -0
  25. esi/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  26. esi/locale/fr_FR/LC_MESSAGES/django.po +59 -0
  27. esi/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  28. esi/locale/it_IT/LC_MESSAGES/django.po +59 -0
  29. esi/locale/ja/LC_MESSAGES/django.mo +0 -0
  30. esi/locale/ja/LC_MESSAGES/django.po +58 -0
  31. esi/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  32. esi/locale/ko_KR/LC_MESSAGES/django.po +58 -0
  33. esi/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  34. esi/locale/nl_NL/LC_MESSAGES/django.po +53 -0
  35. esi/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  36. esi/locale/pl_PL/LC_MESSAGES/django.po +53 -0
  37. esi/locale/ru/LC_MESSAGES/django.mo +0 -0
  38. esi/locale/ru/LC_MESSAGES/django.po +61 -0
  39. esi/locale/sk/LC_MESSAGES/django.mo +0 -0
  40. esi/locale/sk/LC_MESSAGES/django.po +55 -0
  41. esi/locale/uk/LC_MESSAGES/django.mo +0 -0
  42. esi/locale/uk/LC_MESSAGES/django.po +57 -0
  43. esi/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  44. esi/locale/zh_Hans/LC_MESSAGES/django.po +58 -0
  45. esi/management/commands/__init__.py +0 -0
  46. esi/management/commands/esi_clear_spec_cache.py +21 -0
  47. esi/management/commands/generate_esi_stubs.py +661 -0
  48. esi/management/commands/migrate_to_ssov2.py +188 -0
  49. esi/managers.py +303 -0
  50. esi/managers.pyi +85 -0
  51. esi/migrations/0001_initial.py +55 -0
  52. esi/migrations/0002_scopes_20161208.py +56 -0
  53. esi/migrations/0003_hide_tokens_from_admin_site.py +23 -0
  54. esi/migrations/0004_remove_unique_access_token.py +18 -0
  55. esi/migrations/0005_remove_token_length_limit.py +23 -0
  56. esi/migrations/0006_remove_url_length_limit.py +18 -0
  57. esi/migrations/0007_fix_mysql_8_migration.py +18 -0
  58. esi/migrations/0008_nullable_refresh_token.py +18 -0
  59. esi/migrations/0009_set_old_tokens_to_sso_v1.py +18 -0
  60. esi/migrations/0010_set_new_tokens_to_sso_v2.py +18 -0
  61. esi/migrations/0011_add_token_indices.py +28 -0
  62. esi/migrations/0012_fix_token_type_choices.py +18 -0
  63. esi/migrations/0013_squashed_0012_fix_token_type_choices.py +57 -0
  64. esi/migrations/__init__.py +0 -0
  65. esi/models.py +349 -0
  66. esi/openapi_clients.py +1225 -0
  67. esi/rate_limiting.py +107 -0
  68. esi/signals.py +21 -0
  69. esi/static/esi/img/EVE_SSO_Login_Buttons_Large_Black.png +0 -0
  70. esi/static/esi/img/EVE_SSO_Login_Buttons_Large_White.png +0 -0
  71. esi/static/esi/img/EVE_SSO_Login_Buttons_Small_Black.png +0 -0
  72. esi/static/esi/img/EVE_SSO_Login_Buttons_Small_White.png +0 -0
  73. esi/stubs.py +2 -0
  74. esi/stubs.pyi +6807 -0
  75. esi/tasks.py +78 -0
  76. esi/templates/esi/select_token.html +116 -0
  77. esi/templatetags/__init__.py +0 -0
  78. esi/templatetags/scope_tags.py +8 -0
  79. esi/tests/__init__.py +134 -0
  80. esi/tests/client_authed_pilot.py +63 -0
  81. esi/tests/client_public_pilot.py +53 -0
  82. esi/tests/factories.py +47 -0
  83. esi/tests/factories_2.py +60 -0
  84. esi/tests/jwt_factory.py +135 -0
  85. esi/tests/test_checks.py +48 -0
  86. esi/tests/test_clients.py +1019 -0
  87. esi/tests/test_decorators.py +578 -0
  88. esi/tests/test_management_command.py +307 -0
  89. esi/tests/test_managers.py +673 -0
  90. esi/tests/test_models.py +403 -0
  91. esi/tests/test_openapi.json +854 -0
  92. esi/tests/test_openapi.py +1017 -0
  93. esi/tests/test_swagger.json +489 -0
  94. esi/tests/test_swagger_full.json +51112 -0
  95. esi/tests/test_tasks.py +116 -0
  96. esi/tests/test_templatetags.py +22 -0
  97. esi/tests/test_views.py +331 -0
  98. esi/tests/threading_pilot.py +69 -0
  99. esi/urls.py +9 -0
  100. esi/views.py +129 -0
@@ -0,0 +1,403 @@
1
+ """unit tests for esi"""
2
+
3
+ from datetime import timedelta
4
+ from unittest.mock import patch, Mock
5
+
6
+ from django.core.exceptions import ImproperlyConfigured
7
+ from django.contrib.auth.models import User
8
+ from django.utils import timezone
9
+ from django.test import TestCase
10
+
11
+ from oauthlib.oauth2.rfc6749.errors import InvalidGrantError, \
12
+ MissingTokenError, InvalidClientError, InvalidTokenError, \
13
+ InvalidClientIdError
14
+
15
+ from esi.errors import TokenInvalidError, NotRefreshableTokenError, \
16
+ TokenExpiredError, IncompleteResponseError, TokenError
17
+ from esi.models import Token
18
+
19
+ from . import _generate_token, _store_as_Token
20
+ from .factories_2 import CallbackRedirectFactory, ScopeFactory, TokenFactory
21
+
22
+
23
+ MODULE_PATH = "esi.models"
24
+
25
+
26
+ class TestScope(TestCase):
27
+ def test_should_return_str(self):
28
+ # given
29
+ obj = ScopeFactory.build(name="dummy_scope")
30
+ # when/then
31
+ self.assertEqual(str(obj), "dummy_scope")
32
+
33
+ def test_should_return_friendly_name_1(self):
34
+ # given
35
+ obj = ScopeFactory.build(name="dummy_scope")
36
+ # when/then
37
+ self.assertEqual(obj.friendly_name, "dummy_scope")
38
+
39
+ def test_should_return_friendly_name_2(self):
40
+ # given
41
+ obj = ScopeFactory.build(name="test.dummy_scope.test")
42
+ # when/then
43
+ self.assertEqual(obj.friendly_name, "dummy scope")
44
+
45
+
46
+ class TestToken2(TestCase):
47
+ def test_should_return_str(self):
48
+ # given
49
+ scope = ScopeFactory(name="dummy_scope")
50
+ token = TokenFactory(character_name="Bruce Wayne", scopes=[scope])
51
+ # when/then
52
+ self.assertEqual(str(token), "Bruce Wayne - dummy_scope")
53
+
54
+ def test_should_return_str_not_created(self):
55
+ # given
56
+ token = TokenFactory.build(character_name="Bruce Wayne")
57
+ # when/then
58
+ self.assertEqual(str(token), "Bruce Wayne - ")
59
+
60
+ def test_should_return_repr(self):
61
+ # given
62
+ scope = ScopeFactory(name="dummy_scope")
63
+ token = TokenFactory(
64
+ id=99, character_id=1001, character_name="Bruce Wayne", scopes=[scope]
65
+ )
66
+ # when/then
67
+ self.assertEqual(repr(token), "<Token(id=99): 1001, Bruce Wayne>")
68
+
69
+ def test_should_find_token(self):
70
+ # given
71
+ scope = ScopeFactory(name="dummy_scope")
72
+ token = TokenFactory(character_id=1001, scopes=[scope])
73
+ TokenFactory()
74
+ # when
75
+ result = Token.get_token(1001, ['dummy_scope'])
76
+ # then
77
+ self.assertEqual(token, result)
78
+
79
+ def test_should_return_false_when_token_not_found(self):
80
+ # given
81
+ scope = ScopeFactory(name="dummy_scope")
82
+ TokenFactory(character_id=1001, scopes=[scope])
83
+ # when
84
+ result = Token.get_token(1001, ['unknown'])
85
+ # then
86
+ self.assertFalse(result)
87
+
88
+ def test_should_be_able_to_refresh(self):
89
+ # given
90
+ token = TokenFactory.build()
91
+ # when/then
92
+ self.assertTrue(token.can_refresh)
93
+
94
+ def test_should_not_be_able_to_refresh(self):
95
+ # given
96
+ token = TokenFactory.build(refresh_token=None)
97
+ # when/then
98
+ self.assertFalse(token.can_refresh)
99
+
100
+
101
+ @patch(MODULE_PATH + ".Token.refresh", spec=True)
102
+ class TestTokenRefreshOrDelete(TestCase):
103
+ def test_should_refresh_token(self, mock_token_refresh):
104
+ # given
105
+ token = TokenFactory()
106
+ # when
107
+ token.refresh_or_delete()
108
+ # then
109
+ self.assertTrue(mock_token_refresh.called)
110
+ self.assertTrue(Token.objects.filter(pk=token.pk).exists())
111
+
112
+ def test_should_delete_token_with_errors(self, mock_token_refresh):
113
+ # given
114
+ mock_token_refresh.side_effect = TokenError
115
+ token = TokenFactory()
116
+ # when
117
+ token.refresh_or_delete()
118
+ # then
119
+ self.assertTrue(mock_token_refresh.called)
120
+ self.assertFalse(Token.objects.filter(pk=token.pk).exists())
121
+
122
+ def test_should_not_delete_token_with_incomplete_response_error(self, mock_token_refresh):
123
+ # given
124
+ mock_token_refresh.side_effect = IncompleteResponseError
125
+ token = TokenFactory()
126
+ # when
127
+ with self.assertRaises(IncompleteResponseError):
128
+ token.refresh_or_delete()
129
+ # then
130
+ self.assertTrue(mock_token_refresh.called)
131
+ self.assertTrue(Token.objects.filter(pk=token.pk).exists())
132
+
133
+
134
+ class TestToken(TestCase):
135
+ def setUp(self):
136
+
137
+ character_id = 1000
138
+ character_name = 'Bruce Wayne'
139
+
140
+ self.user = User.objects.create_user(
141
+ character_name,
142
+ 'abc@example.com',
143
+ 'password'
144
+ )
145
+ self.token = _store_as_Token(
146
+ _generate_token(
147
+ character_id=character_id,
148
+ character_name=character_name,
149
+ scopes=['esi-universe.read_structures.v1']
150
+ ),
151
+ self.user
152
+ )
153
+
154
+ @patch(MODULE_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
155
+ def test_expires(self):
156
+ self.assertEqual(
157
+ self.token.created + timedelta(seconds=120),
158
+ self.token.expires
159
+ )
160
+
161
+ @patch(MODULE_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
162
+ def test_not_expired(self):
163
+ self.assertFalse(self.token.expired)
164
+
165
+ @patch(MODULE_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
166
+ def test_has_expired(self):
167
+ self.token.created -= timedelta(121)
168
+ self.assertTrue(self.token.expired)
169
+
170
+ def test_refresh_normal_1(self):
171
+ mock_auth = Mock()
172
+ mock_session = Mock()
173
+ mock_session.refresh_token.return_value = {
174
+ 'access_token': 'access_token_2',
175
+ 'refresh_token': 'refresh_token_2'
176
+ }
177
+
178
+ self.token.refresh(mock_session, mock_auth)
179
+ self.assertEqual(
180
+ self.token.refresh_token, 'refresh_token_2'
181
+ )
182
+ self.assertEqual(
183
+ self.token.access_token, 'access_token_2'
184
+ )
185
+ self.assertGreaterEqual(
186
+ self.token.created, timezone.now() - timedelta(seconds=60)
187
+ )
188
+
189
+ @patch(MODULE_PATH + '.HTTPBasicAuth', autospec=True)
190
+ @patch(MODULE_PATH + '.OAuth2Session', autospec=True)
191
+ def test_refresh_normal_2(self, mock_OAuth2Session, mock_HTTPBasicAuth):
192
+ mock_session = Mock()
193
+ mock_session.refresh_token.return_value = {
194
+ 'access_token': 'access_token_2',
195
+ 'refresh_token': 'refresh_token_2'
196
+ }
197
+ mock_OAuth2Session.return_value = mock_session
198
+
199
+ self.token.refresh()
200
+ self.assertEqual(
201
+ self.token.refresh_token,
202
+ 'refresh_token_2'
203
+ )
204
+ self.assertEqual(
205
+ self.token.access_token,
206
+ 'access_token_2'
207
+ )
208
+ self.assertGreaterEqual(
209
+ self.token.created,
210
+ timezone.now() - timedelta(seconds=60))
211
+
212
+ def test_valid_access_token(self):
213
+ self.assertFalse(self.token.expired)
214
+ self.assertEqual(self.token.valid_access_token(), 'access_token')
215
+
216
+ @patch(MODULE_PATH + '.HTTPBasicAuth', autospec=True)
217
+ @patch(MODULE_PATH + '.OAuth2Session', autospec=True)
218
+ @patch(MODULE_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
219
+ def test_valid_access_token_refresh(self, mock_OAuth2Session, mock_HTTPBasicAuth):
220
+ mock_session = Mock()
221
+ mock_session.refresh_token.return_value = {
222
+ 'access_token': 'access_token_new',
223
+ 'refresh_token': 'refresh_token_2'
224
+ }
225
+ mock_OAuth2Session.return_value = mock_session
226
+
227
+ self.token.created -= timedelta(121)
228
+ self.assertTrue(self.token.expired)
229
+ self.assertEqual(
230
+ self.token.valid_access_token(), 'access_token_new')
231
+
232
+ @patch(MODULE_PATH + '.HTTPBasicAuth', autospec=True)
233
+ @patch(MODULE_PATH + '.OAuth2Session', autospec=True)
234
+ @patch(MODULE_PATH + '.app_settings.ESI_TOKEN_VALID_DURATION', 120)
235
+ def test_valid_access_token_cant_refresh(
236
+ self, mock_OAuth2Session, mock_HTTPBasicAuth
237
+ ):
238
+ self.token.refresh_token = None
239
+ self.token.created -= timedelta(121)
240
+ self.assertTrue(self.token.expired)
241
+ with self.assertRaises(TokenExpiredError):
242
+ self.token.valid_access_token()
243
+
244
+ def test_refresh_errors_1(self):
245
+ mock_auth = Mock()
246
+ mock_session = Mock()
247
+ mock_session.refresh_token.return_value = {
248
+ 'access_token': 'access_token_2',
249
+ 'refresh_token': 'refresh_token_2'
250
+ }
251
+ self.token.refresh_token = None
252
+ with self.assertRaises(NotRefreshableTokenError):
253
+ self.token.refresh(mock_session, mock_auth)
254
+
255
+ def test_refresh_errors_2(self):
256
+ mock_auth = Mock()
257
+ mock_session = Mock()
258
+
259
+ mock_session.refresh_token.side_effect = InvalidGrantError
260
+ with self.assertRaises(TokenInvalidError):
261
+ self.token.refresh(mock_session, mock_auth)
262
+
263
+ mock_session.refresh_token.side_effect = InvalidTokenError
264
+ with self.assertRaises(TokenInvalidError):
265
+ self.token.refresh(mock_session, mock_auth)
266
+
267
+ mock_session.refresh_token.side_effect = InvalidClientIdError
268
+ with self.assertRaises(TokenInvalidError):
269
+ self.token.refresh(mock_session, mock_auth)
270
+
271
+ mock_session.refresh_token.side_effect = MissingTokenError
272
+ with self.assertRaises(IncompleteResponseError):
273
+ self.token.refresh(mock_session, mock_auth)
274
+
275
+ mock_session.refresh_token.side_effect = InvalidClientError
276
+ with self.assertRaises(ImproperlyConfigured):
277
+ self.token.refresh(mock_session, mock_auth)
278
+
279
+ @patch(MODULE_PATH + '.esi_client_factory', autospec=True)
280
+ def test_get_esi_client(self, mock_esi_client):
281
+ mock_esi_client.return_value = "Johnny"
282
+ x = self.token.get_esi_client()
283
+ self.assertEqual(x, "Johnny")
284
+ self.assertEqual(mock_esi_client.call_count, 1)
285
+
286
+ """
287
+ @patch('esi.managers.TokenManager')
288
+ def test_get_token_data(self, mock_decode_jwt):
289
+ mock_decode_jwt._decode_jwt.return_value = \
290
+ _generate_token(
291
+ 99, 'Bruce Wayne', scopes=[
292
+ 'esi-calendar.read_calendar_events.v1',
293
+ 'esi-location.read_location.v1',
294
+ 'esi-location.read_ship_type.v1',
295
+ 'esi-unknown-scope'
296
+ ]
297
+ )
298
+ data = self.token.get_token_data(access_token='access_token_2')
299
+ self.assertEqual(data['name'], "Bruce Wayne")
300
+ """
301
+ @patch(MODULE_PATH + '.Token.get_token_data')
302
+ def test_update_token_data_normal_1(self, mock_get_token_data):
303
+ mock_get_token_data.return_value = _generate_token(99, 'Bruce Wayne')
304
+ self.token.update_token_data()
305
+ self.token.refresh_from_db()
306
+ self.assertEqual(
307
+ self.token.character_id,
308
+ 99
309
+ )
310
+ self.assertEqual(
311
+ self.token.character_name,
312
+ 'Bruce Wayne'
313
+ )
314
+ self.assertEqual(
315
+ self.token.character_owner_hash,
316
+ 'character_owner_hash'
317
+ )
318
+ self.assertEqual(
319
+ self.token.token_type,
320
+ 'character'
321
+ )
322
+
323
+ @patch(MODULE_PATH + '.HTTPBasicAuth', autospec=True)
324
+ @patch(MODULE_PATH + '.OAuth2Session', autospec=True)
325
+ @patch(MODULE_PATH + '.Token.get_token_data')
326
+ def test_update_token_data_normal_2(
327
+ self,
328
+ mock_get_token_data,
329
+ mock_OAuth2Session,
330
+ mock_HTTPBasicAuth
331
+ ):
332
+ mock_session = Mock()
333
+ mock_session.refresh_token.return_value = {
334
+ 'access_token': 'access_token_2',
335
+ 'refresh_token': 'refresh_token_2'
336
+ }
337
+ mock_OAuth2Session.return_value = mock_session
338
+
339
+ mock_get_token_data.return_value = {
340
+ 'character_id': 99,
341
+ 'name': 'CharacterName',
342
+ 'owner': 'CharacterOwnerHash',
343
+ 'token_type': 'character',
344
+ }
345
+ self.token.created -= timedelta(121)
346
+
347
+ self.token.update_token_data()
348
+ self.token.refresh_from_db()
349
+ self.assertEqual(
350
+ self.token.character_id,
351
+ 99
352
+ )
353
+
354
+ @patch(MODULE_PATH + '.Token.get_token_data')
355
+ def test_update_token_data_normal_3(self, mock_get_token_data):
356
+ mock_get_token_data.return_value = {
357
+ 'character_id': 99,
358
+ 'name': 'CharacterName',
359
+ 'owner': 'CharacterOwnerHash',
360
+ 'token_type': 'Character',
361
+ }
362
+ self.token.update_token_data(commit=False)
363
+ self.assertEqual(
364
+ self.token.character_id,
365
+ 99
366
+ )
367
+ self.assertEqual(
368
+ self.token.character_name,
369
+ 'CharacterName'
370
+ )
371
+ self.assertEqual(
372
+ self.token.character_owner_hash,
373
+ 'CharacterOwnerHash'
374
+ )
375
+ self.assertEqual(
376
+ self.token.token_type,
377
+ 'Character'
378
+ )
379
+
380
+ @patch(MODULE_PATH + '.Token.get_token_data', auto_spec=True, unsafe=True)
381
+ def test_update_token_data_error(
382
+ self,
383
+ mock_get_token_data
384
+ ):
385
+ self.token.refresh_token = None
386
+ self.token.created -= timedelta(121)
387
+ with self.assertRaises(TokenExpiredError):
388
+ self.token.update_token_data()
389
+
390
+
391
+ class TestCallbackRedirect(TestCase):
392
+ def test_should_return_str(self):
393
+ # given
394
+ cb = CallbackRedirectFactory.build(session_key="abc", url="/green/alpha")
395
+ # when/then
396
+ self.assertEqual("abc: /green/alpha", str(cb))
397
+
398
+ def test_should_return_repr(self):
399
+ # given
400
+ cb = CallbackRedirectFactory(session_key="abc", url="/green/alpha")
401
+ # when/then
402
+ expected = f"<CallbackRedirect(pk={cb.pk}): abc to /green/alpha>"
403
+ self.assertEqual(expected, repr(cb))