mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 00:06:06 +00:00
feat: bind WAC approval urls to a named wait_for_approval step (#10317)
* feat: bind WAC approval urls to a named wait_for_approval step Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject duplicate WAC approval step keys instead of renaming them Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject WAC approval links minted for a step that is not awaiting approval Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: bind WAC approval links to the awaiting step and stop step key aliasing Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject empty approval keys and scope minted-key writes to the workspace Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: enforce WAC approval binding at consumption and reject colliding keys Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: make WAC approval binding and collision checks atomic, harden TS step keys Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: decrement WAC suspend atomically instead of from a pre-lock snapshot Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: add sqlx cache entry for the atomic WAC suspend decrement Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: omit empty approver param from python get_approval_urls Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: pin the suspend-snapshot decrement and the colliding-mint race Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: drop the suspend-snapshot interleave test, it cannot both be stable and discriminate Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: reject step keys that cannot be minted as a URL path segment Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
15a6382c89
commit
9cef724ff2
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
import pytest
|
||||
|
||||
from wmill.client import WorkflowCtx, _StepSuspend, TaskError, workflow, task, step, sleep, parallel, _run_workflow
|
||||
from wmill.client import WorkflowCtx, _StepSuspend, TaskError, workflow, task, step, sleep, parallel, wait_for_approval, _run_workflow
|
||||
|
||||
|
||||
@task
|
||||
@@ -1112,3 +1112,58 @@ class TestParallel:
|
||||
r = _run_workflow(wf, {}, {})
|
||||
assert r["type"] == "complete"
|
||||
assert r["result"] == []
|
||||
|
||||
|
||||
class TestApprovalKeys:
|
||||
"""`key` names the step that get_approval_urls() mints URLs against, so a
|
||||
duplicate must fail rather than silently become `<key>_2` and leave the
|
||||
caller holding a URL for the earlier step."""
|
||||
|
||||
def test_explicit_key_is_used_verbatim(self):
|
||||
@workflow
|
||||
async def wf():
|
||||
return await wait_for_approval(key="manager")
|
||||
|
||||
assert _run_workflow(wf, {}, {})["key"] == "manager"
|
||||
|
||||
def test_duplicate_explicit_key_raises(self):
|
||||
@workflow
|
||||
async def wf():
|
||||
await wait_for_approval(key="manager")
|
||||
await wait_for_approval(key="manager")
|
||||
|
||||
with pytest.raises(RuntimeError, match="already used"):
|
||||
_run_workflow(wf, {"completed_steps": {"manager": {"approved": True}}}, {})
|
||||
|
||||
def test_explicit_key_colliding_with_a_suffixed_step_key_raises(self):
|
||||
"""`step("dup")` twice yields `dup`/`dup_2`, so an approval explicitly named
|
||||
`dup_2` would alias the second step's key."""
|
||||
|
||||
@workflow
|
||||
async def wf():
|
||||
await step("dup", lambda: 1)
|
||||
await step("dup", lambda: 2)
|
||||
await wait_for_approval(key="dup_2")
|
||||
|
||||
with pytest.raises(RuntimeError, match="already used"):
|
||||
_run_workflow(wf, {"completed_steps": {"dup": 1, "dup_2": 2}}, {})
|
||||
|
||||
@pytest.mark.parametrize("bad", ["", " ", ".", "..", "a/b"])
|
||||
def test_unusable_key_raises(self, bad):
|
||||
"""The key travels as one path segment when its URLs are minted, so anything
|
||||
`get_approval_urls` could not address must be refused here too."""
|
||||
|
||||
@workflow
|
||||
async def wf():
|
||||
await wait_for_approval(key=bad)
|
||||
|
||||
with pytest.raises(RuntimeError, match="non-empty step name"):
|
||||
_run_workflow(wf, {}, {})
|
||||
|
||||
def test_unnamed_approvals_still_auto_number(self):
|
||||
@workflow
|
||||
async def wf():
|
||||
await wait_for_approval()
|
||||
await wait_for_approval()
|
||||
|
||||
assert _run_workflow(wf, {"completed_steps": {"approval": {}}}, {})["key"] == "approval_2"
|
||||
|
||||
Reference in New Issue
Block a user