Ctrl K
ComunidadGuías…

tests/_isolation.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""在乾淨的環境裡跑,而不是在沒有環境的地方跑。

這些測試要證明的是「不會偷偷讀到主機上的憑證或設定」。在 Linux 上,
「把環境變數清光」剛好等於那件事;在 Windows 上不等於,而且在 Linux
上其實也不完全等於:

  - 子行程拿到空環境時,python.exe 連 hash 亂數都取不到就死了
    (Fatal Python error: _Py_HashRandomization_Init),returncode 1、
    stderr 全空——看起來像被測的腳本壞掉,其實它一行都沒跑到。
  - Path.home() 與 expanduser("~") 在 Windows 看 USERPROFILE。清掉之後
    直接 RuntimeError: Could not determine home directory.
  - 反過來,POSIX 上把 HOME 清掉,expanduser 會退回 /etc/passwd 裡的
    真實家目錄。那不是隔離,那是隔離破洞——只是它不會報錯,所以沒人發現。

所以要清掉的是「主機的身分與憑證」,不是「作業系統本身」,而家目錄要
指到一個空目錄、不能不設。
"""
from __future__ import annotations

import os
import tempfile
import unittest
from pathlib import Path

# 作業系統自己要用的變數,跟被測的行為無關。少了它們,Windows 上的
# 子行程根本開不起來。
_WINDOWS_ESSENTIALS = (
    "SystemRoot", "SystemDrive", "WINDIR", "COMSPEC", "PATHEXT",
    "TEMP", "TMP", "NUMBER_OF_PROCESSORS", "PROCESSOR_ARCHITECTURE",
)

_SCRATCH_HOME: str | None = None


def scratch_home() -> str:
    """一個空的家目錄,整個測試行程共用。不是主機的家目錄。"""
    global _SCRATCH_HOME
    if _SCRATCH_HOME is None:
        _SCRATCH_HOME = tempfile.mkdtemp(prefix="xkb-test-home-")
    return _SCRATCH_HOME


def clean_env() -> dict[str, str]:
    """作業系統開得起來的最小集合,加上一個空的家目錄。

    用法是把它展開在測試自己的變數前面:

        with mock.patch.dict(os.environ, {**clean_env(), "XKB_ENV_FILE": ...}, clear=True):
    """
    env = {name: os.environ[name] for name in _WINDOWS_ESSENTIALS if name in os.environ}
    home = scratch_home()
    env["HOME"] = home
    env["USERPROFILE"] = home
    return env


def isolated_env(home: Path | str | None = None, **overrides: str | None) -> dict[str, str]:
    """clean_env() 加上指定的變數;home 同時設 HOME 與 USERPROFILE。

    兩個都要設,因為 expanduser 在 POSIX 讀 HOME、在 Windows 讀
    USERPROFILE——只設一個,隔離就只在其中一個平台成立。
    """
    env = clean_env()
    if home is not None:
        env["HOME"] = str(home)
        env["USERPROFILE"] = str(home)
    env.update({key: value for key, value in overrides.items() if value is not None})
    return env


# fixture 用沒有副檔名、靠 shebang 執行的腳本假裝成 bun / git。Windows 沒有
# 那個 exec 語意,跑不起來的是「假裝」那一層,不是被測的契約。明講跳過,
# 不要讓它以紅燈的樣子留著——紅燈留久了就沒人看了。
needs_posix_exec = unittest.skipIf(
    os.name == "nt",
    "fixture 需要 POSIX 的 shebang 執行語意;這條契約在 VPS / CI 上驗",
)