mirror of
https://github.com/simonw/datasette.git
synced 2026-05-27 12:34:37 +02:00
Installs a pytest11 entry point so that every Datasette() constructed inside a pytest_runtest_call phase is auto-closed at the end of the test. Fixture-scoped instances are untouched. Opt out via the datasette_autoclose = false ini option. This gives large test suites a safety net against FD exhaustion and leaked write threads from the now-default temp-disk internal database without requiring every existing test to be rewritten. Refs #2692 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
93 lines
2.9 KiB
Python
93 lines
2.9 KiB
Python
"""
|
|
Tests for datasette._pytest_plugin — the pytest plugin that auto-closes
|
|
Datasette instances constructed inside test bodies.
|
|
|
|
These tests drive a real pytest session in a subprocess so the plugin
|
|
operates exactly as it would for a downstream consumer.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).parent.parent
|
|
|
|
|
|
def _run_pytest(tmp_path: Path) -> subprocess.CompletedProcess:
|
|
return subprocess.run(
|
|
[sys.executable, "-m", "pytest", "-v", str(tmp_path)],
|
|
cwd=str(tmp_path),
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
|
|
def test_auto_close_of_instances_made_in_test_body(tmp_path):
|
|
# Two ordered tests:
|
|
# test_a makes a Datasette() and stashes a hard reference
|
|
# test_b asserts that the hard-reffed instance was closed by the plugin
|
|
(tmp_path / "test_sample.py").write_text(textwrap.dedent("""
|
|
from datasette.app import Datasette
|
|
|
|
_stash = {}
|
|
|
|
def test_a():
|
|
ds = Datasette(memory=True)
|
|
_stash["ds"] = ds
|
|
assert ds._closed is False
|
|
|
|
def test_b():
|
|
assert _stash["ds"]._closed is True
|
|
"""))
|
|
result = _run_pytest(tmp_path)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
|
|
|
|
def test_fixture_scoped_instance_is_not_closed(tmp_path):
|
|
# A module-scoped fixture instance must survive across tests in the module.
|
|
(tmp_path / "test_fixture.py").write_text(textwrap.dedent("""
|
|
import pytest
|
|
from datasette.app import Datasette
|
|
|
|
@pytest.fixture(scope="module")
|
|
def ds():
|
|
return Datasette(memory=True)
|
|
|
|
def test_first(ds):
|
|
assert ds._closed is False
|
|
|
|
def test_second(ds):
|
|
# Still alive because the plugin only tracks instances
|
|
# constructed during pytest_runtest_call, not during fixture
|
|
# setup.
|
|
assert ds._closed is False
|
|
"""))
|
|
result = _run_pytest(tmp_path)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
|
|
|
|
def test_opt_out_via_ini(tmp_path):
|
|
# datasette_autoclose = false should leave instances untouched.
|
|
(tmp_path / "pytest.ini").write_text(textwrap.dedent("""
|
|
[pytest]
|
|
datasette_autoclose = false
|
|
""").strip())
|
|
(tmp_path / "test_optout.py").write_text(textwrap.dedent("""
|
|
from datasette.app import Datasette
|
|
|
|
_stash = {}
|
|
|
|
def test_a():
|
|
ds = Datasette(memory=True)
|
|
_stash["ds"] = ds
|
|
|
|
def test_b():
|
|
# Opt-out: plugin must not have closed it.
|
|
assert _stash["ds"]._closed is False
|
|
_stash["ds"].close()
|
|
"""))
|
|
result = _run_pytest(tmp_path)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|