mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-04 08:28:19 +08:00
* refactor(persistence): adopt continuation-token pagination Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(pagination): address continuation review findings Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(tui): recover completed list refreshes Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(pagination): address review scalability findings Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(pagination): repair branch validation Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs(go): use page size in template example Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Drew Newberry <anewberry@nvidia.com>
128 lines
4.2 KiB
Python
128 lines
4.2 KiB
Python
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from __future__ import annotations
|
|
|
|
import fcntl
|
|
import os
|
|
import time
|
|
from typing import TYPE_CHECKING
|
|
|
|
import grpc
|
|
import pytest
|
|
|
|
from openshell import Sandbox, SandboxClient, WorkspaceClient
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable, Iterator
|
|
|
|
|
|
def pytest_configure(config: pytest.Config) -> None:
|
|
config.addinivalue_line(
|
|
"markers",
|
|
"exclusive_gateway_config: hold an exclusive lock on gateway-global "
|
|
"config so no other xdist worker observes a transient global setting",
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _gateway_config_guard(
|
|
request: pytest.FixtureRequest,
|
|
tmp_path_factory: pytest.TempPathFactory,
|
|
) -> Iterator[None]:
|
|
"""Readers-writer guard over gateway-global config mutations.
|
|
|
|
The e2e gateway is shared across xdist workers, so a test that flips a
|
|
gateway-global setting can leak that transient value into another worker's
|
|
sandbox creation. Every test holds a shared lock by default; a test marked
|
|
``exclusive_gateway_config`` holds an exclusive lock, so while it mutates and
|
|
restores the setting no other worker is mid-test and none can observe the
|
|
transient value. The lock file lives in the run's shared base temp dir
|
|
(``getbasetemp().parent``), which is common to all xdist workers.
|
|
"""
|
|
lock_path = tmp_path_factory.getbasetemp().parent / "gateway-config.lock"
|
|
exclusive = (
|
|
request.node.get_closest_marker("exclusive_gateway_config") is not None
|
|
)
|
|
with lock_path.open("w") as lock_file:
|
|
fcntl.flock(lock_file, fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH)
|
|
yield
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def cluster_name() -> str | None:
|
|
return os.environ.get("OPENSHELL_GATEWAY")
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def sandbox_client(cluster_name: str | None) -> Iterator[SandboxClient]:
|
|
with SandboxClient.from_active_cluster(cluster=cluster_name) as client:
|
|
yield client
|
|
|
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
|
def ensure_sandbox_persistence_ready(sandbox_client: SandboxClient) -> None:
|
|
for _ in range(60):
|
|
try:
|
|
sandbox_client.list_ids(workspace="default", page_size=1)
|
|
return
|
|
except grpc.RpcError as exc:
|
|
details = exc.details() or ""
|
|
if exc.code() == grpc.StatusCode.UNAVAILABLE:
|
|
time.sleep(2)
|
|
continue
|
|
if (
|
|
exc.code() == grpc.StatusCode.INTERNAL
|
|
and "no such table: objects" in details
|
|
):
|
|
time.sleep(1)
|
|
continue
|
|
raise
|
|
|
|
pytest.fail(
|
|
"openshell-server persistence is not initialized (missing sqlite objects table); "
|
|
"redeploy the active cluster and rerun e2e sandbox tests"
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sandbox(cluster_name: str | None) -> Callable[..., Sandbox]:
|
|
def _create(*, spec: object | None = None, delete_on_exit: bool = True) -> Sandbox:
|
|
return Sandbox(
|
|
workspace="default",
|
|
cluster=cluster_name,
|
|
spec=spec,
|
|
delete_on_exit=delete_on_exit,
|
|
# The sandbox image is large (Python, Node.js, coding agents) so the
|
|
# first pod in the cluster may need extra time for the image pull.
|
|
ready_timeout_seconds=300.0,
|
|
)
|
|
|
|
return _create
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def workspace_client(sandbox_client: SandboxClient) -> WorkspaceClient:
|
|
return WorkspaceClient.from_sandbox_client(sandbox_client)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def _worker_suffix(worker_id: str) -> str:
|
|
"""Return a suffix for worker-unique resource names.
|
|
|
|
Uses the built-in ``worker_id`` fixture from pytest-xdist which returns
|
|
``"gw0"``, ``"gw1"``, etc. for workers, or ``"master"`` for non-xdist runs.
|
|
"""
|
|
if worker_id == "master":
|
|
return ""
|
|
return f"-{worker_id}"
|
|
|
|
|
|
@pytest.fixture
|
|
def run_python() -> Callable[[Sandbox, str], tuple[int, str, str]]:
|
|
def _run(sandbox: Sandbox, code: str) -> tuple[int, str, str]:
|
|
result = sandbox.exec(["python", "-c", code], timeout_seconds=20)
|
|
return result.exit_code, result.stdout, result.stderr
|
|
|
|
return _run
|