harness-sdk-python 0.8.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/PKG-INFO +1 -1
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/pyproject.toml +1 -1
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/src/harness_sdk/linear_thread.py +0 -4
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/src/harness_sdk/run_manager.py +4 -30
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/run_helpers.py +0 -4
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_linear_thread.py +2 -11
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_settle.py +0 -2
- harness_sdk_python-0.8.0/tests/test_run_leaf.py +0 -191
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/.gitignore +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/README.md +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/src/harness_sdk/__init__.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/src/harness_sdk/fenced_postgres.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_ack_visibility.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_batches.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_branch_anchor.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_dispatching.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_edit_dispatched.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_edit_reload.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_enqueue.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_facade.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_fenced_postgres.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_input_required.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_meta.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_outcomes.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_placement.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_prepare_hooks.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_rewind_during_run.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_steer.py +0 -0
- {harness_sdk_python-0.8.0 → harness_sdk_python-0.9.0}/tests/test_stop_continue.py +0 -0
|
@@ -21,10 +21,6 @@ class LinearThread:
|
|
|
21
21
|
}
|
|
22
22
|
return None
|
|
23
23
|
|
|
24
|
-
async def get_leaf_message_id(self) -> str | None:
|
|
25
|
-
items = self._messages()
|
|
26
|
-
return items[-1].get("id") if items else None
|
|
27
|
-
|
|
28
24
|
async def get_message_child_id(self, parent_id: str) -> str | None:
|
|
29
25
|
"""The id of the first non-tool message after parent_id; None when parent_id is unknown or only tools follow."""
|
|
30
26
|
items = self._messages()
|
|
@@ -15,9 +15,9 @@ messages, rejected otherwise; ``run/stop`` awaits a future the drain resolves
|
|
|
15
15
|
once the in-flight run has ended.
|
|
16
16
|
|
|
17
17
|
``entry["dispatching"]`` is the dispatch record (or None): the payload the
|
|
18
|
-
run callback receives. ``ctx.ack()`` clears it
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
run callback receives. ``ctx.ack()`` clears it; ``steering.take()`` may
|
|
19
|
+
refill it — at most one unacked batch ever exists. A run that ends with an
|
|
20
|
+
unacked record reverts its items
|
|
21
21
|
by provenance: lane items to the front of their lane, direct dispatches to
|
|
22
22
|
the front of the queue, rewind replacements dropped.
|
|
23
23
|
"""
|
|
@@ -155,10 +155,6 @@ class RunManager:
|
|
|
155
155
|
"""Meta for a known id ({parentId, role, isLeaf, onActiveBranch}), None for an unknown one; a None id probes the root ({isLeaf})."""
|
|
156
156
|
...
|
|
157
157
|
|
|
158
|
-
async def get_leaf_message_id(self) -> str | None:
|
|
159
|
-
"""The active branch's current leaf id, None while the thread is empty."""
|
|
160
|
-
...
|
|
161
|
-
|
|
162
158
|
def __init__(
|
|
163
159
|
self,
|
|
164
160
|
*,
|
|
@@ -200,7 +196,6 @@ class RunManager:
|
|
|
200
196
|
self._staged_rewinds: list[_Rewind] = []
|
|
201
197
|
self._dispatching: list[tuple[str, dict[str, Any]]] = []
|
|
202
198
|
self._run_acked = False
|
|
203
|
-
self._leaf_confirmed: str | None = None
|
|
204
199
|
self._run_futures: list["asyncio.Future[Any]"] = []
|
|
205
200
|
self._input_requests: list[dict[str, Any]] = []
|
|
206
201
|
self._input_answers: dict[str, Any] = {}
|
|
@@ -222,7 +217,6 @@ class RunManager:
|
|
|
222
217
|
"queue",
|
|
223
218
|
"steerQueue",
|
|
224
219
|
"runId",
|
|
225
|
-
"runLeafMessageId",
|
|
226
220
|
"dispatch",
|
|
227
221
|
"inputRequests",
|
|
228
222
|
):
|
|
@@ -247,7 +241,6 @@ class RunManager:
|
|
|
247
241
|
"steerQueue": [],
|
|
248
242
|
"dispatching": None,
|
|
249
243
|
"error": None,
|
|
250
|
-
"runLeafMessageId": None,
|
|
251
244
|
}
|
|
252
245
|
)
|
|
253
246
|
return self._state["runs"][0]
|
|
@@ -454,14 +447,10 @@ class RunManager:
|
|
|
454
447
|
self._stop_reason = None
|
|
455
448
|
self._run_acked = False
|
|
456
449
|
entry = self._ensure_entry()
|
|
457
|
-
self._leaf_confirmed = plain(entry["runLeafMessageId"])
|
|
458
450
|
entry["dispatching"] = record
|
|
459
451
|
entry["error"] = None
|
|
460
452
|
if messages:
|
|
461
453
|
self._dispatched_ids = tuple(m["id"] for m in messages)
|
|
462
|
-
if rollback_to is not _ABSENT:
|
|
463
|
-
# The true leaf is the not-yet-created message; ack records it.
|
|
464
|
-
entry["runLeafMessageId"] = None
|
|
465
454
|
entry["runId"] = uuid.uuid4().hex
|
|
466
455
|
self._set_status("running")
|
|
467
456
|
ctx = RunManager.RunContext(
|
|
@@ -496,7 +485,6 @@ class RunManager:
|
|
|
496
485
|
f"{type(outcome).__name__}"
|
|
497
486
|
)
|
|
498
487
|
except asyncio.CancelledError:
|
|
499
|
-
await self._pull_leaf()
|
|
500
488
|
self._settle(ctx)
|
|
501
489
|
self._set_status("stopped")
|
|
502
490
|
self._entry()["runId"] = None
|
|
@@ -507,7 +495,6 @@ class RunManager:
|
|
|
507
495
|
self._idle.set()
|
|
508
496
|
raise # no drain, no freeze
|
|
509
497
|
except Exception as exc:
|
|
510
|
-
await self._pull_leaf()
|
|
511
498
|
self._settle(ctx)
|
|
512
499
|
message = str(exc) or type(exc).__name__
|
|
513
500
|
if isinstance(exc, StatewireReject):
|
|
@@ -521,7 +508,6 @@ class RunManager:
|
|
|
521
508
|
self._revert_dispatching()
|
|
522
509
|
self._drain()
|
|
523
510
|
return
|
|
524
|
-
await self._pull_leaf()
|
|
525
511
|
self._settle(ctx)
|
|
526
512
|
if self._run_acked:
|
|
527
513
|
error = None
|
|
@@ -536,12 +522,6 @@ class RunManager:
|
|
|
536
522
|
self._outcome = outcome
|
|
537
523
|
self._drain()
|
|
538
524
|
|
|
539
|
-
async def _pull_leaf(self) -> None:
|
|
540
|
-
# An end with an unacked batch reverts instead of recording a leaf.
|
|
541
|
-
if plain(self._entry()["dispatching"]) is not None:
|
|
542
|
-
return
|
|
543
|
-
self._entry()["runLeafMessageId"] = await self._thread.get_leaf_message_id()
|
|
544
|
-
|
|
545
525
|
def _settle(self, ctx: "RunManager.RunContext") -> None:
|
|
546
526
|
if self._ctx is ctx:
|
|
547
527
|
self._ctx = None
|
|
@@ -566,17 +546,11 @@ class RunManager:
|
|
|
566
546
|
self._dispatching = []
|
|
567
547
|
entry["dispatching"] = None
|
|
568
548
|
self._run_acked = True
|
|
569
|
-
if record["messages"]:
|
|
570
|
-
entry["runLeafMessageId"] = record["messages"][-1]["id"]
|
|
571
|
-
self._leaf_confirmed = plain(entry["runLeafMessageId"])
|
|
572
549
|
|
|
573
550
|
def _revert_dispatching(self) -> None:
|
|
574
551
|
taken, self._dispatching = self._dispatching, []
|
|
575
552
|
entry = self._ensure_entry()
|
|
576
|
-
record = plain(entry["dispatching"])
|
|
577
553
|
entry["dispatching"] = None
|
|
578
|
-
if record is not None and "rollbackTo" in record:
|
|
579
|
-
entry["runLeafMessageId"] = self._leaf_confirmed
|
|
580
554
|
if not taken:
|
|
581
555
|
return
|
|
582
556
|
for lane in ("steerQueue", "queue"):
|
|
@@ -1319,7 +1293,7 @@ class RunManager:
|
|
|
1319
1293
|
raise RuntimeError("this run has already settled")
|
|
1320
1294
|
|
|
1321
1295
|
def ack(self) -> None:
|
|
1322
|
-
"""Ack the current batch: clears the dispatch record
|
|
1296
|
+
"""Ack the current batch: clears the dispatch record."""
|
|
1323
1297
|
self._ensure_active()
|
|
1324
1298
|
self._manager._ack()
|
|
1325
1299
|
|
|
@@ -32,7 +32,6 @@ class Script:
|
|
|
32
32
|
def __init__(self) -> None:
|
|
33
33
|
self.calls: asyncio.Queue[Call] = asyncio.Queue()
|
|
34
34
|
self.thread: dict[str, dict[str, Any]] = {}
|
|
35
|
-
self.leaf: str | None = None
|
|
36
35
|
|
|
37
36
|
async def run(self, ctx: RunManager.RunContext) -> Any:
|
|
38
37
|
call = Call(ctx, asyncio.get_running_loop().create_future())
|
|
@@ -44,9 +43,6 @@ class Script:
|
|
|
44
43
|
return {"isLeaf": not self.thread}
|
|
45
44
|
return self.thread.get(message_id)
|
|
46
45
|
|
|
47
|
-
async def get_leaf_message_id(self) -> str | None:
|
|
48
|
-
return self.leaf
|
|
49
|
-
|
|
50
46
|
async def next_call(self, timeout: float = 5) -> Call:
|
|
51
47
|
return await asyncio.wait_for(self.calls.get(), timeout)
|
|
52
48
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Contract: ``linear_thread`` projects a linear message list for RunManager —
|
|
2
2
|
``get_message_meta(None)`` probes the root (isLeaf iff empty), a known id gets
|
|
3
3
|
``{parentId, role, isLeaf, onActiveBranch}`` chained by list order, an unknown
|
|
4
|
-
id gets ``None``;
|
|
5
|
-
|
|
4
|
+
id gets ``None``; the extra ``get_message_child_id`` resolves the first
|
|
5
|
+
non-tool successor."""
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
@@ -69,14 +69,6 @@ async def test_unknown_id_returns_none():
|
|
|
69
69
|
assert await thread.get_message_meta("nope") is None
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
async def test_leaf_is_the_last_message_id():
|
|
73
|
-
assert await _thread_for(THREAD).get_leaf_message_id() == "u2"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
async def test_leaf_of_an_empty_thread_is_none():
|
|
77
|
-
assert await _thread_for([]).get_leaf_message_id() is None
|
|
78
|
-
|
|
79
|
-
|
|
80
72
|
async def test_child_id_skips_tool_messages():
|
|
81
73
|
thread = _thread_for(
|
|
82
74
|
[
|
|
@@ -109,7 +101,6 @@ async def test_reads_the_live_list():
|
|
|
109
101
|
assert await thread.get_message_meta(None) == {"isLeaf": True}
|
|
110
102
|
messages.append({"id": "u1", "type": "human"})
|
|
111
103
|
assert await thread.get_message_meta(None) == {"isLeaf": False}
|
|
112
|
-
assert await thread.get_leaf_message_id() == "u1"
|
|
113
104
|
|
|
114
105
|
|
|
115
106
|
def test_non_callable_messages_raises():
|
|
@@ -101,7 +101,6 @@ async def test_unacked_run_end_reverts_entries_to_the_lane_front():
|
|
|
101
101
|
await drv.wait_status("error")
|
|
102
102
|
assert queue_ids(drv.replica, "queue") == ["m1", "m2"]
|
|
103
103
|
assert drv.run["error"] == {"message": "boom"}
|
|
104
|
-
assert drv.run["runLeafMessageId"] is None
|
|
105
104
|
await drv.command("run/steer", {"messageId": "m1"}, terminal=False)
|
|
106
105
|
continued = await script.next_call()
|
|
107
106
|
assert continued.ctx.trigger == "error-continue"
|
|
@@ -211,7 +210,6 @@ async def test_task_cancellation_reverts_entries_and_settles_sends():
|
|
|
211
210
|
assert (await drv.res(seq))["type"] == "accepted"
|
|
212
211
|
await drv.wait_status("stopped")
|
|
213
212
|
assert queue_ids(drv.replica, "queue") == ["m1", "m2"]
|
|
214
|
-
assert drv.run["runLeafMessageId"] is None
|
|
215
213
|
assert task.cancelled()
|
|
216
214
|
|
|
217
215
|
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
from run_helpers import Script, add, msg, run_host, run_of
|
|
2
|
-
|
|
3
|
-
from harness_sdk import RunManager
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def thread_with_turn(script: Script) -> None:
|
|
7
|
-
script.thread.update(
|
|
8
|
-
{
|
|
9
|
-
"u1": {
|
|
10
|
-
"parentId": None,
|
|
11
|
-
"role": "user",
|
|
12
|
-
"isLeaf": False,
|
|
13
|
-
"onActiveBranch": True,
|
|
14
|
-
},
|
|
15
|
-
"a1": {
|
|
16
|
-
"parentId": "u1",
|
|
17
|
-
"role": "assistant",
|
|
18
|
-
"isLeaf": True,
|
|
19
|
-
"onActiveBranch": True,
|
|
20
|
-
},
|
|
21
|
-
}
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
async def test_ready_state_has_no_run_entry():
|
|
26
|
-
script = Script()
|
|
27
|
-
thread_with_turn(script)
|
|
28
|
-
async with run_host(script) as (drv, host):
|
|
29
|
-
assert drv.replica["status"] == "ready"
|
|
30
|
-
assert drv.replica["runs"] == []
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
async def test_ack_sets_the_leaf_to_the_last_batch_message():
|
|
34
|
-
script = Script()
|
|
35
|
-
async with run_host(script) as (drv, host):
|
|
36
|
-
drv.post("run/enqueue", add("m1"))
|
|
37
|
-
call = await script.next_call()
|
|
38
|
-
await drv.wait_status("running")
|
|
39
|
-
assert drv.run["runLeafMessageId"] is None
|
|
40
|
-
call.ack()
|
|
41
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "m1")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
async def test_steer_batch_ack_sets_the_leaf_to_the_last_lane_item():
|
|
45
|
-
script = Script()
|
|
46
|
-
async with run_host(script) as (drv, host):
|
|
47
|
-
drv.post("run/enqueue", add("m1"))
|
|
48
|
-
call = await script.next_call()
|
|
49
|
-
call.ack()
|
|
50
|
-
await drv.command("run/steer", add("s1", anchor="m1"), terminal=False)
|
|
51
|
-
await drv.command("run/steer", add("s2", anchor="s1"), terminal=False)
|
|
52
|
-
call.finish(RunManager.Complete())
|
|
53
|
-
steered = await script.next_call()
|
|
54
|
-
assert steered.ctx.messages[-1]["id"] == "s2"
|
|
55
|
-
assert drv.run["runLeafMessageId"] == "m1"
|
|
56
|
-
steered.ack()
|
|
57
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "s2")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
async def test_steering_take_moves_the_leaf_at_its_ack():
|
|
61
|
-
script = Script()
|
|
62
|
-
async with run_host(script) as (drv, host):
|
|
63
|
-
drv.post("run/enqueue", add("m1"))
|
|
64
|
-
call = await script.next_call()
|
|
65
|
-
call.ack()
|
|
66
|
-
await drv.command("run/steer", add("s1", anchor="m1"), terminal=False)
|
|
67
|
-
await drv.wait(lambda s: len(run_of(s).get("steerQueue", [])) == 1)
|
|
68
|
-
call.ctx.steering.take()
|
|
69
|
-
assert drv.run["runLeafMessageId"] == "m1"
|
|
70
|
-
call.ack()
|
|
71
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "s1")
|
|
72
|
-
call.finish(RunManager.Complete())
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
async def test_settle_pulls_the_thread_leaf():
|
|
76
|
-
for outcome in [
|
|
77
|
-
RunManager.Error(dispatch_queue=False),
|
|
78
|
-
RunManager.Stop(dispatch_queue=False),
|
|
79
|
-
]:
|
|
80
|
-
script = Script()
|
|
81
|
-
async with run_host(script) as (drv, host):
|
|
82
|
-
drv.post("run/enqueue", add("m1"))
|
|
83
|
-
call = await script.next_call()
|
|
84
|
-
call.ack()
|
|
85
|
-
script.leaf = "a1"
|
|
86
|
-
call.finish(outcome)
|
|
87
|
-
await drv.wait_status("error", "stopped")
|
|
88
|
-
assert drv.run["runLeafMessageId"] == "a1"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
async def test_input_required_settle_pulls_the_thread_leaf():
|
|
92
|
-
script = Script()
|
|
93
|
-
async with run_host(script) as (drv, host):
|
|
94
|
-
drv.post("run/enqueue", add("m1"))
|
|
95
|
-
call = await script.next_call()
|
|
96
|
-
call.ack()
|
|
97
|
-
script.leaf = "a1"
|
|
98
|
-
call.finish(
|
|
99
|
-
RunManager.InputRequired(
|
|
100
|
-
requests=({"id": "r1", "type": "tool-call", "toolCallId": "t1"},)
|
|
101
|
-
)
|
|
102
|
-
)
|
|
103
|
-
await drv.wait_status("input-required")
|
|
104
|
-
assert drv.run["runLeafMessageId"] == "a1"
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
async def test_unacked_end_reverts_instead_of_pulling():
|
|
108
|
-
script = Script()
|
|
109
|
-
async with run_host(script) as (drv, host):
|
|
110
|
-
drv.post("run/enqueue", add("m1"))
|
|
111
|
-
call = await script.next_call()
|
|
112
|
-
script.leaf = "a1"
|
|
113
|
-
call.fail(RuntimeError("boom"))
|
|
114
|
-
await drv.wait_status("error")
|
|
115
|
-
assert drv.run["runLeafMessageId"] is None
|
|
116
|
-
assert [item["id"] for item in drv.run["queue"]] == ["m1"]
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
async def test_complete_clears_the_leaf_on_ready():
|
|
120
|
-
script = Script()
|
|
121
|
-
async with run_host(script) as (drv, host):
|
|
122
|
-
drv.post("run/enqueue", add("m1"))
|
|
123
|
-
call = await script.next_call()
|
|
124
|
-
call.ack()
|
|
125
|
-
script.leaf = "a1"
|
|
126
|
-
await drv.wait_status("running")
|
|
127
|
-
call.finish(RunManager.Complete())
|
|
128
|
-
await drv.wait_status("ready")
|
|
129
|
-
assert drv.replica["runs"] == []
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
async def test_complete_with_a_queued_item_keeps_the_leaf_flowing():
|
|
133
|
-
script = Script()
|
|
134
|
-
async with run_host(script) as (drv, host):
|
|
135
|
-
drv.post("run/enqueue", add("m1"))
|
|
136
|
-
call = await script.next_call()
|
|
137
|
-
call.ack()
|
|
138
|
-
await drv.command("run/enqueue", add("m2", anchor="m1"), terminal=False)
|
|
139
|
-
script.leaf = "a1"
|
|
140
|
-
call.finish(RunManager.Complete())
|
|
141
|
-
drain = await script.next_call()
|
|
142
|
-
drain.ack()
|
|
143
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "m2")
|
|
144
|
-
drain.finish(RunManager.Complete())
|
|
145
|
-
await drv.wait_status("ready")
|
|
146
|
-
assert drv.replica["runs"] == []
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
async def test_rewind_dispatch_nulls_the_leaf_and_the_ack_records_the_replacement():
|
|
150
|
-
script = Script()
|
|
151
|
-
thread_with_turn(script)
|
|
152
|
-
async with run_host(script, capabilities=("rewind",)) as (drv, host):
|
|
153
|
-
drv.post("run/edit", {"sourceId": "u1", "message": msg("u2")})
|
|
154
|
-
call = await script.next_call()
|
|
155
|
-
await drv.wait_status("running")
|
|
156
|
-
assert drv.run["runLeafMessageId"] is None
|
|
157
|
-
call.ack()
|
|
158
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "u2")
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
async def test_reload_dispatch_nulls_the_leaf_until_the_settle_pull():
|
|
162
|
-
script = Script()
|
|
163
|
-
thread_with_turn(script)
|
|
164
|
-
async with run_host(script, capabilities=("rewind",)) as (drv, host):
|
|
165
|
-
drv.post("run/reload", {"sourceId": "a1"})
|
|
166
|
-
call = await script.next_call()
|
|
167
|
-
await drv.wait_status("running")
|
|
168
|
-
assert drv.run["runLeafMessageId"] is None
|
|
169
|
-
call.ack()
|
|
170
|
-
assert drv.run["runLeafMessageId"] is None
|
|
171
|
-
script.leaf = "a2"
|
|
172
|
-
call.finish(RunManager.Stop(dispatch_queue=False))
|
|
173
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "a2")
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
async def test_unacked_rewind_end_restores_the_previous_leaf():
|
|
177
|
-
script = Script()
|
|
178
|
-
thread_with_turn(script)
|
|
179
|
-
async with run_host(script, capabilities=("rewind",)) as (drv, host):
|
|
180
|
-
drv.post("run/enqueue", add("m1", anchor="a1"))
|
|
181
|
-
call = await script.next_call()
|
|
182
|
-
call.ack()
|
|
183
|
-
script.leaf = "a2"
|
|
184
|
-
call.finish(RunManager.Stop(dispatch_queue=False))
|
|
185
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") == "a2")
|
|
186
|
-
drv.post("run/edit", {"sourceId": "u1", "message": msg("u2")})
|
|
187
|
-
rerun = await script.next_call()
|
|
188
|
-
await drv.wait(lambda s: run_of(s).get("runLeafMessageId") is None)
|
|
189
|
-
rerun.fail(RuntimeError("boom"))
|
|
190
|
-
await drv.wait_status("error")
|
|
191
|
-
assert drv.run["runLeafMessageId"] == "a2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|