revert check fix, add test cov
This commit is contained in:
@@ -452,13 +452,13 @@ Do not change catalog refresh behavior yet. The adapter remains the temporary ex
|
||||
In `tests/wf_mcp/test_workflow_wrappers.py`, replace:
|
||||
|
||||
```python
|
||||
adapter = (cast(BackendAdapter, adapter),)
|
||||
adapter = cast(BackendAdapter, adapter)
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```python
|
||||
executor = (cast(ToolExecutor, adapter),)
|
||||
executor = cast(ToolExecutor, adapter)
|
||||
```
|
||||
|
||||
and import:
|
||||
|
||||
@@ -13,23 +13,6 @@ from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _utf8_subprocess_env() -> dict[str, str]:
|
||||
"""Force child tools toward UTF-8 so captured agent output is decodable.
|
||||
|
||||
OpenCode emits UTF-8 JSONL, but Windows defaults Python's subprocess text
|
||||
decoding to the active ANSI code page unless an encoding is supplied. The
|
||||
environment nudges child Python tools too; the explicit subprocess encoding
|
||||
below is the actual guard against cp1252 reader-thread crashes.
|
||||
"""
|
||||
env = dict(os.environ)
|
||||
env.setdefault("PYTHONUTF8", "1")
|
||||
env.setdefault("PYTHONIOENCODING", "utf-8")
|
||||
return env
|
||||
|
||||
|
||||
from examples.agent_challenges.names import (
|
||||
short_challenge_name,
|
||||
short_model_name,
|
||||
@@ -64,6 +47,22 @@ from examples.agent_challenges.workspace import (
|
||||
wf_command_prefix_for_config,
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def _utf8_subprocess_env() -> dict[str, str]:
|
||||
"""Force child tools toward UTF-8 so captured agent output is decodable.
|
||||
|
||||
OpenCode emits UTF-8 JSONL, but Windows defaults Python's subprocess text
|
||||
decoding to the active ANSI code page unless an encoding is supplied. The
|
||||
environment nudges child Python tools too; the explicit subprocess encoding
|
||||
below is the actual guard against cp1252 reader-thread crashes.
|
||||
"""
|
||||
env = dict(os.environ)
|
||||
env.setdefault("PYTHONUTF8", "1")
|
||||
env.setdefault("PYTHONIOENCODING", "utf-8")
|
||||
return env
|
||||
|
||||
|
||||
def _opencode_trial_title(
|
||||
*, challenge_id: str, model: str, profile: str, index: int
|
||||
|
||||
+9
-1
@@ -52,6 +52,14 @@ typeCheckingMode = "basic" # too many errors
|
||||
|
||||
[tool.ruff.format]
|
||||
preview = false
|
||||
# Ruff 0.16 formats Markdown by default, but many docs contain partial Python
|
||||
# call fragments that are illustrative rather than standalone programs.
|
||||
exclude = ["**/*.md"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
extend-select = ["I"]
|
||||
# Keep the established lint contract stable across Ruff default-rule changes.
|
||||
select = ["E4", "E7", "E9", "F", "I"]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
# This generator adjusts sys.path before importing repository-local modules.
|
||||
"docs/thesis/generate_agent_challenge_evaluation.py" = ["E402"]
|
||||
|
||||
@@ -53,7 +53,7 @@ def workflow_mcp_source_to_connection_config(source: object) -> ConnectionConfig
|
||||
for field in ("id", "provider", "account", "enabled", "ownership", "transport"):
|
||||
if getattr(source, field, None) is None:
|
||||
raise ValueError(f"wf_config MCP source missing required field: {field}")
|
||||
transport = source.transport
|
||||
transport = getattr(source, "transport")
|
||||
metadata = dict(getattr(source, "metadata", {}))
|
||||
if transport.kind == "stdio":
|
||||
metadata.update(
|
||||
@@ -83,12 +83,12 @@ def workflow_mcp_source_to_connection_config(source: object) -> ConnectionConfig
|
||||
if auth_ref is not None:
|
||||
metadata["auth_ref"] = auth_ref
|
||||
return ConnectionConfig(
|
||||
id=source.id,
|
||||
server=source.provider,
|
||||
account=source.account,
|
||||
enabled=source.enabled,
|
||||
id=getattr(source, "id"),
|
||||
server=getattr(source, "provider"),
|
||||
account=getattr(source, "account"),
|
||||
enabled=getattr(source, "enabled"),
|
||||
metadata=metadata,
|
||||
source_config_ownership=source.ownership,
|
||||
source_config_ownership=getattr(source, "ownership"),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from typing import NoReturn
|
||||
|
||||
import fastapi_jsonrpc as jsonrpc
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel, ConfigDict, ValidationError
|
||||
|
||||
|
||||
class WorkflowRpcError(jsonrpc.BaseError):
|
||||
@@ -22,6 +22,11 @@ class WorkflowRpcError(jsonrpc.BaseError):
|
||||
def raise_workflow_rpc_error(exc: Exception) -> NoReturn:
|
||||
"""Map expected application exceptions without swallowing programming bugs."""
|
||||
|
||||
# DTO validation happens before handlers, while complete-document validation
|
||||
# happens inside the service. Both are invalid JSON-RPC parameters.
|
||||
if isinstance(exc, ValidationError):
|
||||
raise jsonrpc.InvalidParams(data={"message": str(exc)}) from exc
|
||||
|
||||
raise WorkflowRpcError(
|
||||
data={
|
||||
"code": exc.__class__.__name__,
|
||||
|
||||
@@ -112,4 +112,4 @@ def test_context_runtime_runner_uses_workflow_runtime_service(tmp_path: Path) ->
|
||||
service = WfMcpService(store=FileStore(tmp_path / "context_runtime"))
|
||||
context = context_from_service(service)
|
||||
|
||||
assert context.runtime.runtime is service.workflow_runtime
|
||||
assert getattr(context.runtime, "runtime") is service.workflow_runtime
|
||||
|
||||
@@ -467,6 +467,81 @@ async def test_rpc_replace_document_replaces_complete_draft_workspace(
|
||||
assert after_malformed["result"] == inspected["result"]
|
||||
|
||||
|
||||
async def test_rpc_replace_document_rejects_invalid_object_without_mutation(
|
||||
tmp_path,
|
||||
) -> None:
|
||||
server = build_local_static_workflow_server(tmp_path / "store")
|
||||
app = create_rpc_app(server)
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.create_empty",
|
||||
{"workspace_id": "report", "name": "initial"},
|
||||
)
|
||||
before = await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.get",
|
||||
{"workspace_id": "report", "include_draft": True},
|
||||
)
|
||||
malformed = await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.replace_document",
|
||||
{
|
||||
"workspace_id": "report",
|
||||
"revision": 1,
|
||||
"draft": {"name": "missing-required-fields"},
|
||||
},
|
||||
)
|
||||
after = await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.get",
|
||||
{"workspace_id": "report", "include_draft": True},
|
||||
)
|
||||
|
||||
assert malformed["error"]["code"] == -32602
|
||||
assert after["result"] == before["result"]
|
||||
|
||||
|
||||
async def test_rpc_replace_document_persists_semantically_invalid_draft(
|
||||
tmp_path,
|
||||
) -> None:
|
||||
server = build_local_static_workflow_server(tmp_path / "store")
|
||||
app = create_rpc_app(server)
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.create_empty",
|
||||
{"workspace_id": "report", "name": "initial"},
|
||||
)
|
||||
initial = await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.get",
|
||||
{"workspace_id": "report", "include_draft": True},
|
||||
)
|
||||
replacement = {**initial["result"]["draft"], "start": "missing_step"}
|
||||
replaced = await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.replace_document",
|
||||
{
|
||||
"workspace_id": "report",
|
||||
"revision": 1,
|
||||
"draft": replacement,
|
||||
},
|
||||
)
|
||||
after = await _rpc(
|
||||
client,
|
||||
"workflow.draft_workspaces.get",
|
||||
{"workspace_id": "report", "include_draft": True},
|
||||
)
|
||||
|
||||
assert replaced["result"]["status"] == "invalid"
|
||||
assert replaced["result"]["diagnostics"]
|
||||
assert after["result"]["draft"]["start"] == "missing_step"
|
||||
assert after["result"]["diagnostics"] == replaced["result"]["diagnostics"]
|
||||
|
||||
|
||||
async def test_rpc_draft_workspace_lifecycle_methods(tmp_path) -> None:
|
||||
server = build_local_static_workflow_server(tmp_path / "store")
|
||||
app = create_rpc_app(server)
|
||||
|
||||
Reference in New Issue
Block a user