Skip to content

Commit 7bae606

Browse files
committed
experimental use of vcrpy
1 parent a73faf1 commit 7bae606

18 files changed

Lines changed: 4402 additions & 38 deletions

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Recorded GitHub API responses, re-generated rather than edited by hand
2+
tests/cassettes/** linguist-generated=true

.github/workflows/live-api.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Run the test suite against the live GitHub API once a week, to catch
2+
# changes on GitHub's side that the recorded cassettes can't show.
3+
name: live-api
4+
5+
on:
6+
schedule:
7+
# Mondays at 06:00 UTC
8+
- cron: "0 6 * * 1"
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
tests:
16+
if: github.repository == 'executablebooks/github-activity'
17+
runs-on: ubuntu-24.04
18+
env:
19+
GITHUB_ACCESS_TOKEN: "${{ github.token }}"
20+
# Identical requests are only sent once, so the run fits within the
21+
# GITHUB_TOKEN rate limit, see tests/conftest.py
22+
GITHUB_ACTIVITY_LIVE_TESTS: "1"
23+
24+
steps:
25+
- uses: actions/checkout@v7
26+
- uses: actions/setup-python@v7
27+
with:
28+
python-version: "3.14"
29+
- name: Install dependencies
30+
run: |
31+
python -m pip install --upgrade pip
32+
pip install -e ".[testing]"
33+
34+
- name: Run tests
35+
run: pytest

.github/workflows/tests.yaml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,9 @@ jobs:
4141
4242
tests:
4343
runs-on: ubuntu-24.04
44-
env:
45-
GITHUB_ACCESS_TOKEN: "${{ github.token }}"
4644
strategy:
47-
# max-parallel declared to reduce rate limitation issues
48-
max-parallel: 1
4945
matrix:
5046
include:
51-
# Only test oldest supported and latest python version to reduce
52-
# GitHub API calls, as they can get rate limited
5347
- python-version: 3.x
5448
- python-version: "3.10"
5549

@@ -63,8 +57,9 @@ jobs:
6357
python -m pip install --upgrade pip
6458
pip install -e ".[testing]"
6559
60+
# GitHub API responses are replayed from tests/cassettes/
6661
- name: Run tests
67-
run: pytest
62+
run: pytest --block-network
6863

6964
docs:
7065
runs-on: ubuntu-24.04

docs/contribute.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@ This will install the local version of the package and run the test suite.
1111
nox -s test
1212
```
1313

14+
The tests don't call the GitHub API.
15+
Instead they replay responses recorded with [`pytest-recording`](https://github.com/kiwicom/pytest-recording), which are stored in `tests/cassettes/`.
16+
17+
If you change the requests github-activity makes, the tests will fail with `CannotOverwriteExistingCassetteException`.
18+
Re-record the cassettes with a GitHub token (read-only access to public repositories is enough) and commit them:
19+
20+
```bash
21+
GITHUB_ACCESS_TOKEN=... nox -s test -- --record-mode=rewrite
22+
```
23+
24+
Tokens are filtered out of the recordings.
25+
26+
Because the cassettes can't show changes on GitHub's side, the `live-api` workflow also runs the tests against the real GitHub API once a week.
27+
To do the same locally, run:
28+
29+
```bash
30+
GITHUB_ACCESS_TOKEN=... GITHUB_ACTIVITY_LIVE_TESTS=1 nox -s test
31+
```
32+
1433
## Build the documentation
1534

1635
The easiest way to build the documentation locally is using `nox`.

github_activity/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,12 @@ def load_config_and_defaults(args):
171171
setattr(args, argname, config.get(configname, ARG_DEFAULTS.get(configname)))
172172

173173

174-
def main():
174+
def main(argv=None):
175175
if not _git_installed_check():
176176
print("git is required to run github-activity", file=sys.stderr)
177177
sys.exit(1)
178178

179-
args = parser.parse_args()
179+
args = parser.parse_args(argv)
180180
if args.target and args._target:
181181
raise ValueError(
182182
"target cannot be passed as both a positional and keyword argument"

github_activity/github_activity.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -212,25 +212,29 @@ def get_activity(
212212
since_dt_str = f"{since_dt:%Y-%m-%dT%H:%M:%SZ}"
213213
until_dt_str = f"{until_dt:%Y-%m-%dT%H:%M:%SZ}"
214214

215+
# GitHub App requests using user access tokens return no nodes for mixed
216+
# issue/PR searches, so we search for each kind separately.
217+
kinds = ["issue", "pr"]
215218
if kind:
216-
allowed_kinds = ["issue", "pr"]
217-
if kind not in allowed_kinds:
218-
raise ValueError(f"Kind must be one of {allowed_kinds}, got {kind}")
219-
search_query += f" type:{kind}"
219+
if kind not in kinds:
220+
raise ValueError(f"Kind must be one of {kinds}, got {kind}")
221+
kinds = [kind]
220222

221223
# Query for both opened and closed issues/PRs in this window
222224
print(f"Running search query:\n{search_query}\n\n", file=sys.stderr)
223225
query_data = []
224226
all_bot_users = set()
225227
for activity_type in ["created", "closed"]:
226-
ii_search_query = (
227-
search_query + f" {activity_type}:{since_dt_str}..{until_dt_str}"
228-
)
229-
qu = GitHubGraphQlQuery(ii_search_query, auth=auth)
230-
qu.request()
231-
query_data.append(qu.data)
232-
# Collect bot users from each query
233-
all_bot_users.update(qu.data.attrs.get("bot_users", set()))
228+
for ii_kind in kinds:
229+
ii_search_query = (
230+
search_query
231+
+ f" type:{ii_kind} {activity_type}:{since_dt_str}..{until_dt_str}"
232+
)
233+
qu = GitHubGraphQlQuery(ii_search_query, auth=auth)
234+
qu.request()
235+
query_data.append(qu.data)
236+
# Collect bot users from each query
237+
all_bot_users.update(qu.data.attrs.get("bot_users", set()))
234238

235239
query_data = (
236240
pd.concat(query_data).drop_duplicates(subset=["id"]).reset_index(drop=True)

github_activity/graphql.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ def is_bot(user_dict):
322322
self.data = pd.DataFrame(self.issues_and_or_prs)
323323
self.data.attrs["bot_users"] = bot_users
324324

325+
# Issues don't have the PR only fields, so make sure the columns always exist
326+
for column in ["mergedBy", "mergeCommit", "baseRefName", "reviews", "commits"]:
327+
if column not in self.data:
328+
self.data[column] = None
329+
325330
# Add some extra fields
326331
def get_login(user):
327332
return user["login"] if pd.notna(user) else user

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Source = "https://github.com/executablebooks/github-activity"
2525
testing = [
2626
"pytest",
2727
"pytest-cov",
28+
"pytest-recording",
2829
"pytest-regressions",
2930
]
3031
sphinx = [

0 commit comments

Comments
 (0)