pytest prints a failing test's fixture arguments via repr() as part of the default traceback header — no -v or --showlocals needed — so any test taking the test_database_url fixture and failing printed the real TEST_DATABASE_URL straight into the terminal, password included. test_database_url now returns a MaskedURL: a str subclass identical to a plain string for every functional use (passed to .replace(), urlsplit(), a subprocess environment) but whose repr() masks the password while still showing host and database name. __str__ is untouched, so every caller still connects with the real value. tests/test_conftest.py proves it, both for the fixture itself and for MaskedURL's string behaviour in isolation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
44 lines
1.9 KiB
Python
44 lines
1.9 KiB
Python
"""Tests for conftest.py's own test-infrastructure fixtures.
|
|
|
|
core-api T-12: a failing database test used to print the test database
|
|
password, because pytest renders a failing test's fixture arguments with
|
|
`repr()` — no `-v` or `--showlocals` needed to trigger it, just a test that
|
|
takes `test_database_url` and fails. `MaskedURL` (conftest.py) is the fix:
|
|
same string, same connection, but a `repr()` that never shows the password.
|
|
"""
|
|
from urllib.parse import urlsplit
|
|
|
|
from tests.conftest import MaskedURL
|
|
|
|
|
|
def test_database_url_fixture_renders_without_its_password(test_database_url):
|
|
"""Mutation check: revert `MaskedURL.__repr__` to the inherited `str`
|
|
one (or hand back a plain `str` from the fixture) and this fails — the
|
|
real password would appear in `repr(test_database_url)`, exactly as it
|
|
did in a failing test's traceback before T-12."""
|
|
password = urlsplit(test_database_url).password
|
|
assert password, "fixture must carry a real password for this test to mean anything"
|
|
|
|
rendered = repr(test_database_url)
|
|
|
|
assert password not in rendered
|
|
# Host and database name are still visible — only the password is masked.
|
|
assert "***" in rendered
|
|
assert urlsplit(test_database_url).path in rendered
|
|
|
|
|
|
def test_masked_url_is_still_a_fully_usable_string():
|
|
"""The wrapper changes only `repr()`; every other string operation the
|
|
rest of the suite relies on (`.replace()`, `urlsplit()`, use as a
|
|
subprocess environment value) must see the real value unchanged."""
|
|
raw = "postgresql://core_api_test:s3cr3t@localhost:5432/core_api_test"
|
|
masked = MaskedURL(raw)
|
|
|
|
assert masked == raw
|
|
assert str(masked) == raw
|
|
assert masked.replace("postgresql://", "postgresql+asyncpg://", 1) == raw.replace(
|
|
"postgresql://", "postgresql+asyncpg://", 1
|
|
)
|
|
assert urlsplit(masked).password == "s3cr3t"
|
|
assert "s3cr3t" not in repr(masked)
|