Convert additional tests to pytest
This commit is contained in:
@@ -1,2 +1,7 @@
|
||||
pytest==7.4.0
|
||||
pytest-cov==4.1.0
|
||||
pytest-mock==3.11.1 # https://github.com/pytest-dev/pytest-mock/
|
||||
pytest-socket==0.6.0 # https://github.com/miketheman/pytest-socket
|
||||
requests-mock==1.11.0 # https://github.com/jamielennox/requests-mock
|
||||
pytest-subtests==0.11.0 # https://github.com/pytest-dev/pytest-subtests
|
||||
pytest-icdiff==0.6 # https://pypi.org/project/pytest-icdiff/
|
||||
|
||||
@@ -27,6 +27,14 @@ POST = "POST"
|
||||
PUT = "PUT"
|
||||
|
||||
|
||||
def now() -> datetime:
|
||||
"""Returns the current UTC time.
|
||||
|
||||
This function enables simple mocking for freezing time.
|
||||
"""
|
||||
return datetime.utcnow()
|
||||
|
||||
|
||||
class DiscourseClient(object):
|
||||
"""Discourse API client"""
|
||||
|
||||
@@ -236,7 +244,7 @@ class DiscourseClient(object):
|
||||
????
|
||||
|
||||
"""
|
||||
suspend_until = (datetime.now() + timedelta(days=duration)).isoformat()
|
||||
suspend_until = (now() + timedelta(days=duration)).isoformat()
|
||||
return self._put(
|
||||
"/admin/users/{0}/suspend".format(userid),
|
||||
suspend_until=suspend_until,
|
||||
|
||||
+41
-9
@@ -1,48 +1,80 @@
|
||||
"""Test fixtures."""
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
from pydiscourse import client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def sso_secret():
|
||||
return "d836444a9e4084d5b224a60c208dce14"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def sso_nonce():
|
||||
return "cb68251eefb5211e58c00ff1395f0c0b"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def sso_payload():
|
||||
return "bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def sso_signature():
|
||||
return "2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def name():
|
||||
return "sam"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def username():
|
||||
return "samsam"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def external_id():
|
||||
return "hello123"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def email():
|
||||
return "test@test.com"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(scope="session")
|
||||
def redirect_url():
|
||||
return "/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A&sig=1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def discourse_host():
|
||||
return "http://testhost"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def discourse_api_username():
|
||||
return "testuser"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def discourse_api_key():
|
||||
return "testkey"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def discourse_client(discourse_host, discourse_api_username, discourse_api_key):
|
||||
return client.DiscourseClient(
|
||||
discourse_host, discourse_api_username, discourse_api_key
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def frozen_time(mocker):
|
||||
now = mocker.patch("pydiscourse.client.now")
|
||||
now.return_value = datetime.datetime(
|
||||
2023, 8, 13, 12, 30, 15, tzinfo=datetime.timezone.utc
|
||||
)
|
||||
|
||||
+123
-93
@@ -1,10 +1,131 @@
|
||||
import unittest
|
||||
|
||||
import urllib.parse
|
||||
from unittest import mock
|
||||
|
||||
from pydiscourse import client
|
||||
|
||||
|
||||
def test_empty_content_http_ok(discourse_host, discourse_client, requests_mock):
|
||||
"""Empty content should not raise error
|
||||
|
||||
Critical to test against *bytestrings* rather than unicode
|
||||
"""
|
||||
requests_mock.get(
|
||||
f"{discourse_host}/users/admin/1/unsuspend",
|
||||
headers={"Content-Type": "text/plain; charset=utf-8"},
|
||||
content=b" ",
|
||||
)
|
||||
|
||||
resp = discourse_client._request("GET", "/users/admin/1/unsuspend", {})
|
||||
|
||||
assert resp is None
|
||||
|
||||
|
||||
class TestUserManagement:
|
||||
def test_get_user(self, discourse_host, discourse_client, requests_mock):
|
||||
request = requests_mock.get(
|
||||
f"{discourse_host}/users/someuser.json",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={"user": "someuser"},
|
||||
)
|
||||
discourse_client.user("someuser")
|
||||
assert request.called_once
|
||||
|
||||
def test_create_user(self, discourse_host, discourse_client, requests_mock):
|
||||
session_request = requests_mock.get(
|
||||
f"{discourse_host}/session/hp.json",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={"challenge": "challenge", "value": "value"},
|
||||
)
|
||||
user_request = requests_mock.post(
|
||||
f"{discourse_host}/users",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.create_user(
|
||||
"Test User", "testuser", "test@example.com", "notapassword"
|
||||
)
|
||||
|
||||
assert session_request.called_once
|
||||
assert user_request.called_once
|
||||
|
||||
def test_update_email(self, discourse_host, discourse_client, requests_mock):
|
||||
request = requests_mock.put(
|
||||
f"{discourse_host}/users/someuser/preferences/email",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.update_email("someuser", "newmeail@example.com")
|
||||
|
||||
assert request.called_once
|
||||
|
||||
def test_update_user(self, discourse_client, requests_mock):
|
||||
request = requests_mock.put(
|
||||
f"{discourse_client.host}/users/someuser",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.update_user("someuser", a="a", b="b")
|
||||
|
||||
assert request.called_once
|
||||
|
||||
def test_update_username(self, discourse_client, requests_mock):
|
||||
request = requests_mock.put(
|
||||
f"{discourse_client.host}/users/someuser/preferences/username",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.update_username("someuser", "newname")
|
||||
|
||||
assert request.called_once
|
||||
|
||||
def test_by_external_id(self, discourse_client, requests_mock):
|
||||
request = requests_mock.get(
|
||||
f"{discourse_client.host}/users/by-external/123",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={"user": "123"},
|
||||
)
|
||||
discourse_client.by_external_id(123)
|
||||
|
||||
assert request.called_once
|
||||
|
||||
def test_suspend_user(self, discourse_client, requests_mock, frozen_time):
|
||||
request = requests_mock.put(
|
||||
f"{discourse_client.host}/admin/users/123/suspend",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.suspend(123, 1, "Testing")
|
||||
|
||||
assert request.called_once
|
||||
assert request.last_request.method == "PUT"
|
||||
|
||||
request_payload = urllib.parse.parse_qs(request.last_request.text)
|
||||
|
||||
assert request_payload["reason"] == ["Testing"]
|
||||
assert request_payload["suspend_until"] == ["2023-08-14T12:30:15+00:00"]
|
||||
|
||||
def test_unsuspend_user(self, discourse_client, requests_mock):
|
||||
request = requests_mock.put(
|
||||
f"{discourse_client.host}/admin/users/123/unsuspend",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.unsuspend(123)
|
||||
|
||||
assert request.called_once
|
||||
|
||||
def test_user_bagdes(self, discourse_client, requests_mock):
|
||||
request = requests_mock.get(
|
||||
f"{discourse_client.host}/user-badges/myusername.json",
|
||||
headers={"Content-Type": "application/json; charset=utf-8"},
|
||||
json={},
|
||||
)
|
||||
discourse_client.user_badges("myusername")
|
||||
|
||||
assert request.called_once
|
||||
|
||||
|
||||
def prepare_response(request):
|
||||
# we need to mocked response to look a little more real
|
||||
request.return_value = mock.MagicMock(
|
||||
@@ -13,9 +134,7 @@ def prepare_response(request):
|
||||
|
||||
|
||||
class ClientBaseTestCase(unittest.TestCase):
|
||||
"""
|
||||
|
||||
"""
|
||||
""" """
|
||||
|
||||
def setUp(self):
|
||||
self.host = "http://testhost"
|
||||
@@ -40,96 +159,8 @@ class ClientBaseTestCase(unittest.TestCase):
|
||||
self.assertEqual(kwargs["params"], params)
|
||||
|
||||
|
||||
class TestClientRequests(ClientBaseTestCase):
|
||||
"""
|
||||
Tests for common request handling
|
||||
"""
|
||||
|
||||
@mock.patch("pydiscourse.client.requests")
|
||||
def test_empty_content_http_ok(self, mocked_requests):
|
||||
"""Empty content should not raise error
|
||||
|
||||
Critical to test against *bytestrings* rather than unicode
|
||||
"""
|
||||
mocked_response = mock.MagicMock()
|
||||
mocked_response.content = b" "
|
||||
mocked_response.status_code = 200
|
||||
mocked_response.headers = {"content-type": "text/plain; charset=utf-8"}
|
||||
|
||||
assert "content-type" in mocked_response.headers
|
||||
|
||||
mocked_requests.request = mock.MagicMock()
|
||||
mocked_requests.request.return_value = mocked_response
|
||||
|
||||
resp = self.client._request("GET", "/users/admin/1/unsuspend", {})
|
||||
self.assertIsNone(resp)
|
||||
|
||||
|
||||
@mock.patch("requests.request")
|
||||
class TestUser(ClientBaseTestCase):
|
||||
|
||||
def test_user(self, request):
|
||||
prepare_response(request)
|
||||
self.client.user("someuser")
|
||||
self.assertRequestCalled(request, "GET", "/users/someuser.json")
|
||||
|
||||
def test_create_user(self, request):
|
||||
prepare_response(request)
|
||||
self.client.create_user(
|
||||
"Test User", "testuser", "test@example.com", "notapassword"
|
||||
)
|
||||
self.assertEqual(request.call_count, 2)
|
||||
|
||||
# XXX incomplete
|
||||
|
||||
def test_update_email(self, request):
|
||||
prepare_response(request)
|
||||
email = "test@example.com"
|
||||
self.client.update_email("someuser", email)
|
||||
self.assertRequestCalled(
|
||||
request, "PUT", "/users/someuser/preferences/email", email=email
|
||||
)
|
||||
|
||||
def test_update_user(self, request):
|
||||
prepare_response(request)
|
||||
self.client.update_user("someuser", a="a", b="b")
|
||||
self.assertRequestCalled(request, "PUT", "/users/someuser", a="a", b="b")
|
||||
|
||||
def test_update_username(self, request):
|
||||
prepare_response(request)
|
||||
self.client.update_username("someuser", "newname")
|
||||
self.assertRequestCalled(
|
||||
request, "PUT", "/users/someuser/preferences/username", username="newname"
|
||||
)
|
||||
|
||||
def test_by_external_id(self, request):
|
||||
prepare_response(request)
|
||||
self.client.by_external_id(123)
|
||||
self.assertRequestCalled(request, "GET", "/users/by-external/123")
|
||||
|
||||
def test_suspend_user(self, request):
|
||||
prepare_response(request)
|
||||
self.client.suspend(123, 1, "Testing")
|
||||
self.assertRequestCalled(
|
||||
request, "PUT", "/admin/users/123/suspend", duration=1, reason="Testing"
|
||||
)
|
||||
|
||||
def test_unsuspend_user(self, request):
|
||||
prepare_response(request)
|
||||
self.client.unsuspend(123)
|
||||
self.assertRequestCalled(request, "PUT", "/admin/users/123/unsuspend")
|
||||
|
||||
def test_user_bagdes(self, request):
|
||||
prepare_response(request)
|
||||
self.client.user_badges("username")
|
||||
self.assertRequestCalled(
|
||||
request, "GET", "/user-badges/{}.json".format("username")
|
||||
)
|
||||
|
||||
|
||||
@mock.patch("requests.request")
|
||||
class TestTopics(ClientBaseTestCase):
|
||||
|
||||
def test_hot_topics(self, request):
|
||||
prepare_response(request)
|
||||
self.client.hot_topics()
|
||||
@@ -167,7 +198,6 @@ class TestTopics(ClientBaseTestCase):
|
||||
|
||||
@mock.patch("pydiscourse.client.requests.request")
|
||||
class MiscellaneousTests(ClientBaseTestCase):
|
||||
|
||||
def test_latest_posts(self, request):
|
||||
prepare_response(request)
|
||||
r = self.client.latest_posts(before=54321)
|
||||
|
||||
@@ -13,6 +13,7 @@ setenv =
|
||||
PYTHONPATH = {toxinidir}:{toxinidir}/pydiscourse
|
||||
commands =
|
||||
pytest {posargs} --cov=pydiscourse
|
||||
coverage report -m --include='**/pydiscourse/client.py' --fail-under=45
|
||||
coverage report -m --include='**/pydiscourse/sso.py' --fail-under=100
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
Reference in New Issue
Block a user