Skip to content

Commit 3919927

Browse files
committed
test(core): fix import path and clone test orchestrator pattern
Use the public API for imports: Backend and Context both appear in mellea.core.__all__, so import from mellea.core rather than the internal submodules. Rewrite test_stateful_subclass_clone_isolation to simulate the correct orchestrator pattern: the original requirement is never called directly; each attempt clones from the fresh original, giving _calls == 0 at the start of every attempt. The previous test cloned mid-stream, which tested shallow-copy isolation but demonstrated the wrong usage pattern. Assisted-by: Claude Code Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
1 parent 69bf36b commit 3919927

1 file changed

Lines changed: 21 additions & 14 deletions

File tree

test/core/test_stream_validate.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
import pytest
77

8-
from mellea.core import PartialValidationResult, Requirement
9-
from mellea.core.backend import Backend
10-
from mellea.core.base import Context
8+
from mellea.core import Backend, Context, PartialValidationResult, Requirement
119

1210

1311
@pytest.mark.asyncio
@@ -132,7 +130,12 @@ async def stream_validate(
132130

133131
@pytest.mark.asyncio
134132
async def test_stateful_subclass_clone_isolation():
135-
"""copy() of a stateful requirement gives an independent clone — orchestrator pattern."""
133+
"""Orchestrator clone pattern: copy() before each attempt gives a fresh independent clone.
134+
135+
The orchestrator holds the original requirement and never calls stream_validate on it
136+
directly. Before each attempt it clones the original; each clone starts from the
137+
original's (zero) state and advances independently.
138+
"""
136139

137140
class CallCounter(Requirement):
138141
def __init__(self) -> None:
@@ -145,15 +148,19 @@ async def stream_validate(
145148
self._calls += 1
146149
return PartialValidationResult("unknown")
147150

148-
req = CallCounter()
149-
await req.stream_validate("a", backend=None, ctx=None) # type: ignore[arg-type]
150-
await req.stream_validate("b", backend=None, ctx=None) # type: ignore[arg-type]
151-
assert req._calls == 2
151+
req = CallCounter() # original — never used directly by the orchestrator
152+
153+
# Attempt 1
154+
attempt1 = copy(req)
155+
assert attempt1._calls == 0
156+
await attempt1.stream_validate("a", backend=None, ctx=None) # type: ignore[arg-type]
157+
await attempt1.stream_validate("b", backend=None, ctx=None) # type: ignore[arg-type]
158+
assert attempt1._calls == 2
152159

153-
# Simulate orchestrator cloning before a new attempt
154-
cloned = copy(req)
155-
assert cloned._calls == 2 # clone inherits state at clone time
160+
# Attempt 2 (retry) — fresh clone from the same original
161+
attempt2 = copy(req)
162+
assert attempt2._calls == 0 # starts clean, not carrying attempt1's state
163+
await attempt2.stream_validate("c", backend=None, ctx=None) # type: ignore[arg-type]
164+
assert attempt2._calls == 1
156165

157-
await cloned.stream_validate("c", backend=None, ctx=None) # type: ignore[arg-type]
158-
assert cloned._calls == 3 # clone advances independently
159-
assert req._calls == 2 # original is unchanged
166+
assert req._calls == 0 # original never mutated

0 commit comments

Comments
 (0)