Skip to content

Commit ca551a1

Browse files
make the tests work
1 parent 29b4170 commit ca551a1

13 files changed

+181
-216
lines changed

tests/helpers.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import asyncio
22
import os
3+
from queue import Queue
34
import sys
45
from types import ModuleType
6+
from typing import Optional, Union
57

68

79
def async_test(coro):
@@ -47,17 +49,19 @@ def is_ci_unstable_test_skip_enabled() -> bool:
4749
return os.environ.get("CI_UNSTABLE_TESTS_SKIP_ENABLED") == "1"
4850

4951

50-
def reload_module(root_module: ModuleType):
51-
package_name = root_module.__name__
52-
loaded_package_modules = {
53-
key: value for key, value in sys.modules.items() if key.startswith(package_name) and isinstance(value, ModuleType)
54-
}
52+
class ReceivedRequests:
53+
def __init__(self, queue: Union[Queue, asyncio.Queue]):
54+
self.queue = queue
55+
self.received_requests: dict = {}
5556

56-
for key in loaded_package_modules:
57-
del sys.modules[key]
57+
def get(self, key: str, default: Optional[int] = None) -> Optional[int]:
58+
while not self.queue.empty():
59+
path = self.queue.get()
60+
self.received_requests[path] = self.received_requests.get(path, 0) + 1
61+
return self.received_requests.get(key, default)
5862

59-
for key in loaded_package_modules:
60-
new_module = __import__(key)
61-
old_module = loaded_package_modules[key]
62-
old_module.__dict__.clear()
63-
old_module.__dict__.update(new_module.__dict__)
63+
async def get_async(self, key: str, default: Optional[int] = None) -> Optional[int]:
64+
while not self.queue.empty():
65+
path = await self.queue.get()
66+
self.received_requests[path] = self.received_requests.get(path, 0) + 1
67+
return self.received_requests.get(key, default)

tests/slack_sdk/web/mock_web_api_server.py

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
from http import HTTPStatus
99
from http.server import HTTPServer, SimpleHTTPRequestHandler
1010
from multiprocessing.context import Process
11-
from typing import Type
11+
from queue import Queue
12+
from typing import Type, Union
1213
from unittest import TestCase
13-
from urllib.parse import urlparse, parse_qs
14+
from urllib.parse import parse_qs, urlparse
1415
from urllib.request import Request, urlopen
1516

16-
from tests.helpers import get_mock_server_mode
17+
from tests.helpers import ReceivedRequests, get_mock_server_mode
1718

1819

1920
class MockHandler(SimpleHTTPRequestHandler):
@@ -78,6 +79,8 @@ def set_common_headers(self):
7879

7980
def _handle(self):
8081
try:
82+
# put_nowait is common between Queue & asyncio.Queue, it does not need to be awaited
83+
self.server.queue.put_nowait(self.path)
8184
if self.path == "/received_requests.json":
8285
self.send_response(200)
8386
self.set_common_headers()
@@ -313,32 +316,44 @@ def stop(self):
313316

314317

315318
class MockServerThread(threading.Thread):
316-
def __init__(self, test: TestCase, handler: Type[SimpleHTTPRequestHandler] = MockHandler):
319+
def __init__(
320+
self, queue: Union[Queue, asyncio.Queue], test: TestCase, handler: Type[SimpleHTTPRequestHandler] = MockHandler
321+
):
317322
threading.Thread.__init__(self)
318323
self.handler = handler
319324
self.test = test
325+
self.queue = queue
320326

321327
def run(self):
322328
self.server = HTTPServer(("localhost", 8888), self.handler)
329+
self.server.queue = self.queue
323330
self.test.server_url = "http://localhost:8888"
324331
self.test.host, self.test.port = self.server.socket.getsockname()
325332
self.test.server_started.set() # threading.Event()
326333

327334
self.test = None
328335
try:
329-
self.server.serve_forever()
336+
self.server.serve_forever(0.05)
330337
finally:
331338
self.server.server_close()
332339

333340
def stop(self):
341+
with self.server.queue.mutex:
342+
del self.server.queue
343+
self.server.shutdown()
344+
self.join()
345+
346+
def stop_unsafe(self):
347+
del self.server.queue
334348
self.server.shutdown()
335349
self.join()
336350

337351

338352
def setup_mock_web_api_server(test: TestCase):
339353
if get_mock_server_mode() == "threading":
340354
test.server_started = threading.Event()
341-
test.thread = MockServerThread(test)
355+
test.received_requests = ReceivedRequests(Queue())
356+
test.thread = MockServerThread(test.received_requests.queue, test)
342357
test.thread.start()
343358
test.server_started.wait()
344359
else:
@@ -389,37 +404,65 @@ def cleanup_mock_web_api_server(test: TestCase):
389404
test.process = None
390405

391406

392-
def assert_auth_test_count(test: TestCase, expected_count: int):
393-
time.sleep(0.1)
394-
retry_count = 0
407+
def assert_received_request_count(test: TestCase, path: str, min_count: int, timeout: float = 1):
408+
start_time = time.time()
395409
error = None
396-
while retry_count < 3:
410+
while time.time() - start_time < timeout:
397411
try:
398-
test.mock_received_requests["/auth.test"] == expected_count
399-
break
412+
received_count = test.received_requests.get(path, 0)
413+
assert (
414+
received_count == min_count
415+
), f"Expected {min_count} '{path}' {'requests' if min_count > 1 else 'request'}, but got {received_count}!"
416+
return
400417
except Exception as e:
401418
error = e
402-
retry_count += 1
403-
# waiting for mock_received_requests updates
404-
time.sleep(0.1)
419+
# waiting for some requests to be received
420+
time.sleep(0.05)
405421

406422
if error is not None:
407423
raise error
408424

409425

410-
async def assert_auth_test_count_async(test: TestCase, expected_count: int):
411-
await asyncio.sleep(0.1)
412-
retry_count = 0
426+
def assert_auth_test_count(test: TestCase, expected_count: int):
427+
assert_received_request_count(test, "/auth.test", expected_count, 0.5)
428+
429+
430+
#########
431+
# async #
432+
#########
433+
434+
435+
def setup_mock_web_api_server_async(test: TestCase):
436+
test.server_started = threading.Event()
437+
test.received_requests = ReceivedRequests(asyncio.Queue())
438+
test.thread = MockServerThread(test.received_requests.queue, test)
439+
test.thread.start()
440+
test.server_started.wait()
441+
442+
443+
def cleanup_mock_web_api_server_async(test: TestCase):
444+
test.thread.stop_unsafe()
445+
test.thread = None
446+
447+
448+
async def assert_received_request_count_async(test: TestCase, path: str, min_count: int, timeout: float = 1):
449+
start_time = time.time()
413450
error = None
414-
while retry_count < 3:
451+
while time.time() - start_time < timeout:
415452
try:
416-
test.mock_received_requests["/auth.test"] == expected_count
417-
break
453+
received_count = await test.received_requests.get_async(path, 0)
454+
assert (
455+
received_count == min_count
456+
), f"Expected {min_count} '{path}' {'requests' if min_count > 1 else 'request'}, but got {received_count}!"
457+
return
418458
except Exception as e:
419459
error = e
420-
retry_count += 1
421460
# waiting for mock_received_requests updates
422-
await asyncio.sleep(0.1)
461+
await asyncio.sleep(0.05)
423462

424463
if error is not None:
425464
raise error
465+
466+
467+
async def assert_auth_test_count_async(test: TestCase, expected_count: int):
468+
await assert_received_request_count_async(test, "/auth.test", expected_count, 0.5)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from unittest import TestCase
2+
3+
from slack_sdk.web.legacy_client import LegacyWebClient
4+
from tests.slack_sdk.web.mock_web_api_server import (
5+
assert_received_request_count,
6+
cleanup_mock_web_api_server,
7+
setup_mock_web_api_server,
8+
)
9+
10+
11+
class TestLegacyWebClientUrlFormat(TestCase):
12+
def setUp(self):
13+
setup_mock_web_api_server(self)
14+
self.client = LegacyWebClient(token="xoxb-api_test", base_url="http://localhost:8888")
15+
self.client_base_url_slash = LegacyWebClient(token="xoxb-api_test", base_url="http://localhost:8888/")
16+
17+
def tearDown(self):
18+
cleanup_mock_web_api_server(self)
19+
20+
def test_base_url_without_slash_api_method_without_slash(self):
21+
self.client.api_call("chat.postMessage")
22+
assert_received_request_count(self, "/chat.postMessage", 1)
23+
24+
def test_base_url_without_slash_api_method_with_slash(self):
25+
self.client.api_call("/chat.postMessage")
26+
assert_received_request_count(self, "/chat.postMessage", 1)
27+
28+
def test_base_url_with_slash_api_method_without_slash(self):
29+
self.client_base_url_slash.api_call("chat.postMessage")
30+
assert_received_request_count(self, "/chat.postMessage", 1)
31+
32+
def test_base_url_with_slash_api_method_with_slash(self):
33+
self.client_base_url_slash.api_call("/chat.postMessage")
34+
assert_received_request_count(self, "/chat.postMessage", 1)
35+
36+
def test_base_url_without_slash_api_method_with_slash_and_trailing_slash(self):
37+
self.client.api_call("/chat.postMessage/")
38+
assert_received_request_count(self, "/chat.postMessage/", 1)
Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,38 @@
11
from unittest import TestCase
2-
from unittest.mock import patch
3-
from urllib import request
4-
from urllib.request import Request, urlopen
52

6-
import slack_sdk.web
7-
from tests.helpers import reload_module
3+
from slack_sdk.web import WebClient
84
from tests.slack_sdk.web.mock_web_api_server import (
9-
setup_mock_web_api_server,
5+
assert_received_request_count,
106
cleanup_mock_web_api_server,
7+
setup_mock_web_api_server,
118
)
129

1310

14-
def build_spy_urlopen(test: TestCase):
15-
def spy_urlopen(*args, **kwargs):
16-
test.urlopen_spy_args = args
17-
test.urlopen_spy_kwargs = kwargs
18-
return urlopen(*args, **kwargs)
19-
20-
return spy_urlopen
21-
22-
2311
class TestWebClientUrlFormat(TestCase):
2412
def setUp(self):
2513
setup_mock_web_api_server(self)
26-
with patch.object(request, "urlopen") as mock_urlopen:
27-
mock_urlopen.side_effect = build_spy_urlopen(self)
28-
reload_module(slack_sdk.web)
29-
self.client = slack_sdk.web.WebClient(token="xoxb-api_test", base_url="http://localhost:8888")
30-
self.client_base_url_slash = slack_sdk.web.WebClient(token="xoxb-api_test", base_url="http://localhost:8888/")
14+
self.client = WebClient(token="xoxb-api_test", base_url="http://localhost:8888")
15+
self.client_base_url_slash = WebClient(token="xoxb-api_test", base_url="http://localhost:8888/")
3116

3217
def tearDown(self):
3318
cleanup_mock_web_api_server(self)
34-
self.urlopen_spy_args = None
35-
self.urlopen_spy_kwargs = None
36-
37-
@classmethod
38-
def tearDownClass(cls):
39-
reload_module(slack_sdk.web)
4019

4120
def test_base_url_without_slash_api_method_without_slash(self):
42-
self.client.api_call("api.test")
43-
self.assertIsInstance(self.urlopen_spy_args[0], Request)
44-
self.assertEqual(self.urlopen_spy_args[0].full_url, "http://localhost:8888/api.test")
21+
self.client.api_call("chat.postMessage")
22+
assert_received_request_count(self, "/chat.postMessage", 1)
4523

4624
def test_base_url_without_slash_api_method_with_slash(self):
47-
self.client.api_call("/api.test")
48-
self.assertIsInstance(self.urlopen_spy_args[0], Request)
49-
self.assertEqual(self.urlopen_spy_args[0].full_url, "http://localhost:8888/api.test")
25+
self.client.api_call("/chat.postMessage")
26+
assert_received_request_count(self, "/chat.postMessage", 1)
5027

5128
def test_base_url_with_slash_api_method_without_slash(self):
52-
self.client_base_url_slash.api_call("api.test")
53-
self.assertIsInstance(self.urlopen_spy_args[0], Request)
54-
self.assertEqual(self.urlopen_spy_args[0].full_url, "http://localhost:8888/api.test")
29+
self.client_base_url_slash.api_call("chat.postMessage")
30+
assert_received_request_count(self, "/chat.postMessage", 1)
5531

5632
def test_base_url_with_slash_api_method_with_slash(self):
57-
self.client_base_url_slash.api_call("/api.test")
58-
self.assertIsInstance(self.urlopen_spy_args[0], Request)
59-
self.assertEqual(self.urlopen_spy_args[0].full_url, "http://localhost:8888/api.test")
33+
self.client_base_url_slash.api_call("/chat.postMessage")
34+
assert_received_request_count(self, "/chat.postMessage", 1)
6035

6136
def test_base_url_without_slash_api_method_with_slash_and_trailing_slash(self):
62-
self.client.api_call("/api.test/")
63-
self.assertIsInstance(self.urlopen_spy_args[0], Request)
64-
self.assertEqual(self.urlopen_spy_args[0].full_url, "http://localhost:8888/api.test/")
37+
self.client.api_call("/chat.postMessage/")
38+
assert_received_request_count(self, "/chat.postMessage/", 1)

tests/slack_sdk/web/test_web_legacy_client_url_format.py

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)