test: pytest のセッション全体で DEVBASE_ROOT を隔離する (#209) - #217
Conversation
pytest は実行したシェルの環境を継承するため、tmp の root を作るだけで setenv しない
fixture を使うテストが、自前の `monkeypatch.setenv('DEVBASE_ROOT', ...)` を忘れると
実環境の devbase の `projects/` と `secrets/backend.yml` を読む。
fixture 1 つに setenv を足しても同じ穴は他にも残るため、`tests/conftest.py` に autouse
fixture `_isolate_devbase_root` を置き、テストごとの空の tmp の root へ固定する
(#209 の案 B)。autouse は同じ scope の明示の fixture より先に立つので、既存の 32 か所の
setenv は書き換えずに後勝ちで働き、未設定の分岐を試す道 (delenv) も残る。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | kiro | APPROVE
単一ファイル・+23 行の、明確に scope された test 基盤の修正。設計・正確性ともに問題なし。修正提案なし。
確認した点:
- 漏れの前提が実在する。
tests/conftest.py:392のopenbao_rootなど、tmp の root を作るがDEVBASE_ROOTを setenv しない fixture が実際に存在する。 - autouse (function scope) + function-scope の
monkeypatchの組み合わせは正しい。test 側のmonkeypatch.setenv/delenvが後勝ちで働くことを、tests/cli/test_project_dispatch.py:538の delenv 分岐テスト等で確認。 - 別の空 tmp dir を指す設計は無害。openbao 系テストは
SecretStore(openbao_root)のように path を明示で渡すため、os.environ['DEVBASE_ROOT']に依存しない。 - 手元検証:
uvx ruff check --select=E9,F63,F7,F82 tests/conftest.py通過。fake のDEVBASE_ROOTを設定した状態で代表 163 件 (project_dispatch / openbao / secret_injection) を実行し全 pass を再現。 - 回帰テストを追加しない判断は #209 の「件数不変」要件に沿った妥当なトレードオフとして説明されている。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | agy | APPROVE
指摘事項はありません。テスト環境における DEVBASE_ROOT の隔離実装および後勝ち・未設定分岐の挙動に問題がないことを確認しました。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | kiro | APPROVE
tests/conftest.py の autouse fixture でセッション全体の DEVBASE_ROOT を隔離する変更は目的と範囲が一貫している。4 条件 (修正前後 × 未設定/偽 root) で 2863 件すべて緑という検証で、fixture 順序(autouse が先に立ち、テスト側の setenv/delenv が後勝ち)という load-bearing な前提が実証されている。既存 32 か所の setenv や _require_devbase_root 未設定分岐のテストも、テスト本体で自前の setenv/delenv をするため整合する。修正すべき点は見当たらない。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | codex | APPROVE
修正を求める指摘はありません(関連する既存テスト 268 件成功、全件再実行は未実施)。
完了判定(
|
| 段階 | コマンド | 対象範囲 | 実行時刻 | 結果 |
|---|---|---|---|---|
| 全体テスト(受け入れ条件の確認し直し) | DEVBASE_ROOT=<偽の root> uv run pytest -q |
tests/ 全体 |
2026-09-22 06:41 | 2863 passed in 130.22s / exit=0 |
| 静的解析 | uvx ruff check --select=E9,F63,F7,F82 tests/conftest.py lib |
変更ファイル + lib |
2026-09-22 06:45 | All checks passed! / exit=0 |
| 静的解析 | python3 -m compileall -q lib bin |
lib bin |
2026-09-22 06:45 | 出力なし / exit=0 |
light が求める「変更箇所が読み込まれる経路を 1 度実行する」は、tests/conftest.py が
すべての pytest 実行で読み込まれるため、上の全体テストが兼ねている。
受け入れ条件
- autouse で
DEVBASE_ROOTを隔離した状態で、テスト全体の結果が変わらないこと(test: openbao_root fixture が DEVBASE_ROOT を setenv しないため、使う側が忘れると実環境を読む #209 の完了の目安)
→ 本文の 4 条件(修正前後 × 未設定 / 偽の root)がいずれも2863 passed/ exit=0。
今回さらに 1 度実行し直し、同じ2863 passed/ exit=0 を再現した(上の表の 1 行目)
受け入れ条件: 1/1 満たす。
件数について
issue #209 の完了の目安 1 failed, 2861 passed, 1 skipped は dev コンテナ(Ubuntu)で
測った値で、この Mac では 2863 passed(2861 + 1 + 1)になる。総数は一致しており、
テストは 1 件も増減していない。詳細は本文「件数について」を参照。
実装レビュー(/ndf:cross-review)
2 ラウンドで収束した。母集合の 3 者(codex / agy / kiro)すべてがこの差分を見ている。
| round | レビュー | 指摘 |
|---|---|---|
| 1 | agy=APPROVE / kiro=APPROVE | 0 件 |
| 2 | codex=APPROVE / kiro=APPROVE | 0 件 |
最終スイープ後の未解決スレッド: 0 件(GitHub 側で確認済み)。
その他
- 未検証の項目: ShellCheck(この端末に未インストール)。本 PR は
tests/conftest.pyの 1 ファイルのみで shell スクリプトを 1 つも触っていないため、対象外 - 既存の失敗: なし
- 範囲外と判断したもの:
DEVBASE_ROOT以外の 8 つの環境変数の隔離 → test: pytest が継承する DEV_SERVICE_NAME などの環境変数が隔離されておらず、export した端末で 47 件落ちる #218 として起票済み
🤖 Generated with Claude Code
Pull Request
概要
pytest は実行したシェルの
DEVBASE_ROOTを継承する。tmp の root を作るだけで setenv しないfixture(
tests/conftest.pyのopenbao_root、tests/env/test_backend_config.pyのrootなど)を使うテストが、自前の
monkeypatch.setenv('DEVBASE_ROOT', ...)を忘れると、実環境のdevbase の
projects/とsecrets/backend.ymlを読む。ホストの Mac のシェルはDEVBASE_ROOTを持ち、dev コンテナの中は持たないため、環境で再現したりしなかったりする。fixture 1 つに setenv を足しても同じ穴は他にも開いたままになる(
DEVBASE_ROOTを setenvしない tmp の root の fixture は
openbao_rootだけではない)。そこでtests/conftest.pyにautouse fixture を置き、pytest のセッション全体で
DEVBASE_ROOTを隔離する。採ったのは issue #209 の案 B(tmp のディレクトリへ
setenv)で、案 A(delenv)ではない。_require_devbase_rootの「未設定」分岐を試すテストが、その場でdelenvして書けるようにするためである。案 A だと未設定が既定になり、設定済みの分岐を試す側が毎回 setenv を書くことに
なって現状と変わらない。
このブランチの起点は
release/v3.7.0で、release PR は #212 である。関連 Issue
変更点
tests/conftest.pyに autouse fixture_isolate_devbase_rootを追加した。tmp_path_factory.mktemp('devbase-root')が作るテストごとの空のディレクトリへDEVBASE_ROOTをsetenvするtests/conftest.pyの 1 つだけ(+23行)。lib/bin/containers/は触っていない設計上の 3 点
delenv)ではなく tmp のディレクトリを指すsetenvは 1 行も書き換えないsetenvが後勝ちでそのまま働く動作確認
release/v3.7.0を base にした Pull Request では CI が 1 件も動かない(
.github/workflows/ci.ymlのon.pull_request.branchesがmainだけ。gh pr checksはno checks reportedを返す。起票済み: #216)。検証はすべて手元で実行した。実行環境: macOS(Darwin 25.6.0) /
uv run pytest。1. テスト全体が 2 つの条件で同じ結果になること(本命)
偽の root は
secrets/backend.yml(version: 2/layout: group/group_aliases: {default: nyle})とprojects/fake-aprojects/fake-b.envを持つ、リポジトリの外のディレクトリである。DEVBASE_ROOT未設定env -u DEVBASE_ROOT uv run pytest -q2863 passed in 122.75s0DEVBASE_ROOT=<偽の root> uv run pytest -q2863 passed in 126.46s0DEVBASE_ROOT未設定env -u DEVBASE_ROOT uv run pytest -q2863 passed in 135.73s0DEVBASE_ROOT=<偽の root> uv run pytest -q2863 passed in 132.91s0C と D が同一であり、かつ A・B から変わっていない。
env -u DEVBASE_ROOT uv run pytest -q(exit=0)DEVBASE_ROOT=<外側の偽の root> uv run pytest -q(exit=0)件数について(issue #209 の完了の目安との違い)
issue #209 の完了の目安は
1 failed, 2861 passed, 1 skippedである。これは dev コンテナ(Ubuntu。本文の
/tmp/pytest-of-ubuntuがその出所)で測った値で、この Mac では 4 条件すべてが
2863 passedになる。tests/snapshot/test_restore_incremental.py::test_a_generation_with_swapped_directories_restores_completelyがここでは落ちず、skip される 1 件も skip されないためである。
総数は 2861 + 1 + 1 = 2863 で一致しており、テストは 1 件も増減していない。 目安の主旨で
ある「隔離を入れてもテスト全体の結果が変わらないこと」は、同じ機械で測った A・B と C・D の
比較で満たしている。
2. 隔離が実際に効いていること(1 だけでは示せない)
1 の 4 条件はすべて緑である。issue の言うとおり穴は完全に潜在しているため、
1 は「壊していないこと」しか示さない。効いていることは、
openbao_rootだけを使って自前のsetenv をしない使い捨てのテストで確かめた(コミットしていない。下記「テストを足さなかった理由」)。
issue #209 の「実環境を読むことの確認」の再現(修正前は
_grouped_store()が偽の root を指し、修正後は
Noneを返す)である。delenvと後勝ちの setenv も同時に確かめている。3. 静的解析
./bin/devbase --help— 本 PR はtests/しか触らないため対象外(lib/bin/に変更なし)やらないこと
monkeypatch.setenv('DEVBASE_ROOT', ...)の整理DEVBASE_ROOT以外の環境変数の隔離(DEVBASE_ACCOUNT_GROUPDEVBASE_AGE_KEY_FILECOMPOSE_PROJECT_NAMEXDG_CONFIG_HOMEEDITORSHELLDEVBASE_OPEN_INDEXDEV_SERVICE_NAMEPWD)DEVBASE_ROOTの隔離だけを対象にしている。#218 として起票したtests/cli/conftest.pyの wrapper 側の仕組みの変更pyproject.tomlの[tool.pytest.ini_options]に環境変数を書く仕組みの導入pytest-env)が要るテストを足さなかった理由
隔離そのものを確かめるテスト(上の探り)を
tests/へ残すと、テストの件数が 1 件以上増える。issue #209 の完了の目安が件数を含めて変わらないことを求めているため、探りは実行して
証跡だけを残し、コミットしていない。
補足
/ndf:designの「触る領域を決める」の表のうち「すべての変更」以外の領域が 1 つも当たらないため(判断の記録: test: openbao_root fixture が DEVBASE_ROOT を setenv しないため、使う側が忘れると実環境を読む #209 のコメント)
light(tests/だけを触り、本番の振る舞いも本番コードの構造も変えない)🤖 Generated with Claude Code