Serve documentation from the operator UI

This commit is contained in:
Emil
2026-08-02 15:50:07 +03:00
parent 284aef5d6f
commit f20cc7fe00
72 changed files with 3509 additions and 698 deletions
+5 -1
View File
@@ -1,9 +1,13 @@
from __future__ import annotations
import pytest
from scimesh.cli import main
def test_help_command_prints_runnable_examples(capsys: object) -> None:
def test_help_command_prints_runnable_examples(
capsys: pytest.CaptureFixture[str],
) -> None:
assert main(["help"]) == 0
output = capsys.readouterr().out
assert "scimesh similarity-search" in output
+9 -3
View File
@@ -4,10 +4,14 @@ from __future__ import annotations
from pathlib import Path
import pytest
from scimesh.cli import main
def test_workload_cli_lists_sdk_workloads(capsys: object) -> None:
def test_workload_cli_lists_sdk_workloads(
capsys: pytest.CaptureFixture[str],
) -> None:
assert main(["workload", "list"]) == 0
output = capsys.readouterr().out
assert "descriptor-batch" in output
@@ -16,7 +20,9 @@ def test_workload_cli_lists_sdk_workloads(capsys: object) -> None:
assert "enabled" in output
def test_workload_cli_runs_descriptor_batch(tmp_path: Path, capsys: object) -> None:
def test_workload_cli_runs_descriptor_batch(
tmp_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
dataset = tmp_path / "molecules.tsv"
dataset.write_text(
"chembl_id\tcanonical_smiles\nA\tCCO\nB\tCCCC\nC\tCCN\n",
@@ -107,7 +113,7 @@ def test_workload_cli_rejects_invalid_params_json(tmp_path: Path) -> None:
def test_workload_cli_runs_an_allowlisted_custom_workload(
tmp_path: Path, monkeypatch: object, capsys: object
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
import csv
+2 -1
View File
@@ -12,6 +12,7 @@ import pytest
from scimesh.chemistry.dataset import find_molecule_by_id
from scimesh.sdk import (
ArtifactCollection,
WorkloadManifest,
ArtifactSchema,
CheckpointPolicy,
CompatibilityError,
@@ -498,7 +499,7 @@ def test_local_executor_rejects_non_trusted_trust_modes(tmp_path: Path) -> None:
def _advanced_execution_manifest(
original: WorkloadDefinition,
case: str,
) -> tuple[tuple[str, ...], object]:
) -> tuple[tuple[str, ...], WorkloadManifest]:
"""Declare one negotiable advanced profile the local executor cannot enforce."""
stages = original.manifest.workflow.stages
if case == "process-pool":
+6 -6
View File
@@ -250,9 +250,9 @@ def test_json_backed_values_are_deeply_immutable_and_detached_from_callers() ->
with pytest.raises(TypeError):
manifest.environment.metadata["python"]["version"][0] = 2
with pytest.raises(TypeError):
manifest.inputs["another"] = manifest.inputs["dataset"]
manifest.inputs["another"] = manifest.inputs["dataset"] # type: ignore[index]
with pytest.raises(FrozenInstanceError):
manifest.description = "changed"
manifest.description = "changed" # type: ignore[misc]
def test_collection_kinds_have_distinct_ordering_key_and_duplicate_semantics() -> None:
@@ -324,7 +324,7 @@ def test_workflow_graph_validation_fails_closed_for_unbound_or_inconsistent_depe
WorkflowSpec.from_dict(payload)
payload = workload_manifest().workflow.to_dict()
payload["stages"][0]["needs"] = ["undeclared-stage"]
payload["stages"][0]["needs"] = ["undeclared-stage"] # type: ignore[index]
with pytest.raises(ValueError, match="needs do not match"):
WorkflowSpec.from_dict(payload)
@@ -549,14 +549,14 @@ def test_expansion_is_bound_to_coordinator_parent_and_remaining_budget() -> None
source = ArtifactCollection.single(artifact("dynamic-source"))
planned = ArtifactCollection.single(artifact("dynamic-planned"))
parent = TaskSpec(
**common,
**common, # type: ignore[arg-type]
task_key="root/planner",
stage_id="planner",
inputs={"dataset": source},
expected_outputs={"planned": port},
)
child = TaskSpec(
**common,
**common, # type: ignore[arg-type]
task_key="root/planner/000",
stage_id="compute",
inputs={"dataset": planned},
@@ -992,7 +992,7 @@ def test_allocation_environment_exposes_only_allocation_derived_values() -> None
"ROCR_VISIBLE_DEVICES": "GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
}
with pytest.raises(TypeError):
environment["OMP_NUM_THREADS"] = "1"
environment["OMP_NUM_THREADS"] = "1" # type: ignore[index]
cpu_only = replace(allocation, accelerator_ids=())
assert profile.allocation_environment(cpu_only)["CUDA_VISIBLE_DEVICES"] == ""
+1
View File
@@ -87,6 +87,7 @@ def test_compatibility_failure_occurs_before_planner_invocation(
class CountingPlanner:
calls = 0
entry_point = "tests.sdk_fixture:plan@v1"
def validate(self, request):
self.calls += 1
+1 -1
View File
@@ -92,7 +92,7 @@ def test_resource_inventory_and_requirements_round_trip_without_mutable_aliases(
capabilities["compute"] = "mutated"
assert device.capabilities["compute"] == "9.0"
with pytest.raises(TypeError):
device.capabilities["compute"] = "mutated"
device.capabilities["compute"] = "mutated" # type: ignore[index]
assert ResourceInventory.from_dict(inventory.to_dict()) == inventory
assert ResourceRequirements.from_dict(requirements.to_dict()) == requirements
assert requirements.eligibility_errors(inventory) == ()
+1 -1
View File
@@ -523,7 +523,7 @@ def test_exact_verifier_rejects_owner_equivocation_without_leaking_identity() ->
"candidate_count": 3,
"equivocating_owner_count": 1,
}
assert candidates.candidates[0].owner_id not in json.dumps(decision.to_dict())
assert (candidates.candidates[0].owner_id or "") not in json.dumps(decision.to_dict())
def test_numeric_verifier_accepts_nested_values_with_absolute_and_relative_tolerance() -> None:
+39 -23
View File
@@ -49,7 +49,9 @@ class SeqOpener:
def _exchange_response(token: str, expires_in: int) -> FakeResponse:
return FakeResponse(200, json.dumps({"token": token, "expires_in": expires_in}).encode())
return FakeResponse(
200, json.dumps({"token": token, "expires_in": expires_in}).encode()
)
def test_static_provider_returns_fixed_token_and_never_refreshes():
@@ -68,13 +70,13 @@ def test_worker_key_provider_exchanges_once_then_caches():
provider = WorkerKeyTokenProvider(
"http://users", "scimesh_wk_live_x", timeout=5, now=lambda: clock["t"]
)
provider._opener = SeqOpener([_exchange_response("jwt-1", 100)])
provider._opener = SeqOpener([_exchange_response("jwt-1", 100)]) # type: ignore[reportAttributeAccessIssue]
# First call exchanges; a second call well within the TTL reuses the cache.
assert provider.token() == "jwt-1"
clock["t"] = 1050.0 # 50s later, TTL 100s with 0.2 leeway → refresh at +80s
assert provider.token() == "jwt-1"
assert len(provider._opener.requests) == 1
assert len(provider._opener.requests) == 1 # type: ignore[reportAttributeAccessIssue]
def test_worker_key_provider_refreshes_after_leeway():
@@ -82,22 +84,26 @@ def test_worker_key_provider_refreshes_after_leeway():
provider = WorkerKeyTokenProvider(
"http://users", "k", timeout=5, now=lambda: clock["t"]
)
provider._opener = SeqOpener([
_exchange_response("jwt-1", 100),
_exchange_response("jwt-2", 100),
])
provider._opener = SeqOpener( # type: ignore[reportAttributeAccessIssue]
[
_exchange_response("jwt-1", 100),
_exchange_response("jwt-2", 100),
]
)
assert provider.token() == "jwt-1"
clock["t"] = 85.0 # past the 80s refresh point
assert provider.token() == "jwt-2"
assert len(provider._opener.requests) == 2
assert len(provider._opener.requests) == 2 # type: ignore[reportAttributeAccessIssue]
def test_worker_key_provider_force_refresh():
provider = WorkerKeyTokenProvider("http://users", "k", timeout=5, now=lambda: 0.0)
provider._opener = SeqOpener([
_exchange_response("jwt-1", 100),
_exchange_response("jwt-2", 100),
])
provider._opener = SeqOpener( # type: ignore[reportAttributeAccessIssue]
[
_exchange_response("jwt-1", 100),
_exchange_response("jwt-2", 100),
]
)
assert provider.token() == "jwt-1"
provider.refresh()
assert provider.token() == "jwt-2"
@@ -105,14 +111,18 @@ def test_worker_key_provider_force_refresh():
def test_worker_key_provider_raises_on_rejected_key():
provider = WorkerKeyTokenProvider("http://users", "bad", timeout=5, now=lambda: 0.0)
provider._opener = SeqOpener([HTTPError("http://users", 401, "unauthorized", {}, None)])
provider._opener = SeqOpener( # type: ignore[reportAttributeAccessIssue]
[HTTPError("http://users", 401, "unauthorized", {}, None)] # type: ignore[reportArgumentType]
)
with pytest.raises(TokenExchangeError):
provider.token()
def test_worker_key_provider_raises_when_token_missing():
provider = WorkerKeyTokenProvider("http://users", "k", timeout=5, now=lambda: 0.0)
provider._opener = SeqOpener([FakeResponse(200, json.dumps({"expires_in": 100}).encode())])
provider._opener = SeqOpener( # type: ignore[reportAttributeAccessIssue]
[FakeResponse(200, json.dumps({"expires_in": 100}).encode())]
)
with pytest.raises(TokenExchangeError):
provider.token()
@@ -152,10 +162,12 @@ class RefreshCountingProvider:
def test_coordinator_client_refreshes_and_retries_once_on_401():
provider = RefreshCountingProvider()
client = HttpCoordinatorClient("http://coord", timeout=5, token_provider=provider)
client._opener = SeqOpener([
HTTPError("http://coord/tasks/claim", 401, "unauthorized", {}, None),
FakeResponse(204, b""),
])
client._opener = SeqOpener( # type: ignore[reportAttributeAccessIssue]
[
HTTPError("http://coord/tasks/claim", 401, "unauthorized", {}, None), # type: ignore[reportArgumentType]
FakeResponse(204, b""),
]
)
status, _ = client._request("POST", "/tasks/claim", {"worker_id": "w"})
@@ -168,10 +180,12 @@ def test_coordinator_client_refreshes_and_retries_once_on_401():
def test_coordinator_client_does_not_loop_on_persistent_401():
provider = RefreshCountingProvider()
client = HttpCoordinatorClient("http://coord", timeout=5, token_provider=provider)
client._opener = SeqOpener([
HTTPError("http://coord/x", 401, "unauthorized", {}, None),
HTTPError("http://coord/x", 401, "unauthorized", {}, None),
])
client._opener = SeqOpener( # type: ignore[reportAttributeAccessIssue]
[
HTTPError("http://coord/x", 401, "unauthorized", {}, None), # type: ignore[reportArgumentType]
HTTPError("http://coord/x", 401, "unauthorized", {}, None), # type: ignore[reportArgumentType]
]
)
status, _ = client._request("POST", "/x", {})
@@ -195,7 +209,9 @@ def test_worker_key_requires_userservice_url():
def test_worker_key_with_userservice_url_is_valid():
cfg = WorkerConfig(**_base_config(worker_key="scimesh_wk_live_x", userservice_url="http://users"))
cfg = WorkerConfig(
**_base_config(worker_key="scimesh_wk_live_x", userservice_url="http://users")
)
assert cfg.worker_key == "scimesh_wk_live_x"
assert cfg.userservice_url == "http://users"
+1 -1
View File
@@ -479,7 +479,7 @@ def test_redirect_to_external_storage_strips_authorization() -> None:
headers={"Authorization": "Bearer secret"},
)
redirected = handler.redirect_request(
source, None, 302, "Found", {}, "https://bucket.example/presigned"
source, None, 302, "Found", {}, "https://bucket.example/presigned" # type: ignore[arg-type]
)
assert redirected is not None
assert redirected.get_header("Authorization") is None