Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 2ae3876

Browse files
committed
Skip unit tests which require optional dependencies
If we are lacking an optional dependency, skip the tests that rely on it.
1 parent 8f08021 commit 2ae3876

File tree

7 files changed

+90
-13
lines changed

7 files changed

+90
-13
lines changed

changelog.d/9031.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix running unit tests when optional dependencies are not installed.

tests/handlers/test_oidc.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from twisted.web.resource import Resource
2525

2626
from synapse.api.errors import RedirectException
27-
from synapse.handlers.oidc_handler import OidcError
2827
from synapse.handlers.sso import MappingException
2928
from synapse.rest.client.v1 import login
3029
from synapse.rest.synapse.client.pick_username import pick_username_resource
@@ -34,6 +33,14 @@
3433
from tests.test_utils import FakeResponse, simple_async_mock
3534
from tests.unittest import HomeserverTestCase, override_config
3635

36+
try:
37+
import authlib # noqa: F401
38+
39+
HAS_OIDC = True
40+
except ImportError:
41+
HAS_OIDC = False
42+
43+
3744
# These are a few constants that are used as config parameters in the tests.
3845
ISSUER = "https://issuer/"
3946
CLIENT_ID = "test-client-id"
@@ -113,6 +120,9 @@ async def get_json(url):
113120

114121

115122
class OidcHandlerTestCase(HomeserverTestCase):
123+
if not HAS_OIDC:
124+
skip = "requires OIDC"
125+
116126
def default_config(self):
117127
config = super().default_config()
118128
config["public_baseurl"] = BASE_URL
@@ -458,6 +468,8 @@ def test_callback(self):
458468
self.assertRenderedError("fetch_error")
459469

460470
# Handle code exchange failure
471+
from synapse.handlers.oidc_handler import OidcError
472+
461473
self.handler._exchange_code = simple_async_mock(
462474
raises=OidcError("invalid_request")
463475
)
@@ -538,6 +550,8 @@ def test_exchange_code(self):
538550
body=b'{"error": "foo", "error_description": "bar"}',
539551
)
540552
)
553+
from synapse.handlers.oidc_handler import OidcError
554+
541555
exc = self.get_failure(self.handler._exchange_code(code), OidcError)
542556
self.assertEqual(exc.value.error, "foo")
543557
self.assertEqual(exc.value.error_description, "bar")
@@ -829,6 +843,9 @@ def test_null_localpart(self):
829843

830844

831845
class UsernamePickerTestCase(HomeserverTestCase):
846+
if not HAS_OIDC:
847+
skip = "requires OIDC"
848+
832849
servlets = [login.register_servlets]
833850

834851
def default_config(self):

tests/rest/client/v1/test_login.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
from mock import Mock
66

7-
import jwt
7+
try:
8+
import jwt
9+
except ImportError:
10+
jwt = None
811

912
import synapse.rest.admin
1013
from synapse.appservice import ApplicationService
@@ -460,6 +463,9 @@ def test_deactivated_user(self):
460463

461464

462465
class JWTTestCase(unittest.HomeserverTestCase):
466+
if not jwt:
467+
skip = "requires jwt"
468+
463469
servlets = [
464470
synapse.rest.admin.register_servlets_for_client_rest_resource,
465471
login.register_servlets,
@@ -628,6 +634,9 @@ def test_login_no_token(self):
628634
# RSS256, with a public key configured in synapse as "jwt_secret", and tokens
629635
# signed by the private key.
630636
class JWTPubKeyTestCase(unittest.HomeserverTestCase):
637+
if not jwt:
638+
skip = "requires jwt"
639+
631640
servlets = [
632641
login.register_servlets,
633642
]

tests/rest/client/v2_alpha/test_auth.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
from synapse.types import JsonDict, UserID
2727

2828
from tests import unittest
29+
from tests.handlers.test_oidc import HAS_OIDC
2930
from tests.rest.client.v1.utils import TEST_OIDC_CONFIG
3031
from tests.server import FakeChannel
32+
from tests.unittest import override_config, skip_unless
3133

3234

3335
class DummyRecaptchaChecker(UserInteractiveAuthChecker):
@@ -158,20 +160,22 @@ class UIAuthTests(unittest.HomeserverTestCase):
158160

159161
def default_config(self):
160162
config = super().default_config()
163+
config["public_baseurl"] = "https://synapse.test"
161164

162-
# we enable OIDC as a way of testing SSO flows
163-
oidc_config = {}
164-
oidc_config.update(TEST_OIDC_CONFIG)
165-
oidc_config["allow_existing_users"] = True
165+
if HAS_OIDC:
166+
# we enable OIDC as a way of testing SSO flows
167+
oidc_config = {}
168+
oidc_config.update(TEST_OIDC_CONFIG)
169+
oidc_config["allow_existing_users"] = True
170+
config["oidc_config"] = oidc_config
166171

167-
config["oidc_config"] = oidc_config
168-
config["public_baseurl"] = "https://synapse.test"
169172
return config
170173

171174
def create_resource_dict(self):
172175
resource_dict = super().create_resource_dict()
173-
# mount the OIDC resource at /_synapse/oidc
174-
resource_dict["/_synapse/oidc"] = OIDCResource(self.hs)
176+
if HAS_OIDC:
177+
# mount the OIDC resource at /_synapse/oidc
178+
resource_dict["/_synapse/oidc"] = OIDCResource(self.hs)
175179
return resource_dict
176180

177181
def prepare(self, reactor, clock, hs):
@@ -380,6 +384,8 @@ def test_can_reuse_session(self):
380384
# Note that *no auth* information is provided, not even a session iD!
381385
self.delete_device(self.user_tok, self.device_id, 200)
382386

387+
@skip_unless(HAS_OIDC, "requires OIDC")
388+
@override_config({"oidc_config": TEST_OIDC_CONFIG})
383389
def test_does_not_offer_password_for_sso_user(self):
384390
login_resp = self.helper.login_via_oidc("username")
385391
user_tok = login_resp["access_token"]
@@ -393,13 +399,13 @@ def test_does_not_offer_password_for_sso_user(self):
393399
self.assertEqual(flows, [{"stages": ["m.login.sso"]}])
394400

395401
def test_does_not_offer_sso_for_password_user(self):
396-
# now call the device deletion API: we should get the option to auth with SSO
397-
# and not password.
398402
channel = self.delete_device(self.user_tok, self.device_id, 401)
399403

400404
flows = channel.json_body["flows"]
401405
self.assertEqual(flows, [{"stages": ["m.login.password"]}])
402406

407+
@skip_unless(HAS_OIDC, "requires OIDC")
408+
@override_config({"oidc_config": TEST_OIDC_CONFIG})
403409
def test_offers_both_flows_for_upgraded_user(self):
404410
"""A user that had a password and then logged in with SSO should get both flows
405411
"""

tests/rest/media/v1/test_url_preview.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@
2626
from tests import unittest
2727
from tests.server import FakeTransport
2828

29+
try:
30+
import lxml
31+
except ImportError:
32+
lxml = None
33+
2934

3035
class URLPreviewTests(unittest.HomeserverTestCase):
36+
if not lxml:
37+
skip = "url preview feature requires lxml"
3138

3239
hijack_auth = True
3340
user_id = "@test:user"

tests/test_preview.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@
2020

2121
from . import unittest
2222

23+
try:
24+
import lxml
25+
except ImportError:
26+
lxml = None
27+
2328

2429
class PreviewTestCase(unittest.TestCase):
30+
if not lxml:
31+
skip = "url preview feature requires lxml"
32+
2533
def test_long_summarize(self):
2634
example_paras = [
2735
"""Tromsø (Norwegian pronunciation: [ˈtrʊmsœ] ( listen); Northern Sami:
@@ -137,6 +145,9 @@ def test_small_then_large_summarize(self):
137145

138146

139147
class PreviewUrlTestCase(unittest.TestCase):
148+
if not lxml:
149+
skip = "url preview feature requires lxml"
150+
140151
def test_simple(self):
141152
html = """
142153
<html>

tests/unittest.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import inspect
2121
import logging
2222
import time
23-
from typing import Dict, Iterable, Optional, Tuple, Type, TypeVar, Union
23+
from typing import Callable, Dict, Iterable, Optional, Tuple, Type, TypeVar, Union
2424

2525
from mock import Mock, patch
2626

@@ -736,3 +736,29 @@ def decorator(func):
736736
return func
737737

738738
return decorator
739+
740+
741+
TV = TypeVar("TV")
742+
743+
744+
def skip_unless(condition: bool, reason: str) -> Callable[[TV], TV]:
745+
"""A test decorator which will skip the decorated test unless a condition is set
746+
747+
For example:
748+
749+
class MyTestCase(TestCase):
750+
@skip_unless(HAS_FOO, "Cannot test without foo")
751+
def test_foo(self):
752+
...
753+
754+
Args:
755+
condition: If true, the test will be skipped
756+
reason: the reason to give for skipping the test
757+
"""
758+
759+
def decorator(f: TV) -> TV:
760+
if not condition:
761+
f.skip = reason # type: ignore
762+
return f
763+
764+
return decorator

0 commit comments

Comments
 (0)