何を見つけたか
tests/conftest.py の openbao_root fixture(tests/conftest.py:369)は tmp のリポジトリを作るが、DEVBASE_ROOT を setenv しない。os.environ から DEVBASE_ROOT を読む経路(lib/devbase/commands/container.py の _check_group_consistency / _grouped_store など)をこの fixture で試すテストは、各自で monkeypatch.setenv('DEVBASE_ROOT', ...) する必要がある。
実行環境によってはシェルが DEVBASE_ROOT を持ち、pytest がそれを継承するため、忘れると実環境の devbase の設定を読む。ホストの Mac のシェルは持ち、dev コンテナの中は持たない。環境で再現したりしなかったりする。PLAN62(#185)のテストでは明示的に setenv して回避した。
実環境を読むことの確認
外側に secrets/backend.yml(layout: group)を持つ偽の root を用意し、openbao_root だけを使うテストを DEVBASE_ROOT=<偽の root> で実行すると、_grouped_store() が tmp ではなく偽の root を指す。
E assert PosixPath('.../fakeroot') == PosixPath('/tmp/pytest-of-ubuntu/pytest-0/test_...0')
E _grouped_store().root = PosixPath('.../fakeroot')
今は赤が出ない
シェルの DEVBASE_ROOT を変えても、テスト全体の結果は変わらない。
| 実行 |
結果 |
env -u DEVBASE_ROOT pytest -q |
1 failed, 2861 passed, 1 skipped |
DEVBASE_ROOT=<偽の root> pytest -q |
1 failed, 2861 passed, 1 skipped |
1 failed は tests/snapshot/test_restore_incremental.py::test_a_generation_with_swapped_directories_restores_completely で、本件とは関わらない。
穴は完全に潜在している。 今あるテストは自前で setenv しているため赤にならず、新しく書いたテストが setenv を忘れたときに初めて実環境を読む。見つかるのは、実環境の projects/ や secrets/backend.yml を変えた誰かの手元で、無関係のテストが落ちたときである。
修正レイヤー
現象レイヤー: tests/conftest.py の openbao_root fixture。
修正レイヤー: pytest のセッション全体の環境の隔離(tests/conftest.py の autouse fixture)。
fixture 1 つに setenv を足しても、同じ穴は他にも開いたままになる。実際、
tests/ には monkeypatch.setenv('DEVBASE_ROOT', ...) を各テストが自分で書いている箇所が 32 か所 / 15 ファイルある
DEVBASE_ROOT を setenv しない tmp の root を作る fixture は openbao_root だけではない(tests/env/test_backend_config.py の root、tests/env/test_runtime.py の root、tests/env/test_secret_store_backend.py の root、tests/plugin/test_migrator.py の devbase_root、tests/commands/test_project_migrate_config.py の devbase_root など)
tests/cli/conftest.py は同じ危険(継承した DEVBASE_ROOT で実環境の projects/ を見る)を CLI のテスト向けに別途 docstring と wrapper の代入で塞いでいる
tests/conftest.py の autouse fixture は _release_shared_secret_store(tests/conftest.py:322)の 1 つだけで、環境変数は隔離していない
pyproject.toml の [tool.pytest.ini_options] にも環境変数の設定は無く、pytest は素の環境を継承する。
採る手: 統合(consolidate_duplication)。tests/conftest.py に autouse fixture を置き、DEVBASE_ROOT をセッション全体で固定する。各テストの明示の setenv はそのまま後勝ちで働くため、既存のテストは書き換えなくてよい。
実装の選択肢
| 案 |
内容 |
既存テストへの影響 |
| A |
autouse で monkeypatch.delenv('DEVBASE_ROOT', raising=False) |
無し |
| B |
autouse で tmp のディレクトリへ setenv |
無し |
B を推す。 _require_devbase_root の「未設定」分岐を試すテストが、その場で明示的に delenv して書けるようになる。A だと未設定が既定になり、設定済みの分岐を試す側が毎回 setenv を書くことになって現状と変わらない。
完了の目安: autouse で DEVBASE_ROOT を隔離した状態で、テスト全体が 1 failed(上記の既存の失敗), 2861 passed, 1 skipped のまま変わらないこと。
由来
PLAN62(#185 / PR #206)の完了判定。範囲外と判断した。
進行
モード: light / 作業ツリー: .worktrees/feature/v3.7.0-test-env-isolation
何を見つけたか
tests/conftest.pyのopenbao_rootfixture(tests/conftest.py:369)は tmp のリポジトリを作るが、DEVBASE_ROOTを setenv しない。os.environからDEVBASE_ROOTを読む経路(lib/devbase/commands/container.pyの_check_group_consistency/_grouped_storeなど)をこの fixture で試すテストは、各自でmonkeypatch.setenv('DEVBASE_ROOT', ...)する必要がある。実行環境によってはシェルが
DEVBASE_ROOTを持ち、pytest がそれを継承するため、忘れると実環境の devbase の設定を読む。ホストの Mac のシェルは持ち、dev コンテナの中は持たない。環境で再現したりしなかったりする。PLAN62(#185)のテストでは明示的に setenv して回避した。実環境を読むことの確認
外側に
secrets/backend.yml(layout: group)を持つ偽の root を用意し、openbao_rootだけを使うテストをDEVBASE_ROOT=<偽の root>で実行すると、_grouped_store()が tmp ではなく偽の root を指す。今は赤が出ない
シェルの
DEVBASE_ROOTを変えても、テスト全体の結果は変わらない。env -u DEVBASE_ROOT pytest -qDEVBASE_ROOT=<偽の root> pytest -q1 failed は
tests/snapshot/test_restore_incremental.py::test_a_generation_with_swapped_directories_restores_completelyで、本件とは関わらない。穴は完全に潜在している。 今あるテストは自前で setenv しているため赤にならず、新しく書いたテストが setenv を忘れたときに初めて実環境を読む。見つかるのは、実環境の
projects/やsecrets/backend.ymlを変えた誰かの手元で、無関係のテストが落ちたときである。修正レイヤー
現象レイヤー:
tests/conftest.pyのopenbao_rootfixture。修正レイヤー: pytest のセッション全体の環境の隔離(
tests/conftest.pyの autouse fixture)。fixture 1 つに setenv を足しても、同じ穴は他にも開いたままになる。実際、
tests/にはmonkeypatch.setenv('DEVBASE_ROOT', ...)を各テストが自分で書いている箇所が 32 か所 / 15 ファイルあるDEVBASE_ROOTを setenv しない tmp の root を作る fixture はopenbao_rootだけではない(tests/env/test_backend_config.pyのroot、tests/env/test_runtime.pyのroot、tests/env/test_secret_store_backend.pyのroot、tests/plugin/test_migrator.pyのdevbase_root、tests/commands/test_project_migrate_config.pyのdevbase_rootなど)tests/cli/conftest.pyは同じ危険(継承したDEVBASE_ROOTで実環境のprojects/を見る)を CLI のテスト向けに別途 docstring と wrapper の代入で塞いでいるtests/conftest.pyの autouse fixture は_release_shared_secret_store(tests/conftest.py:322)の 1 つだけで、環境変数は隔離していないpyproject.tomlの[tool.pytest.ini_options]にも環境変数の設定は無く、pytest は素の環境を継承する。採る手: 統合(
consolidate_duplication)。tests/conftest.pyに autouse fixture を置き、DEVBASE_ROOTをセッション全体で固定する。各テストの明示の setenv はそのまま後勝ちで働くため、既存のテストは書き換えなくてよい。実装の選択肢
monkeypatch.delenv('DEVBASE_ROOT', raising=False)setenvB を推す。
_require_devbase_rootの「未設定」分岐を試すテストが、その場で明示的にdelenvして書けるようになる。A だと未設定が既定になり、設定済みの分岐を試す側が毎回 setenv を書くことになって現状と変わらない。完了の目安: autouse で
DEVBASE_ROOTを隔離した状態で、テスト全体が1 failed(上記の既存の失敗), 2861 passed, 1 skippedのまま変わらないこと。由来
PLAN62(#185 / PR #206)の完了判定。範囲外と判断した。
進行
モード: light / 作業ツリー:
.worktrees/feature/v3.7.0-test-env-isolation