Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve test speed #1017

Merged

Conversation

WilliamBergamin
Copy link
Contributor

@WilliamBergamin WilliamBergamin commented Jan 19, 2024

Flow up to the discussion started in #1015

This PR aims to improvement on the mock_web_api_server by making it more robust and attempts to reduce the time spent sleeping in test ("Run tests without aiohttp")

This is achieved by incorporating a solution that allows for waiting up to a specified duration (X amount of time) to confirm the receipt of requests, instead of waiting for a fixed period and then confirming the reception of requests.

Previously, the test located in "Run tests without aiohttp" required 93 seconds for execution. With these changes, the tests can now be completed in just 24 seconds, resulting in a nearly fourfold reduction in wait time.

Similar changes can be applied to asynchronous test and similar reduction in wait time can also be expected for this as well

NOTE: adding content length header to the mock_web_api_server drastically improved request reliability

Before

Screenshot 2024-02-07 at 11 17 07 AM

After

Screenshot 2024-02-07 at 11 17 28 AM

Feedback

The code can be made clearer and there may be some changes that are not necessary, please point out anything that seems relevant

Is this approach worth implementing?

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Document pages under /docs
  • Others

Requirements (place an x in each [ ])

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

  • I've read and understood the Contributing Guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've run ./scripts/install_all_and_run_tests.sh after making the changes.

Copy link

codecov bot commented Jan 19, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (69e033f) 91.76% compared to head (7af76f9) 91.76%.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1017   +/-   ##
=======================================
  Coverage   91.76%   91.76%           
=======================================
  Files         181      181           
  Lines        6312     6312           
=======================================
  Hits         5792     5792           
  Misses        520      520           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@seratch seratch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THIS IS FANTASTIC! Thank you so much for drastically improving the speed and the stability too! We definitely should go with this approach!

@@ -221,4 +221,4 @@ async def test_url_verification(self):

assert response.status_code == 200
assert response.headers.get("content-type") == "application/json;charset=utf-8"
assert_auth_test_count(self, 1)
assert_auth_test_count(self, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, this should be zero, but perhaps this had been wrong due to the current test runner mechanism's behavior, right? Nice improvement 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess, I suspect this may be caused by the current test runner mechanism's behavior but I need to dig into each individual test to see what the expected behavior should be and why these values are different, there seem to be a few like these 🤔

tests/mock_web_api_server.py Outdated Show resolved Hide resolved
self.wfile.write("OK".encode("utf-8"))
return

if path == "/received_requests.json":
if path == "/health":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using this endpoint? If we no longer need this, you can simply remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 💯 I did add this to try and see if it would be more reliable to ensure the mock_web_api_server responds with a success response before running the tests, there was no notable difference here

self.received_requests = {}

def get(self, key, default=None):
while not self.queue.empty():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite a wise approach 👍

tests/mock_web_api_server.py Outdated Show resolved Hide resolved
@@ -253,9 +257,22 @@ def stop(self):
self.join()


class ReceivedRequestsHandler:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a matter of preference but this class does not handle anything but it just stores the received requests. If I named this, it would be a simpler one such as "ReceivedRequests" or "SavedReceivedRequests"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree 💯

def setup_mock_web_api_server(test: TestCase):
test.server_started = threading.Event()
test.thread = MockServerThread(test)
test.received_requests_handler = ReceivedRequestsHandler(Queue())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the same reason I mentioned above, test.received_requests.get("/auth.test") sounds more natural to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree 💯

@@ -77,7 +74,7 @@ class TestExecutor(Executor):

def test_valid_single_auth(self):
app = App(signing_secret="valid", client=self.web_client)
assert app != None
assert app is not None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

Copy link
Contributor Author

@WilliamBergamin WilliamBergamin Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but I think I'll remove these changes in order to simplify the PR, I can do this in a follow up

@@ -38,9 +39,9 @@ def handle_app_mention(say):
response = app.dispatch(request)
assert response.status == 200
assert_auth_test_count(self, 1)
sleep(1) # wait a bit after auto ack()
sleep(0.5) # wait a bit after auto ack()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess this is still required since we are asserting that an endpoint was not executed
If we validate without waiting we may assert the request was not sent before it gets sent

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. that makes sense

Copy link
Member

@seratch seratch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me 👍 You can merge this PR at any time

@@ -38,9 +39,9 @@ def handle_app_mention(say):
response = app.dispatch(request)
assert response.status == 200
assert_auth_test_count(self, 1)
sleep(1) # wait a bit after auto ack()
sleep(0.5) # wait a bit after auto ack()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. that makes sense

@WilliamBergamin WilliamBergamin marked this pull request as ready for review February 1, 2024 22:21
@WilliamBergamin
Copy link
Contributor Author

WilliamBergamin commented Feb 7, 2024

@seratch I understood why some assert_auth_test_count need to be asserted as being called twice instead of once

  1. The auth test is performed for the bot token and the user token in InstallationStoreAuthorize
  2. Many tests use a variation of this fake MemoryInstallationStore implementation, where a user_token is hard coded into the installation returned by find_installation

I'm assuming the changes regarding the auth_test assertion count now reflect the behavior of bolt python

@WilliamBergamin WilliamBergamin merged commit 91f9ef1 into slackapi:main Feb 7, 2024
12 checks passed
@WilliamBergamin WilliamBergamin deleted the reduce-sleep-time-in-tests branch February 7, 2024 16:36
@WilliamBergamin WilliamBergamin restored the reduce-sleep-time-in-tests branch March 4, 2024 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants