|
3 | 3 | import os |
4 | 4 | import re |
5 | 5 | from pathlib import Path |
| 6 | +from typing import TYPE_CHECKING |
6 | 7 | from unittest import mock |
7 | 8 |
|
8 | 9 | import pytest |
9 | 10 |
|
| 11 | +from pytest_env.plugin import _env_files_from_toml # noqa: PLC2701 |
| 12 | + |
| 13 | +if TYPE_CHECKING: |
| 14 | + import pytest_mock |
| 15 | + |
10 | 16 |
|
11 | 17 | @pytest.mark.parametrize( |
12 | 18 | ("env", "ini", "expected_env"), |
@@ -318,6 +324,176 @@ def test_env_via_toml( # noqa: PLR0913, PLR0917 |
318 | 324 | result.assert_outcomes(passed=1) |
319 | 325 |
|
320 | 326 |
|
| 327 | +@pytest.mark.parametrize( |
| 328 | + ("env", "env_file_content", "config", "expected_env", "config_type"), |
| 329 | + [ |
| 330 | + pytest.param( |
| 331 | + {}, |
| 332 | + "MAGIC=alpha\nSORCERY=beta", |
| 333 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 334 | + {"MAGIC": "alpha", "SORCERY": "beta"}, |
| 335 | + "pyproject", |
| 336 | + id="basic env file via pyproject toml", |
| 337 | + ), |
| 338 | + pytest.param( |
| 339 | + {}, |
| 340 | + "MAGIC=alpha\nSORCERY=beta", |
| 341 | + '[pytest_env]\nenv_files = [".env"]', |
| 342 | + {"MAGIC": "alpha", "SORCERY": "beta"}, |
| 343 | + "pytest.toml", |
| 344 | + id="basic env file via pytest toml", |
| 345 | + ), |
| 346 | + pytest.param( |
| 347 | + {}, |
| 348 | + "MAGIC=alpha\nSORCERY=beta", |
| 349 | + "[pytest]\nenv_files = .env", |
| 350 | + {"MAGIC": "alpha", "SORCERY": "beta"}, |
| 351 | + "ini", |
| 352 | + id="basic env file via ini", |
| 353 | + ), |
| 354 | + pytest.param( |
| 355 | + {}, |
| 356 | + "# comment line\n\nMAGIC=alpha\n # indented comment\n", |
| 357 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 358 | + {"MAGIC": "alpha"}, |
| 359 | + "pyproject", |
| 360 | + id="comments and blank lines", |
| 361 | + ), |
| 362 | + pytest.param( |
| 363 | + {}, |
| 364 | + "SINGLE='hello world'\nDOUBLE=\"hello world\"", |
| 365 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 366 | + {"SINGLE": "hello world", "DOUBLE": "hello world"}, |
| 367 | + "pyproject", |
| 368 | + id="quoted values", |
| 369 | + ), |
| 370 | + pytest.param( |
| 371 | + {}, |
| 372 | + "MAGIC=alpha", |
| 373 | + '[tool.pytest_env]\nenv_files = [".env"]\nMAGIC = "beta"', |
| 374 | + {"MAGIC": "beta"}, |
| 375 | + "pyproject", |
| 376 | + id="inline overrides env file", |
| 377 | + ), |
| 378 | + pytest.param( |
| 379 | + {}, |
| 380 | + "", |
| 381 | + '[tool.pytest_env]\nenv_files = ["missing.env"]', |
| 382 | + {}, |
| 383 | + "pyproject", |
| 384 | + id="missing env file is skipped", |
| 385 | + ), |
| 386 | + pytest.param( |
| 387 | + {}, |
| 388 | + "KEY_ONLY\nVALID=yes", |
| 389 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 390 | + {"VALID": "yes"}, |
| 391 | + "pyproject", |
| 392 | + id="line without equals is skipped", |
| 393 | + ), |
| 394 | + pytest.param( |
| 395 | + {}, |
| 396 | + "MAGIC=has=equals", |
| 397 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 398 | + {"MAGIC": "has=equals"}, |
| 399 | + "pyproject", |
| 400 | + id="value with equals sign", |
| 401 | + ), |
| 402 | + pytest.param( |
| 403 | + {}, |
| 404 | + " MAGIC = alpha ", |
| 405 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 406 | + {"MAGIC": "alpha"}, |
| 407 | + "pyproject", |
| 408 | + id="whitespace around key and value", |
| 409 | + ), |
| 410 | + pytest.param( |
| 411 | + {"MAGIC": "original"}, |
| 412 | + "MAGIC=from_file", |
| 413 | + '[tool.pytest_env]\nenv_files = [".env"]\nMAGIC = {value = "from_file", skip_if_set = true}', |
| 414 | + {"MAGIC": "from_file"}, |
| 415 | + "pyproject", |
| 416 | + id="skip if set respects env file", |
| 417 | + ), |
| 418 | + pytest.param( |
| 419 | + {}, |
| 420 | + "=no_key\nVALID=yes", |
| 421 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 422 | + {"VALID": "yes"}, |
| 423 | + "pyproject", |
| 424 | + id="empty key is skipped", |
| 425 | + ), |
| 426 | + pytest.param( |
| 427 | + {}, |
| 428 | + "", |
| 429 | + '[tool.pytest_env]\nenv_files = "some_value"', |
| 430 | + {"env_files": "some_value"}, |
| 431 | + "pyproject", |
| 432 | + id="env_files as env var when string", |
| 433 | + ), |
| 434 | + pytest.param( |
| 435 | + {}, |
| 436 | + "export MAGIC=alpha", |
| 437 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 438 | + {"MAGIC": "alpha"}, |
| 439 | + "pyproject", |
| 440 | + id="export prefix", |
| 441 | + ), |
| 442 | + pytest.param( |
| 443 | + {}, |
| 444 | + 'MAGIC="hello\\nworld"', |
| 445 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 446 | + {"MAGIC": "hello\nworld"}, |
| 447 | + "pyproject", |
| 448 | + id="escape sequences in double quotes", |
| 449 | + ), |
| 450 | + pytest.param( |
| 451 | + {}, |
| 452 | + "MAGIC=alpha #comment", |
| 453 | + '[tool.pytest_env]\nenv_files = [".env"]', |
| 454 | + {"MAGIC": "alpha"}, |
| 455 | + "pyproject", |
| 456 | + id="inline comment", |
| 457 | + ), |
| 458 | + ], |
| 459 | +) |
| 460 | +def test_env_via_env_file( # noqa: PLR0913, PLR0917 |
| 461 | + testdir: pytest.Testdir, |
| 462 | + env: dict[str, str], |
| 463 | + env_file_content: str, |
| 464 | + config: str, |
| 465 | + expected_env: dict[str, str | None], |
| 466 | + config_type: str, |
| 467 | + request: pytest.FixtureRequest, |
| 468 | +) -> None: |
| 469 | + tmp_dir = Path(str(testdir.tmpdir)) |
| 470 | + test_name = re.sub(r"\W|^(?=\d)", "_", request.node.callspec.id).lower() |
| 471 | + Path(str(tmp_dir / f"test_{test_name}.py")).symlink_to(Path(__file__).parent / "template.py") |
| 472 | + if env_file_content: |
| 473 | + (tmp_dir / ".env").write_text(env_file_content, encoding="utf-8") |
| 474 | + config_file_names = {"pyproject": "pyproject.toml", "pytest.toml": "pytest.toml", "ini": "pytest.ini"} |
| 475 | + (tmp_dir / config_file_names[config_type]).write_text(config, encoding="utf-8") |
| 476 | + |
| 477 | + new_env = { |
| 478 | + **env, |
| 479 | + "_TEST_ENV": repr(expected_env), |
| 480 | + "PYTEST_DISABLE_PLUGIN_AUTOLOAD": "1", |
| 481 | + "PYTEST_PLUGINS": "pytest_env.plugin", |
| 482 | + } |
| 483 | + |
| 484 | + with mock.patch.dict(os.environ, new_env, clear=True): |
| 485 | + result = testdir.runpytest() |
| 486 | + |
| 487 | + result.assert_outcomes(passed=1) |
| 488 | + |
| 489 | + |
| 490 | +def test_env_files_from_toml_bad_toml(tmp_path: Path, mocker: pytest_mock.MockerFixture) -> None: |
| 491 | + (tmp_path / "pyproject.toml").write_text("bad toml", encoding="utf-8") |
| 492 | + config = mocker.MagicMock() |
| 493 | + config.rootpath = tmp_path |
| 494 | + assert _env_files_from_toml(config) == [] |
| 495 | + |
| 496 | + |
321 | 497 | @pytest.mark.parametrize("toml_name", ["pytest.toml", ".pytest.toml", "pyproject.toml"]) |
322 | 498 | def test_env_via_pyproject_toml_bad(testdir: pytest.Testdir, toml_name: str) -> None: |
323 | 499 | toml_file = Path(str(testdir.tmpdir)) / toml_name |
|
0 commit comments