Repository: meltano/meltano

Issue:
bug: Flaky test `tests/meltano/api/test_workers.py::TestUIAvailableWorker::test_open_browser`
### Meltano Version

N/A

### Python Version

NA

### Bug scope

API

### Operating System

N/A

### Description

From https://github.com/meltano/meltano/issues/6827

- https://github.com/meltano/meltano/actions/runs/3175819389/jobs/5174350171
- https://github.com/meltano/meltano/actions/runs/3159179840/jobs/5142085679
- https://github.com/meltano/meltano/actions/runs/3056794521/jobs/4931297918
- https://github.com/meltano/meltano/actions/runs/3182662282/jobs/5188896027
- https://github.com/meltano/meltano/actions/runs/3183540252/jobs/5190883701
- https://github.com/meltano/meltano/actions/runs/3184585185/jobs/5193156601

Of the recorded instances, we observe:
- They are all using `ubuntu-latest`
- They are all using Python 3.7
- They are using a variety of database backends

The error occurs on the assert on the last line of the following code block. We see that `requests_get` is always called 3 times, but `sleep` is called 300-500 times.

```python
    @mock.patch("webbrowser.open")
    @mock.patch("requests.get")
    def test_open_browser(self, requests_get, webbrowser_open, subject):
        error = mock.Mock(status_code=400)
        ok = mock.Mock(status_code=200)
        requests_get.side_effect = [error, error, ok]
        with mock.patch("time.sleep") as sleep:
            sleep.return_value = None
            subject.run()
            webbrowser_open.assert_called_with("http://localhost:5000")
            assert requests_get.call_count == sleep.call_count
```

### Code

_No response_


Discussion:

Resolving patch:
diff --git a/src/meltano/api/workers/ui_available_worker.py b/src/meltano/api/workers/ui_available_worker.py
--- a/src/meltano/api/workers/ui_available_worker.py
+++ b/src/meltano/api/workers/ui_available_worker.py
@@ -1,10 +1,11 @@
+"""Meltano UI worker thread definition."""
+
 from __future__ import annotations
 
 import logging
 import threading
 import time
 import traceback
-import webbrowser
 
 import click
 import requests
@@ -17,14 +18,21 @@
 
 
 class UIAvailableWorker(threading.Thread):
-    def __init__(self, project, open_browser=False):
+    """A thread subclass for Meltano UI workers."""
+
+    def __init__(self, project):
+        """Initialize the `UIAvailableWorker` thread.
+
+        Args:
+            project: The Meltano project.
+        """
         super().__init__()
         self.project = project
-        self.open_browser = open_browser
         self.settings_service = ProjectSettingsService(self.project)
         self._terminate = False
 
-    def run(self):
+    def run(self) -> None:
+        """Run the thread, and report when the Meltano UI becomes available."""
         url = f"http://localhost:{self.settings_service.get('ui.bind_port')}"
         headers = {"Host": self.settings_service.get("ui.server_name")}
 
@@ -33,8 +41,6 @@ def run(self):
                 response = requests.get(url, headers=headers)
                 if response.status_code == SUCCESS_STATUS_CODE:
                     click.secho(f"Meltano UI is now available at {url}", fg="green")
-                    if self.open_browser:
-                        webbrowser.open(url)
                     self._terminate = True
             except Exception:
                 logger.debug(
@@ -44,4 +50,5 @@ def run(self):
             time.sleep(2)
 
     def stop(self):
+        """Stop the thread."""
         self._terminate = True


Test patch:
diff --git a/tests/meltano/api/test_workers.py b/tests/meltano/api/test_workers.py
deleted file mode 100644
--- a/tests/meltano/api/test_workers.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from __future__ import annotations
-
-import mock
-import pytest
-
-from meltano.api.workers import UIAvailableWorker
-
-
-class TestUIAvailableWorker:
-    @pytest.fixture
-    def subject(self, project):
-        return UIAvailableWorker(project, open_browser=True)
-
-    @mock.patch("webbrowser.open")
-    @mock.patch("requests.get")
-    def test_open_browser(self, requests_get, webbrowser_open, subject):
-        error = mock.Mock(status_code=400)
-        ok = mock.Mock(status_code=200)
-        requests_get.side_effect = [error, error, ok]
-        with mock.patch("time.sleep") as sleep:
-            sleep.return_value = None
-            subject.run()
-            webbrowser_open.assert_called_with("http://localhost:5000")
-            assert requests_get.call_count == sleep.call_count