Skip to content

Commit

Permalink
add/adapt integration tests for github app auth
Browse files Browse the repository at this point in the history
  • Loading branch information
cbartz committed Jul 31, 2024
1 parent cde2475 commit ec37ec2
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 29 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ jobs:
- name: Run tests (unit + integration)
id: run-tests
env:
GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
AUTH_GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
AUTH_GITHUB_APP_ID : ${{ secrets.TEST_GITHUB_APP_ID }}
AUTH_GITHUB_APP_INSTALLATION_ID : ${{ secrets.TEST_GITHUB_APP_INSTALLATION_ID }}
AUTH_GITHUB_APP_PRIVATE_KEY : ${{ secrets.TEST_GITHUB_APP_PRIVATE_KEY }}
run: |
# Ensure that stdout appears as normal and redirect to file and exit depends on exit code of first command
STDOUT_LOG=$(mktemp --suffix=stdout.log)
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ failing check to be used for testing purposes.
There are two types of test: the application test and the charm test.

### Application tests
To run the application tests, the `GITHUB_TOKEN` environment variable must be set. This
To run the application tests, the `AUTH_GITHUB_TOKEN` environment variable must be set. This
should be a token of a user with full repo permissions for the test repository.
You can also pass in `AUTH_APP_ID`, `AUTH_INSTALLATION_ID`, and `AUTH_PRIVATE_KEY`
to test the authentication using Github App Auth. In that case, the tests will randomly select
either the token or the app auth to run the tests. Note that the Github app should be installed
in the test repository organisation/user namespace, with access granted to the test repository
and in the user namespace that owns the Github token with access to the forked repository.

The command `tox -e test` can be used to run all tests, which are primarily integration tests.
You can also select the repository against which to run the tests by setting
the `--repository` flag. The tests will fork the repository and create PRs against it.
Expand All @@ -66,6 +72,8 @@ bot to test things like comments from a user with no write permissions or above.
GitHub actions should have access to the GitHub token via a secret
called `PERSONAL_GITHUB_TOKEN`. It is recommended to use either a fine-grained PAT or a
token that is short-lived, e.g. 7 days. When it expires, a new token must be set.
For the GitHub App Auth, the `TEST_GITHUB_APP_ID`, `TEST_GIHUB_APP_INSTALLATION_ID`, and `TEST_GITHUB_APPP_RIVATE_KEY`
should be set as secrets.

### Charm tests

Expand Down
26 changes: 14 additions & 12 deletions repo_policy_compliance/github_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@

# Bandit thinks this constant is the real Github token
GITHUB_TOKEN_ENV_NAME = "GITHUB_TOKEN" # nosec
GITHUB_APP_ID_NAME = "GITHUB_APP_ID"
GITHUB_APP_INSTALLATION_ID_NAME = "GITHUB_APP_INSTALLATION_ID"
GITHUB_APP_PRIVATE_KEY_NAME = "GITHUB_APP_PRIVATE_KEY"
GITHUB_APP_ID_ENV_NAME = "GITHUB_APP_ID"
GITHUB_APP_INSTALLATION_ID_ENV_NAME = "GITHUB_APP_INSTALLATION_ID"
GITHUB_APP_PRIVATE_KEY_ENV_NAME = "GITHUB_APP_PRIVATE_KEY"

MISSING_GITHUB_CONFIG_ERR_MSG = (
f"Either the {GITHUB_TOKEN_ENV_NAME} or not all of {GITHUB_APP_ID_NAME},"
f" {GITHUB_APP_INSTALLATION_ID_NAME}, {GITHUB_APP_PRIVATE_KEY_NAME} "
f"Either the {GITHUB_TOKEN_ENV_NAME} or not all of {GITHUB_APP_ID_ENV_NAME},"
f" {GITHUB_APP_INSTALLATION_ID_ENV_NAME}, {GITHUB_APP_PRIVATE_KEY_ENV_NAME} "
f"environment variables were not provided or empty, "
"it is needed for interactions with GitHub, "
)
NOT_ALL_GITHUB_APP_CONFIG_ERR_MSG = (
f"Not all of {GITHUB_APP_ID_NAME}, {GITHUB_APP_INSTALLATION_ID_NAME},"
f" {GITHUB_APP_PRIVATE_KEY_NAME} environment variables were provided, "
f"Not all of {GITHUB_APP_ID_ENV_NAME}, {GITHUB_APP_INSTALLATION_ID_ENV_NAME},"
f" {GITHUB_APP_PRIVATE_KEY_ENV_NAME} environment variables were provided, "
)
# the following is no hardcoded password
PROVIDED_GITHUB_TOKEN_AND_APP_CONFIG_ERR_MSG = ( # nosec
Expand Down Expand Up @@ -77,12 +77,14 @@ def _get_auth() -> Auth:
A GitHub auth object that is configured with a token from the environment.
"""
github_token = os.getenv(GITHUB_TOKEN_ENV_NAME) or os.getenv(f"FLASK_{GITHUB_TOKEN_ENV_NAME}")
github_app_id = os.getenv(GITHUB_APP_ID_NAME) or os.getenv(f"FLASK_{GITHUB_APP_ID_NAME}")
github_app_installation_id_str = os.getenv(GITHUB_APP_INSTALLATION_ID_NAME) or os.getenv(
f"FLASK_{GITHUB_APP_INSTALLATION_ID_NAME}"
github_app_id = os.getenv(GITHUB_APP_ID_ENV_NAME) or os.getenv(
f"FLASK_{GITHUB_APP_ID_ENV_NAME}"
)
github_app_private_key = os.getenv(GITHUB_APP_PRIVATE_KEY_NAME) or os.getenv(
f"FLASK_{GITHUB_APP_PRIVATE_KEY_NAME}"
github_app_installation_id_str = os.getenv(GITHUB_APP_INSTALLATION_ID_ENV_NAME) or os.getenv(
f"FLASK_{GITHUB_APP_INSTALLATION_ID_ENV_NAME}"
)
github_app_private_key = os.getenv(GITHUB_APP_PRIVATE_KEY_ENV_NAME) or os.getenv(
f"FLASK_{GITHUB_APP_PRIVATE_KEY_ENV_NAME}"
)

_ensure_either_github_token_or_app_config(
Expand Down
14 changes: 7 additions & 7 deletions src-docs/github_client.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Module for GitHub client.
**Global Variables**
---------------
- **GITHUB_TOKEN_ENV_NAME**
- **GITHUB_APP_ID_NAME**
- **GITHUB_APP_INSTALLATION_ID_NAME**
- **GITHUB_APP_PRIVATE_KEY_NAME**
- **GITHUB_APP_ID_ENV_NAME**
- **GITHUB_APP_INSTALLATION_ID_ENV_NAME**
- **GITHUB_APP_PRIVATE_KEY_ENV_NAME**
- **MISSING_GITHUB_CONFIG_ERR_MSG**
- **NOT_ALL_GITHUB_APP_CONFIG_ERR_MSG**
- **PROVIDED_GITHUB_TOKEN_AND_APP_CONFIG_ERR_MSG**
Expand Down Expand Up @@ -41,7 +41,7 @@ Get a GitHub client.

---

<a href="../repo_policy_compliance/github_client.py#L183"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../repo_policy_compliance/github_client.py#L185"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>function</kbd> `inject`

Expand All @@ -65,7 +65,7 @@ Injects a GitHub client as the first argument to a function.

---

<a href="../repo_policy_compliance/github_client.py#L236"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../repo_policy_compliance/github_client.py#L238"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>function</kbd> `get_collaborators`

Expand Down Expand Up @@ -95,7 +95,7 @@ Get collaborators with a given affiliation and permission.

---

<a href="../repo_policy_compliance/github_client.py#L271"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../repo_policy_compliance/github_client.py#L273"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>function</kbd> `get_branch`

Expand Down Expand Up @@ -125,7 +125,7 @@ Get the branch for the check.

---

<a href="../repo_policy_compliance/github_client.py#L286"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../repo_policy_compliance/github_client.py#L288"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>function</kbd> `get_collaborator_permission`

Expand Down
60 changes: 52 additions & 8 deletions tests/app/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# See LICENSE file for licensing details.

"""Fixtures for integration tests."""

import logging
import os
import random
from typing import Iterator, cast

import pytest
Expand All @@ -16,13 +17,54 @@
from github.Repository import Repository

import repo_policy_compliance
from repo_policy_compliance.github_client import GITHUB_TOKEN_ENV_NAME, get_collaborators
from repo_policy_compliance.github_client import inject as inject_github_client
from repo_policy_compliance.github_client import (
GITHUB_APP_ID_ENV_NAME,
GITHUB_APP_INSTALLATION_ID_ENV_NAME,
GITHUB_APP_PRIVATE_KEY_ENV_NAME,
GITHUB_TOKEN_ENV_NAME,
get_collaborators,
)

from ...conftest import REPOSITORY_ARGUMENT_NAME
from . import branch_protection
from .types_ import BranchWithProtection, RequestedCollaborator

logger = logging.getLogger(__name__)

TEST_GITHUB_APP_ID_ENV_NAME = f"AUTH_{GITHUB_APP_ID_ENV_NAME}"
TEST_GITHUB_APP_INSTALLATION_ID_ENV_NAME = f"AUTH_{GITHUB_APP_INSTALLATION_ID_ENV_NAME}"
TEST_GITHUB_APP_PRIVATE_KEY_ENV_NAME = f"AUTH_{GITHUB_APP_PRIVATE_KEY_ENV_NAME}"
TEST_GITHUB_TOKEN_ENV_NAME = f"AUTH_{GITHUB_TOKEN_ENV_NAME}"


@pytest.fixture(scope="session", name="randomize_github_auth_method", autouse=True)
def fixture_randomize_github_auth_method(monkeypatch: pytest.MonkeyPatch) -> None:
"""Randomize the GitHub authentication method.
Either use Github Token auth or Github App auth if the latter is set.
This is achieved by monkeypatching the environment variables.
"""
app_id = os.getenv(TEST_GITHUB_APP_ID_ENV_NAME)
app_install_id = os.getenv(TEST_GITHUB_APP_INSTALLATION_ID_ENV_NAME)
app_private_key = os.getenv(TEST_GITHUB_APP_PRIVATE_KEY_ENV_NAME)
github_token = os.getenv(TEST_GITHUB_TOKEN_ENV_NAME)

# random is not used for security purposes
if random.random() < 0.5 and app_id and app_install_id and app_private_key: # nosec
monkeypatch.delenv(GITHUB_TOKEN_ENV_NAME, raising=False)
monkeypatch.setenv(GITHUB_APP_ID_ENV_NAME, app_id)
monkeypatch.setenv(GITHUB_APP_INSTALLATION_ID_ENV_NAME, app_install_id)
monkeypatch.setenv(GITHUB_APP_PRIVATE_KEY_ENV_NAME, app_private_key)
logger.info("Using GitHub App authentication for this test.")
else:
assert (
github_token
), f"GitHub token must be set in the environment variable {TEST_GITHUB_TOKEN_ENV_NAME}"
monkeypatch.delenv(GITHUB_APP_ID_ENV_NAME, raising=False)
monkeypatch.delenv(GITHUB_APP_INSTALLATION_ID_ENV_NAME, raising=False)
monkeypatch.delenv(GITHUB_APP_PRIVATE_KEY_ENV_NAME, raising=False)
monkeypatch.setenv(GITHUB_TOKEN_ENV_NAME, github_token)


@pytest.fixture(scope="session", name="github_repository_name")
def fixture_github_repository_name(pytestconfig: pytest.Config) -> str:
Expand All @@ -31,10 +73,12 @@ def fixture_github_repository_name(pytestconfig: pytest.Config) -> str:


@pytest.fixture(scope="session", name="github_token")
def fixutre_github_token() -> str:
def fixture_github_token() -> str:
"""Get the GitHub token from the environment."""
github_token = os.getenv(GITHUB_TOKEN_ENV_NAME)
assert github_token, f"GitHub must be set in the environment variable {GITHUB_TOKEN_ENV_NAME}"
github_token = os.getenv(TEST_GITHUB_TOKEN_ENV_NAME)
assert (
github_token
), f"GitHub must be set in the environment variable {TEST_GITHUB_TOKEN_ENV_NAME}"
return github_token


Expand Down Expand Up @@ -64,9 +108,9 @@ def fixture_ci_github_repository(


@pytest.fixture(scope="session", name="github_repository")
def fixture_github_repository(github_repository_name: str) -> Repository:
def fixture_github_repository(github_repository_name: str, github_token: str) -> Repository:
"""Returns client to the Github repository."""
github_client = inject_github_client(lambda client: client)()
github_client = Github(auth=Token(github_token))
return github_client.get_repo(github_repository_name)


Expand Down

0 comments on commit ec37ec2

Please sign in to comment.