From ffb0b797c0fed2266c1d85c9d8bc9260324b2f99 Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Thu, 26 Dec 2024 08:50:58 -0500
Subject: [PATCH 1/7] feat: async testing - interim checkin

---
 poetry.lock                                 | 17 ++++++-
 pyproject.toml                              |  1 +
 pytest.ini                                  |  3 ++
 tests/async_test_notes.md                   | 50 +++++++++++++++++++++
 tests/fixtures/fixtures_reviews_response.py | 14 ++++++
 tests/fixtures/reviews_response.json        | 41 +++++++++++++++++
 tests/test_get_reviewers_rest.py            | 40 +++++++++++++++++
 7 files changed, 165 insertions(+), 1 deletion(-)
 create mode 100644 pytest.ini
 create mode 100644 tests/async_test_notes.md
 create mode 100644 tests/fixtures/fixtures_reviews_response.py
 create mode 100644 tests/fixtures/reviews_response.json
 create mode 100644 tests/test_get_reviewers_rest.py

diff --git a/poetry.lock b/poetry.lock
index 9cd6923..aaa8246 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -108,6 +108,21 @@ yarl = ">=1.17.0,<2.0"
 [package.extras]
 speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
 
+[[package]]
+name = "aioresponses"
+version = "0.7.7"
+description = "Mock out requests made by ClientSession from aiohttp package"
+optional = false
+python-versions = "*"
+files = [
+    {file = "aioresponses-0.7.7-py2.py3-none-any.whl", hash = "sha256:6975f31fe5e7f2113a41bd387221f31854f285ecbc05527272cd8ba4c50764a3"},
+    {file = "aioresponses-0.7.7.tar.gz", hash = "sha256:66292f1d5c94a3cb984f3336d806446042adb17347d3089f2d3962dd6e5ba55a"},
+]
+
+[package.dependencies]
+aiohttp = ">=3.3.0,<4.0.0"
+packaging = ">=22.0"
+
 [[package]]
 name = "aiosignal"
 version = "1.3.1"
@@ -862,4 +877,4 @@ propcache = ">=0.2.0"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.11"
-content-hash = "02a148172248ce5f797eba063b5a641faa47b6b4eb763ad2146b871a01c824fd"
+content-hash = "16f6a4fb59c3414cda51c9e86aed644dbf6c9c4825f6d917b095496b59ea56bf"
diff --git a/pyproject.toml b/pyproject.toml
index 89e9a45..6d1558d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,6 +16,7 @@ aiohttp = "^3.11.2"
 requests = "^2.32.3"
 tabulate = "^0.9.0"
 asyncio = "^3.4.3"
+aioresponses = "^0.7.7"
 
 
 [tool.poetry.group.dev.dependencies]
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..c47d933
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+markers =
+    asyncio: mark a test as requiring asyncio
\ No newline at end of file
diff --git a/tests/async_test_notes.md b/tests/async_test_notes.md
new file mode 100644
index 0000000..d1ecf8f
--- /dev/null
+++ b/tests/async_test_notes.md
@@ -0,0 +1,50 @@
+# Asynch testing notes
+## 
+## references
+- [reviewers github api docs](https://docs.github.com/en/rest/pulls/reviews?apiVersion=2022-11-28)
+- [github api docs](https://docs.github.com/en/rest/reference/pulls#reviews)
+
+### sample response
+```json
+[
+  {
+    "id": 80,
+    "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=",
+    "user": {
+      "login": "octocat",
+      "id": 1,
+      "node_id": "MDQ6VXNlcjE=",
+      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/octocat",
+      "html_url": "https://github.com/octocat",
+      "followers_url": "https://api.github.com/users/octocat/followers",
+      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+      "organizations_url": "https://api.github.com/users/octocat/orgs",
+      "repos_url": "https://api.github.com/users/octocat/repos",
+      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/octocat/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "body": "Here is the body for the review.",
+    "state": "APPROVED",
+    "html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
+    "pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
+    "_links": {
+      "html": {
+        "href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
+      },
+      "pull_request": {
+        "href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
+      }
+    },
+    "submitted_at": "2019-11-17T17:43:43Z",
+    "commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091",
+    "author_association": "COLLABORATOR"
+  }
+]
+```
\ No newline at end of file
diff --git a/tests/fixtures/fixtures_reviews_response.py b/tests/fixtures/fixtures_reviews_response.py
new file mode 100644
index 0000000..85da6f1
--- /dev/null
+++ b/tests/fixtures/fixtures_reviews_response.py
@@ -0,0 +1,14 @@
+import json
+from typing import Any
+
+import pytest
+
+
+def read_reviews_file() -> dict[str, Any]:
+    # assume the reviews_response.json file is in the tests/fixtures directory
+    with open("tests/fixtures/reviews_response.json") as file:
+        return json.load(file)
+
+def get_reviews_url(owner: str, repo: str, pull_number: int) -> str:
+    URL = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
+    return URL
diff --git a/tests/fixtures/reviews_response.json b/tests/fixtures/reviews_response.json
new file mode 100644
index 0000000..710bbd5
--- /dev/null
+++ b/tests/fixtures/reviews_response.json
@@ -0,0 +1,41 @@
+[
+  {
+    "id": 80,
+    "node_id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3ODA=",
+    "user": {
+      "login": "octocat",
+      "id": 1,
+      "node_id": "MDQ6VXNlcjE=",
+      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/octocat",
+      "html_url": "https://github.com/octocat",
+      "followers_url": "https://api.github.com/users/octocat/followers",
+      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+      "organizations_url": "https://api.github.com/users/octocat/orgs",
+      "repos_url": "https://api.github.com/users/octocat/repos",
+      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/octocat/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "body": "Here is the body for the review.",
+    "state": "APPROVED",
+    "html_url": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80",
+    "pull_request_url": "https://api.github.com/repos/octocat/Hello-World/pulls/12",
+    "_links": {
+      "html": {
+        "href": "https://github.com/octocat/Hello-World/pull/12#pullrequestreview-80"
+      },
+      "pull_request": {
+        "href": "https://api.github.com/repos/octocat/Hello-World/pulls/12"
+      }
+    },
+    "submitted_at": "2019-11-17T17:43:43Z",
+    "commit_id": "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091",
+    "author_association": "COLLABORATOR"
+  }
+]
diff --git a/tests/test_get_reviewers_rest.py b/tests/test_get_reviewers_rest.py
new file mode 100644
index 0000000..8163703
--- /dev/null
+++ b/tests/test_get_reviewers_rest.py
@@ -0,0 +1,40 @@
+# Python
+import json
+import pytest
+import unittest
+from unittest.mock import AsyncMock, patch
+import aiohttp
+import asyncio
+import os
+
+from aioresponses import aioresponses
+
+from pr_reviews.queries.get_reviewers_rest import (fetch, fetch_batch,
+                                                   get_reviewers_for_pull_requests)
+from tests.fixtures.fixtures_reviews_response import read_reviews_file, \
+    get_reviews_url
+
+import asyncio
+import os
+
+
+class TestGetReviewersRest(unittest.TestCase):
+    def __init__(self, *args, **kwargs):
+        super(TestGetReviewersRest, self).__init__(*args, **kwargs)
+        self.REVIEWS_RESPONSE = read_reviews_file()
+
+    @pytest.mark.asyncio
+    @patch.dict(os.environ, {"GITHUB_TOKEN": "test_token"})
+    async def test_fetch(self):
+        with aioresponses() as mock_get:
+            url = get_reviews_url("expressjs",
+                              "express",
+                              1)
+            mock_get.get(url, status=200, payload=self.REVIEWS_RESPONSE)
+            response = await fetch(url)
+            assert response == self.REVIEWS_RESPONSE
+
+
+
+if __name__ == "__main__":
+    unittest.main()

From 59a37969bb8b3250818a9d7a79d03c1b2eb5940d Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Thu, 26 Dec 2024 17:25:15 -0500
Subject: [PATCH 2/7] feat: async testing

- asyncio/aiohttp testing with aioresponses module
---
 poetry.lock                                 | 39 ++++++++++-
 pr_reviews/queries/get_reviewers_rest.py    | 10 +--
 pyproject.toml                              |  8 ++-
 pytest.ini                                  |  3 -
 tests/fixtures/fixtures_reviews_response.py | 14 ----
 tests/test_get_reviewers_rest.py            | 73 +++++++++++----------
 6 files changed, 87 insertions(+), 60 deletions(-)
 delete mode 100644 pytest.ini
 delete mode 100644 tests/fixtures/fixtures_reviews_response.py

diff --git a/poetry.lock b/poetry.lock
index aaa8246..2695ac0 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -699,6 +699,43 @@ pluggy = ">=1.5,<2"
 [package.extras]
 dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
 
+[[package]]
+name = "pytest-aiohttp"
+version = "1.0.5"
+description = "Pytest plugin for aiohttp support"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pytest-aiohttp-1.0.5.tar.gz", hash = "sha256:880262bc5951e934463b15e3af8bb298f11f7d4d3ebac970aab425aff10a780a"},
+    {file = "pytest_aiohttp-1.0.5-py3-none-any.whl", hash = "sha256:63a5360fd2f34dda4ab8e6baee4c5f5be4cd186a403cabd498fced82ac9c561e"},
+]
+
+[package.dependencies]
+aiohttp = ">=3.8.1"
+pytest = ">=6.1.0"
+pytest-asyncio = ">=0.17.2"
+
+[package.extras]
+testing = ["coverage (==6.2)", "mypy (==0.931)"]
+
+[[package]]
+name = "pytest-asyncio"
+version = "0.25.0"
+description = "Pytest support for asyncio"
+optional = false
+python-versions = ">=3.9"
+files = [
+    {file = "pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3"},
+    {file = "pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609"},
+]
+
+[package.dependencies]
+pytest = ">=8.2,<9"
+
+[package.extras]
+docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"]
+testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
+
 [[package]]
 name = "requests"
 version = "2.32.3"
@@ -877,4 +914,4 @@ propcache = ">=0.2.0"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.11"
-content-hash = "16f6a4fb59c3414cda51c9e86aed644dbf6c9c4825f6d917b095496b59ea56bf"
+content-hash = "506b7f02e3c09662096712ee841ae877d9a1481572168e2a340e1f7441490d8b"
diff --git a/pr_reviews/queries/get_reviewers_rest.py b/pr_reviews/queries/get_reviewers_rest.py
index ab86bd9..9aefdf9 100644
--- a/pr_reviews/queries/get_reviewers_rest.py
+++ b/pr_reviews/queries/get_reviewers_rest.py
@@ -12,18 +12,18 @@
     HTTPS_PROXY = os.getenv("https_proxy")
 
 
-async def fetch(session: aiohttp.ClientSession, url: str) -> dict[str, str]:
+async def fetch(client: aiohttp.ClientSession, url: str) -> dict[str, str]:
     headers = {
         "Authorization": f"Bearer {GITHUB_TOKEN}",
         "Accept": "application/vnd.github.v3+json",
     }
     if HTTPS_PROXY:
-        async with session.get(url,
-                               headers=headers,
-                               proxy=HTTPS_PROXY) as response:
+        async with client.get(url,
+                              headers=headers,
+                              proxy=HTTPS_PROXY) as response:
             return await response.json()
     else:
-        async with session.get(url, headers=headers) as response:
+        async with client.get(url, headers=headers) as response:
             return await response.json()
 
 
diff --git a/pyproject.toml b/pyproject.toml
index 6d1558d..9e5fb2f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,6 +23,8 @@ aioresponses = "^0.7.7"
 pytest = "^8.3.4"
 ruff = "^0.8.4"
 isort = "^5.13.2"
+pytest-aiohttp = "^1.0.5"
+pytest-asyncio = "^0.25.0"
 
 [build-system]
 requires = ["poetry-core"]
@@ -53,4 +55,8 @@ per-file-ignores = { "tests/**/test*.py" = ["S101"] }
 #PT: Pytest-specific rules
 #Q: Quotes (single vs. double)
 #S: Security issues
-#T: Type annotations
\ No newline at end of file
+#T: Type annotations
+
+[tool.pytest.ini_options]
+asyncio_mode = "auto"  # or "strict"
+required_plugins = ["pytest-asyncio"]
\ No newline at end of file
diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index c47d933..0000000
--- a/pytest.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[pytest]
-markers =
-    asyncio: mark a test as requiring asyncio
\ No newline at end of file
diff --git a/tests/fixtures/fixtures_reviews_response.py b/tests/fixtures/fixtures_reviews_response.py
deleted file mode 100644
index 85da6f1..0000000
--- a/tests/fixtures/fixtures_reviews_response.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import json
-from typing import Any
-
-import pytest
-
-
-def read_reviews_file() -> dict[str, Any]:
-    # assume the reviews_response.json file is in the tests/fixtures directory
-    with open("tests/fixtures/reviews_response.json") as file:
-        return json.load(file)
-
-def get_reviews_url(owner: str, repo: str, pull_number: int) -> str:
-    URL = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
-    return URL
diff --git a/tests/test_get_reviewers_rest.py b/tests/test_get_reviewers_rest.py
index 8163703..3c9adff 100644
--- a/tests/test_get_reviewers_rest.py
+++ b/tests/test_get_reviewers_rest.py
@@ -1,40 +1,41 @@
-# Python
 import json
-import pytest
-import unittest
-from unittest.mock import AsyncMock, patch
-import aiohttp
-import asyncio
 import os
+from typing import Callable, Any
+from unittest.mock import patch
 
+import aiohttp
+import pytest
 from aioresponses import aioresponses
-
-from pr_reviews.queries.get_reviewers_rest import (fetch, fetch_batch,
-                                                   get_reviewers_for_pull_requests)
-from tests.fixtures.fixtures_reviews_response import read_reviews_file, \
-    get_reviews_url
-
-import asyncio
-import os
-
-
-class TestGetReviewersRest(unittest.TestCase):
-    def __init__(self, *args, **kwargs):
-        super(TestGetReviewersRest, self).__init__(*args, **kwargs)
-        self.REVIEWS_RESPONSE = read_reviews_file()
-
-    @pytest.mark.asyncio
-    @patch.dict(os.environ, {"GITHUB_TOKEN": "test_token"})
-    async def test_fetch(self):
-        with aioresponses() as mock_get:
-            url = get_reviews_url("expressjs",
-                              "express",
-                              1)
-            mock_get.get(url, status=200, payload=self.REVIEWS_RESPONSE)
-            response = await fetch(url)
-            assert response == self.REVIEWS_RESPONSE
-
-
-
-if __name__ == "__main__":
-    unittest.main()
+from pr_reviews.queries.get_reviewers_rest import fetch
+
+@pytest.fixture
+def read_reviews_file():
+    # assume the reviews_response.json file is in the tests/fixtures directory
+    with open("tests/fixtures/reviews_response.json") as file:
+        return json.load(file)
+
+@pytest.fixture
+def get_reviews_url():
+    def _get_reviews_url(owner: str, repo: str, pull_number: int) -> str:
+        url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
+        return url
+    return _get_reviews_url
+
+
+@pytest.fixture
+def mock_aioresponse():
+    with aioresponses() as m:
+        yield m
+
+@pytest.mark.asyncio
+@patch.dict(os.environ, {"GITHUB_TOKEN": "test_token"})
+async def test_get_reviewers_success(read_reviews_file, get_reviews_url, mock_aioresponse):
+    url = get_reviews_url("expressjs", "express", 1)
+    mock_aioresponse.get(
+        url,
+        status=200,
+        payload=read_reviews_file
+    )
+    async with aiohttp.ClientSession() as client:
+        response = await fetch(client, url)
+        assert response == read_reviews_file

From 88f423f6c9411219a92abd3ebafc5929eef7d6f0 Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Thu, 26 Dec 2024 17:54:50 -0500
Subject: [PATCH 3/7] feat: async testing

- ruff lint, mostly annotations
---
 tests/test_get_reviewers_rest.py | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/tests/test_get_reviewers_rest.py b/tests/test_get_reviewers_rest.py
index 3c9adff..1019e8f 100644
--- a/tests/test_get_reviewers_rest.py
+++ b/tests/test_get_reviewers_rest.py
@@ -1,40 +1,44 @@
 import json
 import os
-from typing import Callable, Any
+from pathlib import Path
+from typing import Callable
 from unittest.mock import patch
 
 import aiohttp
 import pytest
 from aioresponses import aioresponses
+
 from pr_reviews.queries.get_reviewers_rest import fetch
 
+
 @pytest.fixture
-def read_reviews_file():
+def read_reviews_file() -> str:
     # assume the reviews_response.json file is in the tests/fixtures directory
-    with open("tests/fixtures/reviews_response.json") as file:
-        return json.load(file)
+    with Path("tests/fixtures/reviews_response.json").open("r") as file:
+        return json.dumps(json.load(file))
 
 @pytest.fixture
-def get_reviews_url():
+def get_reviews_url() -> callable:
     def _get_reviews_url(owner: str, repo: str, pull_number: int) -> str:
-        url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
-        return url
+        return f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
     return _get_reviews_url
 
 
 @pytest.fixture
-def mock_aioresponse():
+def mock_aioresponse() -> aioresponses:
     with aioresponses() as m:
         yield m
 
 @pytest.mark.asyncio
 @patch.dict(os.environ, {"GITHUB_TOKEN": "test_token"})
-async def test_get_reviewers_success(read_reviews_file, get_reviews_url, mock_aioresponse):
+async def test_get_reviewers_success(read_reviews_file: str,
+                                     get_reviews_url: Callable[[],str],
+                                     mock_aioresponse: aioresponses) -> None:
     url = get_reviews_url("expressjs", "express", 1)
     mock_aioresponse.get(
         url,
         status=200,
-        payload=read_reviews_file
+        payload=read_reviews_file,
     )
     async with aiohttp.ClientSession() as client:
         response = await fetch(client, url)

From 81876f47424bf4f165a59f15a31ab28202396c1a Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Mon, 6 Jan 2025 14:35:08 -0500
Subject: [PATCH 4/7] feat: async testing

- add a class based test for aioresponses that has the same functionality as the non class based example.
---
 tests/test_get_reviewers_rest_cls.py | 34 ++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 tests/test_get_reviewers_rest_cls.py

diff --git a/tests/test_get_reviewers_rest_cls.py b/tests/test_get_reviewers_rest_cls.py
new file mode 100644
index 0000000..ee8d7ec
--- /dev/null
+++ b/tests/test_get_reviewers_rest_cls.py
@@ -0,0 +1,34 @@
+import asyncio
+import json
+import unittest
+from pathlib import Path
+
+import aiohttp
+from aioresponses import aioresponses
+
+from pr_reviews.queries.get_reviewers_rest import fetch
+
+
+class TestFetchJson(unittest.TestCase):
+    def read_reviews_file(self) -> str:
+        # assume the reviews_response.json file is in
+        # the tests/fixtures directory
+        with Path("tests/fixtures/reviews_response.json").open("r") as file:
+            return json.dumps(json.load(file))
+
+    def get_reviews_url(self, owner: str, repo: str, pull_number: int) -> str:
+            return ("https://api.github.com/repos/"
+                   f"{owner}/{repo}/pulls/{pull_number}/reviews")
+
+    @aioresponses()
+    def test_fetch_json(self, mocked: aioresponses) -> None:
+        url = self.get_reviews_url("expressjs", "express", 1)
+        payload = self.read_reviews_file()
+        mocked.get(url, status=200, payload=payload)
+
+        async def run_test() -> None:
+            async with aiohttp.ClientSession() as session:
+                    result = await fetch(session,url)
+                    assert result == payload
+
+        asyncio.run(run_test())

From 7c2bdb1a536d204f8d3864662870b4bc08d753e5 Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Mon, 6 Jan 2025 14:52:40 -0500
Subject: [PATCH 5/7] feat: async testing

- file renaming
- batch fetch testing
---
 ...ls.py => test_get_reviewers_rest_fetch.py} |  2 +-
 tests/test_get_reviewers_rest_fetch_batch.py  | 37 +++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)
 rename tests/{test_get_reviewers_rest_cls.py => test_get_reviewers_rest_fetch.py} (96%)
 create mode 100644 tests/test_get_reviewers_rest_fetch_batch.py

diff --git a/tests/test_get_reviewers_rest_cls.py b/tests/test_get_reviewers_rest_fetch.py
similarity index 96%
rename from tests/test_get_reviewers_rest_cls.py
rename to tests/test_get_reviewers_rest_fetch.py
index ee8d7ec..d865a0f 100644
--- a/tests/test_get_reviewers_rest_cls.py
+++ b/tests/test_get_reviewers_rest_fetch.py
@@ -9,7 +9,7 @@
 from pr_reviews.queries.get_reviewers_rest import fetch
 
 
-class TestFetchJson(unittest.TestCase):
+class TestFetch(unittest.TestCase):
     def read_reviews_file(self) -> str:
         # assume the reviews_response.json file is in
         # the tests/fixtures directory
diff --git a/tests/test_get_reviewers_rest_fetch_batch.py b/tests/test_get_reviewers_rest_fetch_batch.py
new file mode 100644
index 0000000..33e2921
--- /dev/null
+++ b/tests/test_get_reviewers_rest_fetch_batch.py
@@ -0,0 +1,37 @@
+import asyncio
+import json
+import unittest
+from pathlib import Path
+
+from aioresponses import aioresponses
+
+from pr_reviews.queries.get_reviewers_rest import fetch_batch
+
+
+class TestFetchBatch(unittest.TestCase):
+    def read_reviews_file(self) -> str:
+        # assume the reviews_response.json file is in
+        # the tests/fixtures directory
+        with Path("tests/fixtures/reviews_response.json").open("r") as file:
+            return json.dumps(json.load(file))
+
+    def get_reviews_url(self, owner: str, repo: str, pull_number: int) -> str:
+        return ("https://api.github.com/repos/"
+                f"{owner}/{repo}/pulls/{pull_number}/reviews")
+
+    @aioresponses()
+    def test_fetch_json(self, mocked: aioresponses) -> None:
+        payload = self.read_reviews_file()
+        urls =[]
+        for pull_number in range(2):
+            url = self.get_reviews_url("expressjs",
+                                       "express",
+                                       pull_number)
+            urls.append(url)
+            mocked.get(url, status=200, payload=payload)
+
+        async def run_test() -> None:
+            result = await fetch_batch(urls)
+            assert result == [payload, payload]
+
+        asyncio.run(run_test())

From a82308ff0879865ffcdd8cd2eebc050b5ab4bcf9 Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Tue, 7 Jan 2025 10:13:03 -0500
Subject: [PATCH 6/7] feat: async testing - batch fetch testing

---
 pr_reviews/queries/get_reviewers_rest.py      |  1 +
 tests/get_reviewers/__init__.py               |  0
 .../test_get_reviewers_rest.py                |  0
 .../test_get_reviewers_rest_fetch.py          | 15 ++--------
 .../test_get_reviewers_rest_fetch_batch.py    | 18 ++----------
 .../test_get_reviewers_rest_pull_requests.py  | 29 +++++++++++++++++++
 tests/utils.py                                | 11 +++++++
 7 files changed, 47 insertions(+), 27 deletions(-)
 create mode 100644 tests/get_reviewers/__init__.py
 rename tests/{ => get_reviewers}/test_get_reviewers_rest.py (100%)
 rename tests/{ => get_reviewers}/test_get_reviewers_rest_fetch.py (50%)
 rename tests/{ => get_reviewers}/test_get_reviewers_rest_fetch_batch.py (53%)
 create mode 100644 tests/get_reviewers/test_get_reviewers_rest_pull_requests.py
 create mode 100644 tests/utils.py

diff --git a/pr_reviews/queries/get_reviewers_rest.py b/pr_reviews/queries/get_reviewers_rest.py
index 9aefdf9..f83fa82 100644
--- a/pr_reviews/queries/get_reviewers_rest.py
+++ b/pr_reviews/queries/get_reviewers_rest.py
@@ -42,4 +42,5 @@ def get_reviewers_for_pull_requests(
         for pull_number in pull_numbers
     ]
     reviewers = asyncio.run(fetch_batch(urls))
+    # print(reviewers)
     return [item["user"] for sublist in reviewers for item in sublist]
diff --git a/tests/get_reviewers/__init__.py b/tests/get_reviewers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_get_reviewers_rest.py b/tests/get_reviewers/test_get_reviewers_rest.py
similarity index 100%
rename from tests/test_get_reviewers_rest.py
rename to tests/get_reviewers/test_get_reviewers_rest.py
diff --git a/tests/test_get_reviewers_rest_fetch.py b/tests/get_reviewers/test_get_reviewers_rest_fetch.py
similarity index 50%
rename from tests/test_get_reviewers_rest_fetch.py
rename to tests/get_reviewers/test_get_reviewers_rest_fetch.py
index d865a0f..e030a1f 100644
--- a/tests/test_get_reviewers_rest_fetch.py
+++ b/tests/get_reviewers/test_get_reviewers_rest_fetch.py
@@ -7,23 +7,14 @@
 from aioresponses import aioresponses
 
 from pr_reviews.queries.get_reviewers_rest import fetch
+from tests.utils import read_reviews_file, get_reviews_url
 
 
 class TestFetch(unittest.TestCase):
-    def read_reviews_file(self) -> str:
-        # assume the reviews_response.json file is in
-        # the tests/fixtures directory
-        with Path("tests/fixtures/reviews_response.json").open("r") as file:
-            return json.dumps(json.load(file))
-
-    def get_reviews_url(self, owner: str, repo: str, pull_number: int) -> str:
-            return ("https://api.github.com/repos/"
-                   f"{owner}/{repo}/pulls/{pull_number}/reviews")
-
     @aioresponses()
     def test_fetch_json(self, mocked: aioresponses) -> None:
-        url = self.get_reviews_url("expressjs", "express", 1)
-        payload = self.read_reviews_file()
+        url = get_reviews_url("expressjs", "express", 1)
+        payload = read_reviews_file()
         mocked.get(url, status=200, payload=payload)
 
         async def run_test() -> None:
diff --git a/tests/test_get_reviewers_rest_fetch_batch.py b/tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py
similarity index 53%
rename from tests/test_get_reviewers_rest_fetch_batch.py
rename to tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py
index 33e2921..e801cf7 100644
--- a/tests/test_get_reviewers_rest_fetch_batch.py
+++ b/tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py
@@ -1,30 +1,18 @@
 import asyncio
-import json
 import unittest
-from pathlib import Path
 
 from aioresponses import aioresponses
 
 from pr_reviews.queries.get_reviewers_rest import fetch_batch
-
+from tests.utils import read_reviews_file, get_reviews_url
 
 class TestFetchBatch(unittest.TestCase):
-    def read_reviews_file(self) -> str:
-        # assume the reviews_response.json file is in
-        # the tests/fixtures directory
-        with Path("tests/fixtures/reviews_response.json").open("r") as file:
-            return json.dumps(json.load(file))
-
-    def get_reviews_url(self, owner: str, repo: str, pull_number: int) -> str:
-        return ("https://api.github.com/repos/"
-                f"{owner}/{repo}/pulls/{pull_number}/reviews")
-
     @aioresponses()
     def test_fetch_json(self, mocked: aioresponses) -> None:
-        payload = self.read_reviews_file()
+        payload = read_reviews_file()
         urls =[]
         for pull_number in range(2):
-            url = self.get_reviews_url("expressjs",
+            url = get_reviews_url("expressjs",
                                        "express",
                                        pull_number)
             urls.append(url)
diff --git a/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py b/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py
new file mode 100644
index 0000000..891af43
--- /dev/null
+++ b/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py
@@ -0,0 +1,29 @@
+import asyncio
+import unittest
+
+from aioresponses import aioresponses
+from pr_reviews.queries.get_reviewers_rest import get_reviewers_for_pull_requests
+from tests.utils import read_reviews_file, get_reviews_url
+
+class TestGetReviewers(unittest.TestCase):
+    OWNER = "expressjs"
+    REPO = "express"
+    PULL_REQUESTS = [1, 2]
+    @aioresponses()
+    def test_get_reviewers(self, mocked: aioresponses) -> None:
+        payload = read_reviews_file()
+        urls =[]
+        for pull_number in self.PULL_REQUESTS:
+            url = get_reviews_url(self.OWNER,
+                                  self.REPO,
+                                  pull_number)
+            urls.append(url)
+            mocked.get(url, status=200, payload=payload)
+
+        results = get_reviewers_for_pull_requests(self.OWNER,
+                                                       self.REPO,
+                                                       self.PULL_REQUESTS)
+        print(results[0])
+        assert results[0]['id'] == 1
+
+
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000..6bdc3f3
--- /dev/null
+++ b/tests/utils.py
@@ -0,0 +1,11 @@
+# Python
+from pathlib import Path
+import json
+
+def read_reviews_file() -> str:
+    # assume the reviews_response.json file is in the tests/fixtures directory
+    with Path("tests/fixtures/reviews_response.json").open("r") as file:
+        return json.load(file)
+
+def get_reviews_url(owner: str, repo: str, pull_number: int) -> str:
+    return f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
\ No newline at end of file

From 4a914686c3c90d2fbbc73ce934887bb7691faba1 Mon Sep 17 00:00:00 2001
From: ghinks <ghinks@yahoo.com>
Date: Tue, 7 Jan 2025 10:57:49 -0500
Subject: [PATCH 7/7] feat: async testing - linting , ruff

---
 pr_reviews/queries/get_reviewers_rest.py           |  1 -
 pyproject.toml                                     |  1 +
 .../get_reviewers/test_get_reviewers_rest_fetch.py |  4 +---
 .../test_get_reviewers_rest_fetch_batch.py         |  3 ++-
 .../test_get_reviewers_rest_pull_requests.py       | 14 ++++++++------
 tests/utils.py                                     |  5 +++--
 6 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/pr_reviews/queries/get_reviewers_rest.py b/pr_reviews/queries/get_reviewers_rest.py
index f83fa82..9aefdf9 100644
--- a/pr_reviews/queries/get_reviewers_rest.py
+++ b/pr_reviews/queries/get_reviewers_rest.py
@@ -42,5 +42,4 @@ def get_reviewers_for_pull_requests(
         for pull_number in pull_numbers
     ]
     reviewers = asyncio.run(fetch_batch(urls))
-    # print(reviewers)
     return [item["user"] for sublist in reviewers for item in sublist]
diff --git a/pyproject.toml b/pyproject.toml
index 9e5fb2f..8d15be8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,6 +39,7 @@ exclude = [
     "venv",
     "pr_reviews/**/__init__.py",
     "tests/__init__.py",
+    "tests/get_reviewers/__init__.py"
 ]
 
 [tool.ruff.lint]
diff --git a/tests/get_reviewers/test_get_reviewers_rest_fetch.py b/tests/get_reviewers/test_get_reviewers_rest_fetch.py
index e030a1f..c496068 100644
--- a/tests/get_reviewers/test_get_reviewers_rest_fetch.py
+++ b/tests/get_reviewers/test_get_reviewers_rest_fetch.py
@@ -1,13 +1,11 @@
 import asyncio
-import json
 import unittest
-from pathlib import Path
 
 import aiohttp
 from aioresponses import aioresponses
 
 from pr_reviews.queries.get_reviewers_rest import fetch
-from tests.utils import read_reviews_file, get_reviews_url
+from tests.utils import get_reviews_url, read_reviews_file
 
 
 class TestFetch(unittest.TestCase):
diff --git a/tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py b/tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py
index e801cf7..04c66ac 100644
--- a/tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py
+++ b/tests/get_reviewers/test_get_reviewers_rest_fetch_batch.py
@@ -4,7 +4,8 @@
 from aioresponses import aioresponses
 
 from pr_reviews.queries.get_reviewers_rest import fetch_batch
-from tests.utils import read_reviews_file, get_reviews_url
+from tests.utils import get_reviews_url, read_reviews_file
+
 
 class TestFetchBatch(unittest.TestCase):
     @aioresponses()
diff --git a/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py b/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py
index 891af43..c995f56 100644
--- a/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py
+++ b/tests/get_reviewers/test_get_reviewers_rest_pull_requests.py
@@ -1,14 +1,17 @@
-import asyncio
 import unittest
 
 from aioresponses import aioresponses
-from pr_reviews.queries.get_reviewers_rest import get_reviewers_for_pull_requests
-from tests.utils import read_reviews_file, get_reviews_url
+
+from pr_reviews.queries.get_reviewers_rest import (
+    get_reviewers_for_pull_requests,
+)
+from tests.utils import get_reviews_url, read_reviews_file
+
 
 class TestGetReviewers(unittest.TestCase):
     OWNER = "expressjs"
     REPO = "express"
-    PULL_REQUESTS = [1, 2]
+    PULL_REQUESTS: tuple[int, ...] = (1, 2)
     @aioresponses()
     def test_get_reviewers(self, mocked: aioresponses) -> None:
         payload = read_reviews_file()
@@ -23,7 +26,6 @@ def test_get_reviewers(self, mocked: aioresponses) -> None:
         results = get_reviewers_for_pull_requests(self.OWNER,
                                                        self.REPO,
                                                        self.PULL_REQUESTS)
-        print(results[0])
-        assert results[0]['id'] == 1
+        assert results[0]["id"] == 1
 
 
diff --git a/tests/utils.py b/tests/utils.py
index 6bdc3f3..271690f 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,6 +1,7 @@
 # Python
-from pathlib import Path
 import json
+from pathlib import Path
+
 
 def read_reviews_file() -> str:
     # assume the reviews_response.json file is in the tests/fixtures directory
@@ -8,4 +9,4 @@ def read_reviews_file() -> str:
         return json.load(file)
 
 def get_reviews_url(owner: str, repo: str, pull_number: int) -> str:
-    return f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"
\ No newline at end of file
+    return f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews"