fix: smooth draft schema projection ux
This commit is contained in:
@@ -950,6 +950,9 @@ def test_browser_click_wrapper_produces_expected_paths_and_command_prefix() -> N
|
||||
)
|
||||
assert BROWSER_CLICK_DEF.default_prompt.name == "challenge-prompt.md"
|
||||
assert BROWSER_CLICK_DEF.default_prompt.parent.name == "browser_click_challenge"
|
||||
assert "ux_issues_found: []" in BROWSER_CLICK_DEF.default_prompt.read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
def test_generic_runner_can_be_configured_with_fake_challenge_and_fake_opencode(
|
||||
|
||||
@@ -40,6 +40,7 @@ def test_report_challenge_prompt_requires_full_product_lifecycle() -> None:
|
||||
assert "render_markdown_report" in prompt
|
||||
assert "deployment" in prompt.lower()
|
||||
assert "run_id" in prompt
|
||||
assert "ux_issues_found: []" in prompt
|
||||
|
||||
|
||||
def test_report_challenge_workspace_template_contains_safe_input_files(
|
||||
|
||||
@@ -1217,6 +1217,41 @@ async def test_set_workflow_output_map_merges_top_level_output(tmp_path: Path) -
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_workflow_output_map_projects_missing_output_schema(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
artifact_store = FileWorkflowArtifactStore(tmp_path / "drafts_out_project")
|
||||
api, _service, _authoring = _draft_api(artifact_store, register_echo=True)
|
||||
draft = {
|
||||
**_echo_draft(),
|
||||
"state_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"echoed": {"type": "string"},
|
||||
"after": {"type": "object"},
|
||||
},
|
||||
},
|
||||
"output_schema": {"type": "object", "properties": {}},
|
||||
"output": [],
|
||||
}
|
||||
await api.create_draft_workspace(workspace_id="report", draft=draft)
|
||||
|
||||
result = await api.set_workflow_output_map(
|
||||
workspace_id="report",
|
||||
revision=1,
|
||||
output_map={"state.after": "after"},
|
||||
merge=True,
|
||||
)
|
||||
|
||||
assert result["status"] == "valid", result["diagnostics"]
|
||||
fetched = await api.get_draft_workspace(workspace_id="report", include_draft=True)
|
||||
assert fetched["draft"]["output"] == [{"path": "state.after", "target": "after"}]
|
||||
assert fetched["draft"]["output_schema"]["properties"]["after"] == {
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
|
||||
# -- Browser-click test helpers for forward-route tests --
|
||||
|
||||
|
||||
@@ -1312,6 +1347,44 @@ async def test_create_draft_from_capability_does_not_bind_optional_inputs(
|
||||
assert any("open_browser" in note for note in created["wrapper_hints"]["notes"])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_step_projects_explicit_optional_workflow_inputs(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
api, _service = _browser_click_api(
|
||||
FileWorkflowArtifactStore(tmp_path / "drafts_explicit_optional_input")
|
||||
)
|
||||
|
||||
await api.create_draft_workspace_from_capability(
|
||||
workspace_id="browser",
|
||||
capability_name="local.browser_click.open_click_page",
|
||||
name="browser",
|
||||
)
|
||||
|
||||
result = await api.add_step_from_capability(
|
||||
workspace_id="browser",
|
||||
revision=1,
|
||||
step_id="wait",
|
||||
capability_name="local.browser_click.wait_for_click",
|
||||
route_from_step="call",
|
||||
routes={"ok": "__end__"},
|
||||
input_map={
|
||||
"state.session_id": "session_id",
|
||||
"input.simulate": "simulate",
|
||||
"input.timeout_seconds": "timeout_seconds",
|
||||
},
|
||||
bind_outputs={"after": "state.after"},
|
||||
)
|
||||
|
||||
assert result["status"] == "valid", result["diagnostics"]
|
||||
workspace = await api.get_draft_workspace(
|
||||
workspace_id="browser", include_draft=True
|
||||
)
|
||||
properties = workspace["draft"]["input_schema"]["properties"]
|
||||
assert properties["simulate"]["type"] == "object"
|
||||
assert properties["timeout_seconds"]["type"] == "integer"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_step_persists_invalid_forward_route(tmp_path: Path) -> None:
|
||||
api, _service = _browser_click_api(
|
||||
@@ -1551,6 +1624,40 @@ async def test_bind_draft_local_output_to_workflow_output_reuses_compatible_stat
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bind_draft_local_output_to_state_reuses_compatible_state(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
artifact_store = FileWorkflowArtifactStore(tmp_path / "drafts_bind_existing_state")
|
||||
api, _service, authoring = _draft_api(artifact_store, register_echo=True)
|
||||
draft = _echo_draft()
|
||||
draft["state_schema"] = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"echoed": {
|
||||
"description": "Echoed text",
|
||||
"title": "Echoed",
|
||||
"type": "string",
|
||||
}
|
||||
},
|
||||
}
|
||||
await api.create_draft_workspace(workspace_id="report", draft=draft)
|
||||
|
||||
result = await authoring.bind_draft(
|
||||
workspace_id="report",
|
||||
revision=1,
|
||||
step_id="echo",
|
||||
source_path="local.echoed",
|
||||
target_path="state.echoed",
|
||||
)
|
||||
|
||||
assert result["status"] == "valid", result["diagnostics"]
|
||||
workspace = await api.get_draft_workspace(workspace_id="report", include_draft=True)
|
||||
assert workspace["draft"]["steps"]["echo"]["output"] == [
|
||||
{"source": "echoed", "target": "state.echoed"}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bind_draft_local_output_to_quoted_workflow_output_field(
|
||||
tmp_path: Path,
|
||||
|
||||
@@ -99,6 +99,26 @@ def test_project_output_property_rejects_existing_state_field() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_project_output_property_allows_equivalent_existing_state_field() -> None:
|
||||
state_schema = {
|
||||
"type": "object",
|
||||
"properties": {"after": {"type": "object"}},
|
||||
}
|
||||
|
||||
projected = project_output_property_to_state_schema(
|
||||
state_schema=state_schema,
|
||||
output_schema={
|
||||
"type": "object",
|
||||
"properties": {"after": {"type": "object"}},
|
||||
},
|
||||
output_field="after",
|
||||
state_field="after",
|
||||
allow_existing_equivalent=True,
|
||||
)
|
||||
|
||||
assert projected == state_schema
|
||||
|
||||
|
||||
def test_project_output_property_rejects_invalid_output_schema() -> None:
|
||||
with pytest.raises(ValueError, match="output_schema is not valid JSON Schema"):
|
||||
project_output_property_to_state_schema(
|
||||
|
||||
@@ -159,6 +159,7 @@ def test_wf_draft_map_help_explains_replace_merge_and_validate() -> None:
|
||||
assert "replaces the full workflow output map" in workflow_output_help
|
||||
assert "Use --merge only" in workflow_output_help
|
||||
assert "GRAPH_SOURCE=OUTPUT_FIELD" in workflow_output_help
|
||||
assert "output_schema fields are projected" in workflow_output_help
|
||||
assert "draft validate" in workflow_output_help
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user