mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-03 15:52:51 +00:00
move tests to api mocking to remove jitter in ci (#10447)
closes #10446
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
"""Unit tests for base mixins for plugins."""
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.urls import include, path, re_path
|
||||
|
||||
import requests_mock
|
||||
|
||||
from InvenTree.unit_test import InvenTreeTestCase
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.helpers import MixinNotImplementedError
|
||||
@@ -205,12 +205,12 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
'API_TOKEN': {
|
||||
'name': 'API Token',
|
||||
'protected': True,
|
||||
'default': 'reqres-free-v1',
|
||||
'default': 'sample-free-v1',
|
||||
},
|
||||
'API_URL': {
|
||||
'name': 'External URL',
|
||||
'description': 'Where is your API located?',
|
||||
'default': 'https://api.github.com',
|
||||
'default': 'https://api.example.com',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
@property
|
||||
def api_url(self):
|
||||
"""Override API URL for this test."""
|
||||
return 'https://api.github.com'
|
||||
return 'https://api.example.com'
|
||||
|
||||
def get_external_url(self, simple: bool = True):
|
||||
"""Returns data from the sample endpoint."""
|
||||
@@ -229,13 +229,6 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
|
||||
self.mixin = MixinCls()
|
||||
|
||||
# If running in github workflow, make use of GITHUB_TOKEN
|
||||
if settings.TESTING:
|
||||
token = os.getenv('GITHUB_TOKEN', None)
|
||||
|
||||
if token:
|
||||
self.mixin.set_setting('API_TOKEN', token)
|
||||
|
||||
class WrongCLS(APICallMixin, InvenTreePlugin):
|
||||
pass
|
||||
|
||||
@@ -251,7 +244,7 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
# check init
|
||||
self.assertTrue(self.mixin.has_api_call)
|
||||
# api_url
|
||||
self.assertEqual('https://api.github.com', self.mixin.api_url)
|
||||
self.assertEqual('https://api.example.com', self.mixin.api_url)
|
||||
|
||||
# api_headers
|
||||
headers = self.mixin.api_headers
|
||||
@@ -273,9 +266,34 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
result = self.mixin.api_build_url_args({'a': 'b', 'c': ['d', 'efgh', 1337]})
|
||||
self.assertEqual(result, '?a=b&c=d,efgh,1337')
|
||||
|
||||
def test_api_call(self):
|
||||
@requests_mock.Mocker()
|
||||
def test_api_call(self, m: requests_mock.Mocker):
|
||||
"""Test that api calls work."""
|
||||
import time
|
||||
# Set up mock responses
|
||||
m.get(
|
||||
'https://api.example.com/orgs/inventree',
|
||||
json={
|
||||
'login': 'inventree',
|
||||
'email': 'inventree',
|
||||
'name': 'InvenTree',
|
||||
'twitter_username': 'inventree',
|
||||
},
|
||||
status_code=200,
|
||||
)
|
||||
m.post(
|
||||
'https://api.example.com/users/',
|
||||
json={'name': 'morpheus', 'job': 'leader'},
|
||||
status_code=201,
|
||||
headers={
|
||||
'Authorization': 'x-api-key sample-free-v1',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
)
|
||||
m.get(
|
||||
'https://api.example.com/repos/inventree/InvenTree/stargazers?page=2',
|
||||
json={'sample': True},
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
# api_call
|
||||
result = self.mixin.get_external_url()
|
||||
@@ -287,27 +305,17 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
# api_call without json conversion
|
||||
result = self.mixin.get_external_url(False)
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(result.reason, 'OK')
|
||||
self.assertTrue(result.ok)
|
||||
|
||||
# Set API TOKEN
|
||||
self.mixin.set_setting('API_TOKEN', 'reqres-free-v1')
|
||||
self.mixin.set_setting('API_TOKEN', 'sample-free-v1')
|
||||
# api_call with post and data
|
||||
|
||||
# Try multiple times, account for the rate limit
|
||||
result = None
|
||||
|
||||
for _ in range(5):
|
||||
try:
|
||||
result = self.mixin.api_call(
|
||||
'https://reqres.in/api/users/',
|
||||
json={'name': 'morpheus', 'job': 'leader'},
|
||||
method='POST',
|
||||
endpoint_is_url=True,
|
||||
timeout=5000,
|
||||
)
|
||||
break
|
||||
except Exception:
|
||||
time.sleep(1)
|
||||
result = self.mixin.api_call(
|
||||
'https://api.example.com/users/',
|
||||
json={'name': 'morpheus', 'job': 'leader'},
|
||||
method='POST',
|
||||
endpoint_is_url=True,
|
||||
)
|
||||
|
||||
self.assertTrue(result)
|
||||
self.assertNotIn('error', result)
|
||||
@@ -317,16 +325,21 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
# api_call with endpoint with leading slash
|
||||
result = self.mixin.api_call('/orgs/inventree', simple_response=False)
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(result.reason, 'OK')
|
||||
self.assertTrue(result.ok)
|
||||
|
||||
# api_call with filter
|
||||
# api_call with filter - this errors out the mocker if not created correctly
|
||||
result = self.mixin.api_call(
|
||||
'repos/inventree/InvenTree/stargazers', url_args={'page': '2'}
|
||||
)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_function_errors(self):
|
||||
@requests_mock.Mocker()
|
||||
def test_function_errors(self, m: requests_mock.Mocker):
|
||||
"""Test function errors."""
|
||||
# Set up mock responses
|
||||
m.get('https://api.example.com/orgs/inventree', status_code=404)
|
||||
m.post('https://api.example.com/api/users/', status_code=400)
|
||||
|
||||
# wrongly defined plugins should not load
|
||||
with self.assertRaises(MixinNotImplementedError):
|
||||
self.mixin_wrong.has_api_call()
|
||||
@@ -338,12 +351,12 @@ class APICallMixinTest(BaseMixinDefinition, TestCase):
|
||||
# Too many data arguments
|
||||
with self.assertRaises(ValueError):
|
||||
self.mixin.api_call(
|
||||
'https://reqres.in/api/users/', json={'a': 1}, data={'a': 1}
|
||||
'https://api.example.com/api/users/', json={'a': 1}, data={'a': 1}
|
||||
)
|
||||
|
||||
# Sending a request with a wrong data format should result in 40
|
||||
# Sending a request with a wrong data format should result in 400
|
||||
result = self.mixin.api_call(
|
||||
'https://reqres.in/api/users/',
|
||||
'https://api.example.com/api/users/',
|
||||
data={'name': 'morpheus', 'job': 'leader'},
|
||||
method='POST',
|
||||
endpoint_is_url=True,
|
||||
|
@@ -18,7 +18,7 @@ class SampleApiCallerPlugin(APICallMixin, SettingsMixin, InvenTreePlugin):
|
||||
'API_URL': {
|
||||
'name': 'External URL',
|
||||
'description': 'Where is your API located?',
|
||||
'default': 'reqres.in',
|
||||
'default': 'api.example.com',
|
||||
},
|
||||
}
|
||||
API_URL_SETTING = 'API_URL'
|
||||
|
@@ -2,15 +2,19 @@
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
import requests_mock
|
||||
|
||||
from plugin import registry
|
||||
|
||||
|
||||
class SampleApiCallerPluginTests(TestCase):
|
||||
"""Tests for SampleApiCallerPluginTests."""
|
||||
|
||||
def test_return(self):
|
||||
@requests_mock.Mocker()
|
||||
def test_return(self, m):
|
||||
"""Check if the external api call works."""
|
||||
import time
|
||||
# Set up mock responses
|
||||
m.get('https://api.example.com/api/users/2', json={'data': 'sample'})
|
||||
|
||||
# The plugin should be defined
|
||||
self.assertIn('sample-api-caller', registry.plugins)
|
||||
@@ -18,15 +22,7 @@ class SampleApiCallerPluginTests(TestCase):
|
||||
self.assertTrue(plg)
|
||||
|
||||
# do an api call
|
||||
# Note: rate limits may apply in CI
|
||||
result = False
|
||||
|
||||
for _i in range(5):
|
||||
result = plg.get_external_url()
|
||||
if result:
|
||||
break
|
||||
else:
|
||||
time.sleep(1)
|
||||
result = plg.get_external_url()
|
||||
|
||||
self.assertTrue(result)
|
||||
self.assertIn('data', result)
|
||||
|
@@ -12,3 +12,4 @@ pdfminer.six # PDF validation
|
||||
ty # type checking
|
||||
django-types # typing
|
||||
django-stubs # typing
|
||||
requests-mock # Mock requests for unit tests
|
||||
|
@@ -11,6 +11,12 @@ build==1.3.0 \
|
||||
--hash=sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397 \
|
||||
--hash=sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4
|
||||
# via pip-tools
|
||||
certifi==2025.8.3 \
|
||||
--hash=sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407 \
|
||||
--hash=sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
cffi==1.17.1 \
|
||||
--hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \
|
||||
--hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \
|
||||
@@ -182,6 +188,7 @@ charset-normalizer==3.4.2 \
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# pdfminer-six
|
||||
# requests
|
||||
click==8.1.8 \
|
||||
--hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \
|
||||
--hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a
|
||||
@@ -359,6 +366,12 @@ identify==2.6.12 \
|
||||
--hash=sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2 \
|
||||
--hash=sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6
|
||||
# via pre-commit
|
||||
idna==3.10 \
|
||||
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
|
||||
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
importlib-metadata==8.7.0 \
|
||||
--hash=sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000 \
|
||||
--hash=sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd
|
||||
@@ -470,6 +483,16 @@ pyyaml==6.0.2 \
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# pre-commit
|
||||
requests==2.32.5 \
|
||||
--hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \
|
||||
--hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests-mock
|
||||
requests-mock==1.12.1 \
|
||||
--hash=sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563 \
|
||||
--hash=sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401
|
||||
# via -r src/backend/requirements-dev.in
|
||||
setuptools==80.9.0 \
|
||||
--hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 \
|
||||
--hash=sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c
|
||||
@@ -559,6 +582,12 @@ typing-extensions==4.14.1 \
|
||||
# django-stubs
|
||||
# django-stubs-ext
|
||||
# django-test-migrations
|
||||
urllib3==1.26.20 \
|
||||
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
|
||||
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
virtualenv==20.33.1 \
|
||||
--hash=sha256:07c19bc66c11acab6a5958b815cbcee30891cd1c2ccf53785a28651a0d8d8a67 \
|
||||
--hash=sha256:1b44478d9e261b3fb8baa5e74a0ca3bc0e05f21aa36167bf9cbf850e542765b8
|
||||
|
Reference in New Issue
Block a user