forked from tindie/pydiscourse
-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
179 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 "[email protected]" | ||
|
||
|
||
@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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,135 +1,166 @@ | ||
import unittest | ||
|
||
import urllib.parse | ||
from unittest import mock | ||
|
||
from pydiscourse import client | ||
|
||
|
||
def prepare_response(request): | ||
# we need to mocked response to look a little more real | ||
request.return_value = mock.MagicMock( | ||
headers={"content-type": "application/json; charset=utf-8"} | ||
) | ||
|
||
|
||
class ClientBaseTestCase(unittest.TestCase): | ||
""" | ||
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" ", | ||
) | ||
|
||
def setUp(self): | ||
self.host = "http://testhost" | ||
self.api_username = "testuser" | ||
self.api_key = "testkey" | ||
resp = discourse_client._request("GET", "/users/admin/1/unsuspend", {}) | ||
|
||
self.client = client.DiscourseClient(self.host, self.api_username, self.api_key) | ||
assert resp is None | ||
|
||
def assertRequestCalled(self, request, verb, url, **params): | ||
self.assertTrue(request.called) | ||
|
||
args, kwargs = request.call_args | ||
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", "[email protected]", "notapassword" | ||
) | ||
|
||
self.assertEqual(args[0], verb) | ||
self.assertEqual(args[1], self.host + url) | ||
assert session_request.called_once | ||
assert user_request.called_once | ||
|
||
headers = kwargs["headers"] | ||
self.assertEqual(headers.pop("Api-Username"), self.api_username) | ||
self.assertEqual(headers.pop("Api-Key"), self.api_key) | ||
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", "[email protected]") | ||
|
||
if verb == "GET": | ||
self.assertEqual(kwargs["params"], params) | ||
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") | ||
|
||
class TestClientRequests(ClientBaseTestCase): | ||
""" | ||
Tests for common request handling | ||
""" | ||
assert request.called_once | ||
|
||
@mock.patch("pydiscourse.client.requests") | ||
def test_empty_content_http_ok(self, mocked_requests): | ||
"""Empty content should not raise error | ||
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") | ||
|
||
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 request.called_once | ||
|
||
assert "content-type" in mocked_response.headers | ||
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) | ||
|
||
mocked_requests.request = mock.MagicMock() | ||
mocked_requests.request.return_value = mocked_response | ||
assert request.called_once | ||
|
||
resp = self.client._request("GET", "/users/admin/1/unsuspend", {}) | ||
self.assertIsNone(resp) | ||
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" | ||
|
||
@mock.patch("requests.request") | ||
class TestUser(ClientBaseTestCase): | ||
request_payload = urllib.parse.parse_qs(request.last_request.text) | ||
|
||
def test_user(self, request): | ||
prepare_response(request) | ||
self.client.user("someuser") | ||
self.assertRequestCalled(request, "GET", "/users/someuser.json") | ||
assert request_payload["reason"] == ["Testing"] | ||
assert request_payload["suspend_until"] == ["2023-08-14T12:30:15+00:00"] | ||
|
||
def test_create_user(self, request): | ||
prepare_response(request) | ||
self.client.create_user( | ||
"Test User", "testuser", "[email protected]", "notapassword" | ||
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={}, | ||
) | ||
self.assertEqual(request.call_count, 2) | ||
discourse_client.unsuspend(123) | ||
|
||
# XXX incomplete | ||
assert request.called_once | ||
|
||
def test_update_email(self, request): | ||
prepare_response(request) | ||
email = "[email protected]" | ||
self.client.update_email("someuser", email) | ||
self.assertRequestCalled( | ||
request, "PUT", "/users/someuser/preferences/email", email=email | ||
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") | ||
|
||
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") | ||
assert request.called_once | ||
|
||
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 prepare_response(request): | ||
# we need to mocked response to look a little more real | ||
request.return_value = mock.MagicMock( | ||
headers={"content-type": "application/json; charset=utf-8"} | ||
) | ||
|
||
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") | ||
class ClientBaseTestCase(unittest.TestCase): | ||
""" """ | ||
|
||
def test_user_bagdes(self, request): | ||
prepare_response(request) | ||
self.client.user_badges("username") | ||
self.assertRequestCalled( | ||
request, "GET", "/user-badges/{}.json".format("username") | ||
) | ||
def setUp(self): | ||
self.host = "http://testhost" | ||
self.api_username = "testuser" | ||
self.api_key = "testkey" | ||
|
||
self.client = client.DiscourseClient(self.host, self.api_username, self.api_key) | ||
|
||
def assertRequestCalled(self, request, verb, url, **params): | ||
self.assertTrue(request.called) | ||
|
||
args, kwargs = request.call_args | ||
|
||
self.assertEqual(args[0], verb) | ||
self.assertEqual(args[1], self.host + url) | ||
|
||
headers = kwargs["headers"] | ||
self.assertEqual(headers.pop("Api-Username"), self.api_username) | ||
self.assertEqual(headers.pop("Api-Key"), self.api_key) | ||
|
||
if verb == "GET": | ||
self.assertEqual(kwargs["params"], params) | ||
|
||
|
||
@mock.patch("requests.request") | ||
class TestTopics(ClientBaseTestCase): | ||
|
||
def test_hot_topics(self, request): | ||
prepare_response(request) | ||
self.client.hot_topics() | ||
|
@@ -167,7 +198,6 @@ def invite_user_to_topic(self, request): | |
|
||
@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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters