bd26e9ce6e
Two faults found by running the pipeline inside the Docker image for the first time, both of which let a 25-page run finish "successfully" having stored nothing. The image declares ENV DASHSCOPE_API_KEY= so the variable is visible to docker inspect and overridable at docker run. That leaves it set-but-empty in every container, and load_dotenv used os.environ.setdefault, so the empty string won and the mounted .env was ignored. An empty value now counts as unset; a real value still wins, because overriding on the command line is the point. The consequence was worse than a missing key. Every page failed, the per-page fallback kept the font-mangled embedded text -- exactly the text OCR is there to replace -- and the run went on to spend a full detection pass on it. Tolerating one unreadable page is right; tolerating every page is a configuration error wearing a success message, so Document now raises OcrUnavailable when OCR was attempted and not one page came back, quoting the first real failure. A page range that needed no OCR at all is still not an error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ci5T3gWDhhmqDec3e1XtYZ
70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
"""The .env loader, which is only interesting where it disagrees with itself.
|
|
|
|
These tests exist because of one real incident: the Docker image declares
|
|
``ENV DASHSCOPE_API_KEY=`` so the variable shows up in ``docker inspect`` and can
|
|
be overridden at ``docker run``. That left it set-but-empty inside every container,
|
|
where it shadowed the mounted .env file. Every OCR page failed with "No API key",
|
|
the pipeline fell back page by page as designed, and a 25-page run finished
|
|
successfully having stored nothing.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from leanbench.config import load_dotenv
|
|
|
|
|
|
def write_env(directory: Path, body: str) -> Path:
|
|
path = directory / ".env"
|
|
path.write_text(body, encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def test_an_empty_variable_does_not_shadow_the_file(tmp_path: Path, monkeypatch) -> None:
|
|
"""The incident, in one assertion."""
|
|
monkeypatch.setenv("LEANBENCH_TEST_KEY", "")
|
|
load_dotenv(write_env(tmp_path, "LEANBENCH_TEST_KEY=from-file\n"))
|
|
import os
|
|
|
|
assert os.environ["LEANBENCH_TEST_KEY"] == "from-file"
|
|
|
|
|
|
def test_a_real_value_still_wins(tmp_path: Path, monkeypatch) -> None:
|
|
"""Overriding at the command line must keep working -- that is the whole point."""
|
|
monkeypatch.setenv("LEANBENCH_TEST_KEY", "from-environment")
|
|
load_dotenv(write_env(tmp_path, "LEANBENCH_TEST_KEY=from-file\n"))
|
|
import os
|
|
|
|
assert os.environ["LEANBENCH_TEST_KEY"] == "from-environment"
|
|
|
|
|
|
def test_an_unset_variable_is_taken_from_the_file(tmp_path: Path, monkeypatch) -> None:
|
|
monkeypatch.delenv("LEANBENCH_TEST_KEY", raising=False)
|
|
load_dotenv(write_env(tmp_path, "LEANBENCH_TEST_KEY=from-file\n"))
|
|
import os
|
|
|
|
assert os.environ["LEANBENCH_TEST_KEY"] == "from-file"
|
|
|
|
|
|
def test_comments_blanks_and_quotes(tmp_path: Path, monkeypatch) -> None:
|
|
monkeypatch.delenv("LEANBENCH_A", raising=False)
|
|
monkeypatch.delenv("LEANBENCH_B", raising=False)
|
|
monkeypatch.delenv("LEANBENCH_C", raising=False)
|
|
load_dotenv(write_env(tmp_path, """
|
|
# a comment
|
|
LEANBENCH_A = "quoted value"
|
|
|
|
LEANBENCH_B='single'
|
|
not a pair
|
|
LEANBENCH_C=has=equals=inside
|
|
"""))
|
|
import os
|
|
|
|
assert os.environ["LEANBENCH_A"] == "quoted value"
|
|
assert os.environ["LEANBENCH_B"] == "single"
|
|
assert os.environ["LEANBENCH_C"] == "has=equals=inside"
|
|
|
|
|
|
def test_a_missing_file_is_not_an_error(tmp_path: Path) -> None:
|
|
load_dotenv(tmp_path / "nope.env")
|