Skip to content

Commit

Permalink
Merge pull request #227 from praw-dev/ensure_no_unused_cassettes
Browse files Browse the repository at this point in the history
Ensure no unused cassettes
  • Loading branch information
LilSpazJoekp committed Dec 26, 2022
2 parents 7aa91e8 + 779ff38 commit b2064be
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 201 deletions.
182 changes: 93 additions & 89 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,115 +3,119 @@ jobs:
needs: test-multi-python
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
lint-multi-os:
name: Lint ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-lint-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-lint-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
pip install .[lint]
- name: Run pre-commit hooks
uses: pre-commit/action@v2.0.3
- name: Run sphinx
run: sphinx-build -W --keep-going docs/ /tmp/foo
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-lint-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-lint-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
pip install .[lint]
- name: Run pre-commit hooks
uses: pre-commit/action@v2.0.3
- name: Run sphinx
run: sphinx-build -W --keep-going docs/ /tmp/foo
strategy:
matrix:
os: [macOS-latest, ubuntu-latest, windows-latest]
os: [ macOS-latest, ubuntu-latest, windows-latest ]
test-multi-os:
name: Test ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-test-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-test-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Test with pytest
run: pytest
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-test-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-test-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Test with pytest
env:
ENSURE_NO_UNUSED_CASSETTES: 1
run: pytest
strategy:
matrix:
os: [macOS-latest, ubuntu-latest, windows-latest]
os: [ macOS-latest, ubuntu-latest, windows-latest ]
test-multi-python:
name: Test Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-test-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-test-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[ci,test]
- name: Test with pytest
run: coverage run --source asyncpraw --module pytest
- env:
COVERALLS_PARALLEL: true
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: Submit to coveralls
run: coveralls --service=github
- name: Check coverage
run: coverage report -m --fail-under=100
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-test-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-test-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[ci,test]
- name: Test with pytest
run: coverage run --source asyncpraw --module pytest
env:
ENSURE_NO_UNUSED_CASSETTES: 1
- name: Submit to coveralls
env:
COVERALLS_PARALLEL: true
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: coveralls --service=github
- name: Check coverage
run: coverage report -m --fail-under=100
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, '3.10']
python-version: [ 3.7, 3.8, 3.9, '3.10' ]
test-network:
name: Test Network Request
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-test-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-test-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Run network test
run: pytest tests/integration/test_github_actions.py::test_github_actions
env:
NETWORK_TEST_CLIENT_ID: ${{ secrets.NETWORK_TEST_CLIENT_ID }}
NETWORK_TEST_CLIENT_SECRET: ${{ secrets.NETWORK_TEST_CLIENT_SECRET }}
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.x
- uses: actions/cache@v1
with:
key: v0-${{ runner.os }}-pip-test-${{ hashFiles('setup.py') }}
path: ~/.cache/pip
restore-keys: |
v0-${{ runner.os }}-pip-test-
v0-${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Run network test
run: pytest tests/integration/test_github_actions.py::test_github_actions
env:
NETWORK_TEST_CLIENT_ID: ${{ secrets.NETWORK_TEST_CLIENT_ID }}
NETWORK_TEST_CLIENT_SECRET: ${{ secrets.NETWORK_TEST_CLIENT_SECRET }}
name: CI
on: [pull_request, push]
on: [ pull_request, push ]
37 changes: 36 additions & 1 deletion docs/package_info/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,41 @@ to first delete it, and then rerun the test suite.
Please always verify that only the requests you expect to be made are contained within
your cassette.

There are a few pytest markers that can be used to control how cassettes are recorded or
used.

- ``@pytest.mark.add_placeholder``: Allows you to add custom placeholders to the
cassette. This can be useful when you want to record dynamic or generated data, but do
not want it to be saved in the cassette. This marker takes at least one keyword
argument and can be applied to a test class or individual test methods. It can also be
applied multiple times.
- ``@pytest.mark.cassette_name``: Allows you to set the cassette name. This can be
useful when you want to use a cassette that was recorded by another test.
- ``@pytest.mark.recorder_kwargs``: Allows you to pass additional arguments to the
recorder. This can be useful if you need to specify parameters for the
``VCR.use_cassette`` method. Like the ``add_placeholder`` marker, this marker can be
applied to a test class or individual test methods and can be applied multiple times.

Examples:

.. code-block:: python
@pytest.mark.recorder_kwargs(allow_playback_repeats=True)
class TestClass:
@pytest.mark.recorder_kwargs(match_on=["uri", "method", "body"])
async def test_example(self):
...
@pytest.mark.cassette_name("TestClass.test_example")
@pytest.mark.recorder_kwargs(match_on=["uri", "method", "body"])
async def test_example__different_assertion(self):
...
@pytest.mark.add_placeholder(generated_data_a=generate_data_a())
@pytest.mark.add_placeholder(generated_data_b=generate_data_b())
async def test_example__with_generated_placeholders(self):
...
Documentation
-------------

Expand Down Expand Up @@ -261,7 +296,7 @@ the ``AUTHORS.rst`` file.
CHANGES.rst
~~~~~~~~~~~

For feature additions, bugfixes, or code removal please add an appropriate entry to
For feature additions, bug fixes, or code removal please add an appropriate entry to
``CHANGES.rst``. If the ``Unreleased`` section does not exist at the top of
``CHANGES.rst`` please add it. See `commit 280525c16ba28cdd69cdbb272a0e2764b1c7e6a0`_
for an example.
Expand Down
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def _get_path(name):

def pytest_configure(config):
pytest.placeholders = Placeholders(placeholders)
config.addinivalue_line(
"markers", "add_placeholder: Define an additional placeholder for the cassette."
)
config.addinivalue_line(
"markers", "cassette_name: Name of cassette to use for test."
)
Expand Down
74 changes: 39 additions & 35 deletions tests/integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,59 @@ def cassette_name(self, request, vcr_cassette_name):
return vcr_cassette_name
return marker.args[0]

@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(autouse=True, scope="session")
def cassette_tracker(self):
"""Return a dictionary to track cassettes."""
"""Track cassettes to ensure unused cassettes are not uploaded."""
global existing_cassettes
for cassette in os.listdir(CASSETTES_PATH):
if cassette.endswith(".json"):
existing_cassettes.add(cassette.replace(".json", ""))
existing_cassettes.add(cassette[: cassette.rindex(".")])
yield
unused_cassettes = existing_cassettes - used_cassettes
if unused_cassettes and os.getenv("ensure_no_unused_cassettes", "0") == "1":
if unused_cassettes and os.getenv("ENSURE_NO_UNUSED_CASSETTES", "0") == "1":
raise AssertionError(
f"The following cassettes are unused: {', '.join(unused_cassettes)}."
)

@pytest.fixture(autouse=True)
def cassette(self, request, recorder, cassette_name):
"""Wrap a test in a VCR cassette."""
global used_cassettes
kwargs = {}
for marker in request.node.iter_markers("add_placeholder"):
recorder.persister.add_additional_placeholders(marker.kwargs)
for marker in request.node.iter_markers("recorder_kwargs"):
for key, value in marker.kwargs.items():
# Don't overwrite existing values since function markers are provided
# before class markers.
kwargs.setdefault(key, value)
with recorder.use_cassette(cassette_name, **kwargs) as cassette:
if not cassette.write_protected:
ensure_environment_variables()
yield cassette
ensure_integration_test(cassette)
used_cassettes.add(cassette_name)

@pytest.fixture(autouse=True)
def read_only(self, reddit):
"""Make reddit instance read-only."""
"""Make the Reddit instance read-only."""
# Require tests to explicitly disable read_only mode.
reddit.read_only = True

@pytest.fixture(autouse=True)
def recorder(self):
"""Configure VCR."""
vcr = VCR()
vcr.before_record_response = filter_access_token
vcr.cassette_library_dir = CASSETTES_PATH
vcr.decode_compressed_response = True
vcr.match_on = ["uri", "method"]
vcr.path_transformer = VCR.ensure_suffix(".json")
vcr.register_persister(CustomPersister)
vcr.register_serializer("custom_serializer", CustomSerializer)
vcr.serializer = "custom_serializer"
yield vcr
CustomPersister.additional_placeholders = {}

@pytest.fixture
async def reddit(self, vcr, event_loop: asyncio.AbstractEventLoop):
"""Configure Reddit."""
Expand All @@ -75,32 +108,3 @@ async def reddit(self, vcr, event_loop: asyncio.AbstractEventLoop):

async with Reddit(**reddit_kwargs) as reddit_instance:
yield reddit_instance

@pytest.fixture(autouse=True)
def vcr(self):
"""Configure VCR instance."""
vcr = VCR()
vcr.before_record_response = filter_access_token
vcr.cassette_library_dir = CASSETTES_PATH
vcr.decode_compressed_response = True
vcr.match_on = ["uri", "method"]
vcr.path_transformer = VCR.ensure_suffix(".json")
vcr.register_persister(CustomPersister)
vcr.register_serializer("custom_serializer", CustomSerializer)
vcr.serializer = "custom_serializer"
yield vcr

@pytest.fixture(autouse=True)
def vcr_cassette(self, request, vcr, cassette_name):
"""Wrap a test in a VCR.py cassette"""
global used_cassettes
kwargs = {}
marker = request.node.get_closest_marker("recorder_kwargs")
if marker is not None:
kwargs.update(kwargs)
with vcr.use_cassette(cassette_name, **kwargs) as cassette:
if not cassette.write_protected:
ensure_environment_variables()
yield cassette
ensure_integration_test(cassette)
used_cassettes.add(cassette_name)
Original file line number Diff line number Diff line change
Expand Up @@ -485,5 +485,6 @@
}
}
],
"recorded_at": "2022-12-11T18:58:51",
"version": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,5 +206,6 @@
}
}
],
"recorded_at": "2022-12-11T18:58:28",
"version": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,5 +405,6 @@
}
}
],
"recorded_at": "2022-12-11T18:58:28",
"version": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,5 +405,6 @@
}
}
],
"recorded_at": "2022-12-11T18:58:28",
"version": 1
}

0 comments on commit b2064be

Please sign in to comment.