mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-12 03:48:56 +00:00
* test(e2e): move the harness self-tests out of tests/e2e The nightly Buildkite run copies tests/e2e into the runner image and runs bare pytest, so the 672 tests of the harness itself (fixture parsing, JUnit properties, the stack lock, the load aggregators, the Claude Code driver) counted as e2e tests on the status page even though none of them reaches a proxy. They now live in tests/e2e_harness, mirroring the tests/e2e layout, and run in the GitHub Actions lint job and the CircleCI provider_replay_harness job instead * fix(ci): point the providers replay controls at tests/e2e_harness The providers integration job still selected the four replay-control tests under tests/e2e/test_provider_edge.py, so pytest exited before they ran. The raw-HTTP check's file walk also drops to one loop per comprehension * style(tests): mark the raw-HTTP check's bindings Final
137 lines
6.2 KiB
Python
137 lines
6.2 KiB
Python
"""Harness coverage for the custom JUnit properties.
|
|
|
|
No proxy and no ``e2e`` marker. Pins the two normalizations that have to agree
|
|
about where a suite file lives -- ``package_from_nodeid`` (strip the suite root)
|
|
and ``source_from_location`` (re-root at it) -- across both ways the suite is
|
|
launched, plus the one-based line offset and the refusal to emit a path that
|
|
escapes the suite. The consumers of these properties are the Loki/Grafana
|
|
rollups and, for ``source``, the status page's per-test links to GitHub.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import inspect
|
|
from pathlib import Path
|
|
|
|
import junit_properties
|
|
import pytest
|
|
from junit_properties import (
|
|
SUITE_ROOT,
|
|
attach_result_properties,
|
|
dedupe_covers,
|
|
package_from_nodeid,
|
|
result_properties,
|
|
source_from_location,
|
|
suite_parts,
|
|
)
|
|
|
|
|
|
def collected_item(request: pytest.FixtureRequest, name: str) -> pytest.Item:
|
|
"""The Item pytest collected for test ``name`` in this file: the real nodeid,
|
|
location and marker machinery the collection hook reads, as pytest built it."""
|
|
return next(item for item in request.session.items if item.path == request.path and item.name == name)
|
|
|
|
|
|
def repo_root() -> Path | None:
|
|
"""The litellm checkout above this file, or None when there isn't one."""
|
|
return next((p for p in Path(__file__).resolve().parents if (p / ".git").exists()), None)
|
|
|
|
|
|
class TestSuiteParts:
|
|
@pytest.mark.parametrize(
|
|
"path",
|
|
["logging/test_x.py", "tests/e2e/logging/test_x.py", "./logging/test_x.py", "tests\\e2e\\logging\\test_x.py"],
|
|
)
|
|
def test_both_invocation_shapes_collapse_to_the_same_components(self, path: str) -> None:
|
|
"""A repo-root run and a suite-cwd run report the same file differently;
|
|
every downstream signal has to see one spelling."""
|
|
assert suite_parts(path) == ("logging", "test_x.py")
|
|
|
|
def test_top_level_suite_file_keeps_its_single_component(self) -> None:
|
|
assert suite_parts("tests/e2e/test_fixture_mode.py") == ("test_fixture_mode.py",)
|
|
|
|
|
|
class TestPackageFromNodeid:
|
|
@pytest.mark.parametrize(
|
|
("nodeid", "expected"),
|
|
[
|
|
("logging/test_x.py::TestFoo::test_bar", "logging"),
|
|
("tests/e2e/logging/test_x.py::TestFoo::test_bar", "logging"),
|
|
("quota_management/spend_tracking/test_x.py::test_bar", "quota_management"),
|
|
("test_fixture_mode.py::TestParseFixtureMode::test_known_values_normalize", "root"),
|
|
("tests/e2e/test_fixture_mode.py::test_bar", "root"),
|
|
],
|
|
)
|
|
def test_package_is_the_first_dir_under_the_suite_root(self, nodeid: str, expected: str) -> None:
|
|
assert package_from_nodeid(nodeid) == expected
|
|
|
|
|
|
class TestSourceFromLocation:
|
|
@pytest.mark.parametrize("path", ["a2a/test_a2a_agent_e2e.py", "tests/e2e/a2a/test_a2a_agent_e2e.py"])
|
|
def test_path_is_repo_relative_however_pytest_was_started(self, path: str) -> None:
|
|
assert source_from_location(path, 40) == "tests/e2e/a2a/test_a2a_agent_e2e.py:41"
|
|
|
|
def test_line_is_emitted_one_based(self) -> None:
|
|
"""pytest.Item.location counts from 0; editors, tracebacks and GitHub's
|
|
#L anchor all count from 1, and an off-by-one lands on the decorator."""
|
|
assert source_from_location("a2a/test_x.py", 0) == "tests/e2e/a2a/test_x.py:1"
|
|
|
|
def test_top_level_suite_file_sits_directly_under_the_suite_root(self) -> None:
|
|
assert source_from_location("test_fixture_mode.py", 39) == "tests/e2e/test_fixture_mode.py:40"
|
|
|
|
@pytest.mark.parametrize(
|
|
("path", "lineno"),
|
|
[
|
|
("a2a/test_x.py", None),
|
|
("/app/e2e/a2a/test_x.py", 40),
|
|
("C:\\app\\e2e\\a2a\\test_x.py", 40),
|
|
("../conftest.py", 40),
|
|
("", 40),
|
|
],
|
|
)
|
|
def test_nothing_linkable_yields_empty_rather_than_a_guess(self, path: str, lineno: int | None) -> None:
|
|
"""A colon is rejected on two counts: it is how a Windows absolute path
|
|
arrives, and `path:line` cannot represent one in the path half."""
|
|
assert source_from_location(path, lineno) == ""
|
|
|
|
|
|
class TestResultProperties:
|
|
def test_every_test_carries_package_covers_and_source(self, request: pytest.FixtureRequest) -> None:
|
|
"""Read off this test's own collected Item, so the nodeid and location are
|
|
whatever pytest reports for the launch shape in use, and the marker is added
|
|
at run time so the coverage registry's collect-only pass never sees it. The
|
|
source re-roots the location under the suite root as it would for a suite
|
|
file: the constant is hardcoded, not looked up, so a file outside the suite
|
|
gets the same treatment."""
|
|
test = type(self).test_every_test_carries_package_covers_and_source
|
|
request.applymarker(pytest.mark.covers("LOG-1", "LOG-2"))
|
|
assert result_properties(collected_item(request, test.__name__)) == (
|
|
("package", "root"),
|
|
("covers", "LOG-1,LOG-2"),
|
|
("source", f"{SUITE_ROOT}/{Path(__file__).name}:{test.__code__.co_firstlineno}"),
|
|
)
|
|
|
|
def test_attach_is_idempotent(self, request: pytest.FixtureRequest) -> None:
|
|
"""Collection can run the hook more than once; a second pass must not
|
|
double the <property> entries in the report."""
|
|
item = collected_item(request, type(self).test_attach_is_idempotent.__name__)
|
|
attach_result_properties(item)
|
|
attach_result_properties(item)
|
|
assert [name for name, _ in item.user_properties] == ["package", "covers", "source"]
|
|
|
|
|
|
class TestSuiteRoot:
|
|
def test_suite_root_names_the_harness_s_real_home(self) -> None:
|
|
"""SUITE_ROOT is hardcoded because the runner image has no repo to read it
|
|
from. Where there IS a checkout, prove the constant still points at the
|
|
harness -- otherwise a moved tests/e2e/ ships links that 404."""
|
|
root = repo_root()
|
|
if root is None:
|
|
pytest.skip("no checkout above this file")
|
|
harness_home = Path(inspect.getfile(junit_properties)).resolve()
|
|
assert (root / SUITE_ROOT / "junit_properties.py").resolve() == harness_home
|
|
|
|
|
|
class TestDedupeCovers:
|
|
def test_ids_are_unique_order_preserving_and_non_empty_strings(self) -> None:
|
|
assert dedupe_covers([("A", "B"), ("B", ""), ("C", 7)]) == ("A", "B", "C")
|