Skip to content

Commit 2a4ad4d

Browse files
committed
Various e2e improvements
1 parent 479f441 commit 2a4ad4d

File tree

11 files changed

+238
-65
lines changed

11 files changed

+238
-65
lines changed

.dockerignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Git
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# Byte-compiled / optimized / DLL files
7+
**/__pycache__/
8+
**/*.py[cod]
9+
10+
# Virtual environment
11+
**/.env
12+
**/.venv/
13+
**/venv/
14+

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,7 @@ debug/
168168
.idea
169169

170170
# Restate data
171-
restate-data
171+
restate-data
172+
173+
# Test reports
174+
test-services/test_report/

test-services/Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
# syntax=docker.io/docker/dockerfile:1.7-labs
2+
13
FROM ghcr.io/pyo3/maturin AS build-sdk
24

35
WORKDIR /usr/src/app
46

5-
COPY . .
6-
RUN maturin build --out dist --interpreter python3.12
7+
COPY --exclude=test-services/ . .
78

9+
RUN maturin build --out dist --interpreter python3.12
810

911
FROM python:3.12-slim AS test-services
1012

@@ -13,7 +15,7 @@ WORKDIR /usr/src/app
1315
COPY --from=build-sdk /usr/src/app/dist/* /usr/src/app/deps/
1416

1517
RUN pip install deps/* && pip install hypercorn
16-
COPY test-services/* .
18+
COPY test-services/ .
1719

1820
EXPOSE 9080
1921

test-services/exclusions.yaml

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,41 @@
11
exclusions:
2-
"default":
3-
- "dev.restate.sdktesting.tests.KillInvocation"
4-
- "dev.restate.sdktesting.tests.CallOrdering"
5-
- "dev.restate.sdktesting.tests.UserErrors"
6-
- "dev.restate.sdktesting.tests.SleepWithFailures"
7-
- "dev.restate.sdktesting.tests.Ingress"
8-
- "dev.restate.sdktesting.tests.WorkflowAPI"
9-
- "dev.restate.sdktesting.tests.State"
10-
- "dev.restate.sdktesting.tests.ServiceToServiceCommunication"
11-
- "dev.restate.sdktesting.tests.CancelInvocation"
12-
- "dev.restate.sdktesting.tests.Sleep"
13-
- "dev.restate.sdktesting.tests.AwaitTimeout"
142
"alwaysSuspending":
15-
- "dev.restate.sdktesting.tests.WorkflowAPI"
16-
- "dev.restate.sdktesting.tests.SideEffect"
17-
- "dev.restate.sdktesting.tests.State"
18-
- "dev.restate.sdktesting.tests.ServiceToServiceCommunication"
19-
- "dev.restate.sdktesting.tests.UserErrors"
20-
- "dev.restate.sdktesting.tests.Sleep"
21-
- "dev.restate.sdktesting.tests.AwaitTimeout"
22-
- "dev.restate.sdktesting.tests.SleepWithFailures"
23-
- "dev.restate.sdktesting.tests.NonDeterminismErrors"
24-
"singleThreadSinglePartition":
25-
- "dev.restate.sdktesting.tests.KillInvocation"
26-
- "dev.restate.sdktesting.tests.CallOrdering"
27-
- "dev.restate.sdktesting.tests.UserErrors"
28-
- "dev.restate.sdktesting.tests.SleepWithFailures"
29-
- "dev.restate.sdktesting.tests.Ingress"
30-
- "dev.restate.sdktesting.tests.WorkflowAPI"
31-
- "dev.restate.sdktesting.tests.State"
32-
- "dev.restate.sdktesting.tests.ServiceToServiceCommunication"
33-
- "dev.restate.sdktesting.tests.CancelInvocation"
34-
- "dev.restate.sdktesting.tests.Sleep"
35-
- "dev.restate.sdktesting.tests.AwaitTimeout"
3+
- "dev.restate.sdktesting.tests.AwaitTimeout"
4+
- "dev.restate.sdktesting.tests.NonDeterminismErrors"
5+
- "dev.restate.sdktesting.tests.ServiceToServiceCommunication"
6+
- "dev.restate.sdktesting.tests.SideEffect"
7+
- "dev.restate.sdktesting.tests.Sleep"
8+
- "dev.restate.sdktesting.tests.SleepWithFailures"
9+
- "dev.restate.sdktesting.tests.State"
10+
- "dev.restate.sdktesting.tests.UserErrors"
11+
"default":
12+
- "dev.restate.sdktesting.tests.AwaitTimeout"
13+
- "dev.restate.sdktesting.tests.CallOrdering"
14+
- "dev.restate.sdktesting.tests.CancelInvocation"
15+
- "dev.restate.sdktesting.tests.Ingress"
16+
- "dev.restate.sdktesting.tests.KillInvocation"
17+
- "dev.restate.sdktesting.tests.ServiceToServiceCommunication"
18+
- "dev.restate.sdktesting.tests.Sleep"
19+
- "dev.restate.sdktesting.tests.SleepWithFailures"
20+
- "dev.restate.sdktesting.tests.State"
21+
- "dev.restate.sdktesting.tests.UserErrors"
3622
"lazyState":
37-
- "dev.restate.sdktesting.tests.State"
23+
- "dev.restate.sdktesting.tests.State"
3824
"persistedTimers":
39-
- "dev.restate.sdktesting.tests.Sleep"
25+
- "dev.restate.sdktesting.tests.Sleep"
26+
"singleThreadSinglePartition":
27+
- "dev.restate.sdktesting.tests.AwaitTimeout"
28+
- "dev.restate.sdktesting.tests.CallOrdering"
29+
- "dev.restate.sdktesting.tests.CancelInvocation"
30+
- "dev.restate.sdktesting.tests.Ingress"
31+
- "dev.restate.sdktesting.tests.KafkaIngress"
32+
- "dev.restate.sdktesting.tests.KillInvocation"
33+
- "dev.restate.sdktesting.tests.KillRuntime"
34+
- "dev.restate.sdktesting.tests.ProxyRequestSigning"
35+
- "dev.restate.sdktesting.tests.ServiceToServiceCommunication"
36+
- "dev.restate.sdktesting.tests.Sleep"
37+
- "dev.restate.sdktesting.tests.SleepWithFailures"
38+
- "dev.restate.sdktesting.tests.State"
39+
- "dev.restate.sdktesting.tests.StopRuntime"
40+
- "dev.restate.sdktesting.tests.UserErrors"
41+
- "dev.restate.sdktesting.tests.WorkflowAPI"

test-services/services/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3+
#
4+
# This file is part of the Restate SDK for Python,
5+
# which is released under the MIT license.
6+
#
7+
# You can find a copy of the license in file LICENSE in the root
8+
# directory of this repository or package, or at
9+
# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10+
11+
from typing import Dict, Union
12+
from restate import Service, VirtualObject, Workflow
13+
14+
from .counter import counter
15+
from .proxy import proxy
16+
from .awakable_holder import awakeable_holder
17+
from. block_and_wait_workflow import workflow
18+
from .cancel_test import runner, blocking_service
19+
20+
def list_services(bindings):
21+
"""List all services in this module"""
22+
return {obj.name : obj for _, obj in bindings.items() if isinstance(obj, (Service, VirtualObject, Workflow))}
23+
24+
def services_named(service_names):
25+
return [ _all_services[name] for name in service_names ]
26+
27+
def all_services():
28+
return _all_services.values()
29+
30+
_all_services = list_services(locals())
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3+
#
4+
# This file is part of the Restate SDK for Python,
5+
# which is released under the MIT license.
6+
#
7+
# You can find a copy of the license in file LICENSE in the root
8+
# directory of this repository or package, or at
9+
# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10+
#
11+
"""example.py"""
12+
# pylint: disable=C0116
13+
# pylint: disable=W0613
14+
# pylint: disable=W0622
15+
16+
from restate import VirtualObject, ObjectContext
17+
from restate.exceptions import TerminalError
18+
19+
awakeable_holder = VirtualObject("AwakeableHolder")
20+
21+
@awakeable_holder.handler()
22+
async def hold(ctx: ObjectContext, id: str):
23+
ctx.set("id", id)
24+
25+
@awakeable_holder.handler(name="hasAwakeable")
26+
async def has_awakeable(ctx: ObjectContext) -> bool:
27+
res = await ctx.get("id")
28+
return res is not None
29+
30+
@awakeable_holder.handler()
31+
async def unlock(ctx: ObjectContext, payload: str):
32+
id = await ctx.get("id")
33+
if id is None:
34+
raise TerminalError(message="No awakeable is registered")
35+
ctx.resolve_awakeable(id, payload)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#
2+
# Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3+
#
4+
# This file is part of the Restate SDK for Python,
5+
# which is released under the MIT license.
6+
#
7+
# You can find a copy of the license in file LICENSE in the root
8+
# directory of this repository or package, or at
9+
# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10+
#
11+
"""example.py"""
12+
# pylint: disable=C0116
13+
# pylint: disable=W0613
14+
# pylint: disable=W0622
15+
16+
from restate import Workflow, WorkflowContext, WorkflowSharedContext
17+
from restate.exceptions import TerminalError
18+
19+
workflow = Workflow("BlockAndWaitWorkflow")
20+
21+
@workflow.main()
22+
async def run(ctx: WorkflowContext, input: str):
23+
ctx.set("my-state", input)
24+
output = await ctx.promise("durable-promise").value()
25+
26+
peek = await ctx.promise("durable-promise").peek()
27+
if peek is None:
28+
raise TerminalError(message="Durable promise should be completed")
29+
30+
return output
31+
32+
33+
@workflow.handler()
34+
async def unblock(ctx: WorkflowSharedContext, output: str):
35+
await ctx.promise("durable-promise").resolve(output)
36+
37+
@workflow.handler(name="getState")
38+
async def get_state(ctx: WorkflowSharedContext, output: str) -> str | None:
39+
return await ctx.get("my-state")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#
2+
# Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3+
#
4+
# This file is part of the Restate SDK for Python,
5+
# which is released under the MIT license.
6+
#
7+
# You can find a copy of the license in file LICENSE in the root
8+
# directory of this repository or package, or at
9+
# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10+
#
11+
"""example.py"""
12+
# pylint: disable=C0116
13+
# pylint: disable=W0613
14+
15+
from datetime import timedelta
16+
from typing import Literal
17+
18+
from restate import VirtualObject, ObjectContext
19+
from restate.exceptions import TerminalError
20+
21+
from . import awakable_holder
22+
23+
BlockingOperation = Literal["CALL", "SLEEP", "AWAKEABLE"]
24+
25+
runner = VirtualObject("CancelTestRunner")
26+
27+
@runner.handler(name="startTest")
28+
async def start_test(ctx: ObjectContext, op: BlockingOperation):
29+
try:
30+
await ctx.object_call(block, key="", arg=op)
31+
except TerminalError as t:
32+
if t.status_code == 409:
33+
ctx.set("state", True)
34+
else:
35+
raise t
36+
37+
@runner.handler(name="verifyTest")
38+
async def verify_test(ctx: ObjectContext):
39+
state = await ctx.get("state")
40+
if state is None:
41+
return False
42+
return state
43+
44+
45+
blocking_service = VirtualObject("BlockingService")
46+
47+
48+
@blocking_service.handler()
49+
async def block(ctx: ObjectContext, op: BlockingOperation):
50+
name, awakeable = ctx.awakeable()
51+
await ctx.object_call(awakable_holder.hold, key="cancel", arg=name)
52+
await awakeable
53+
54+
if op == "CALL":
55+
await ctx.object_call(block, key="", arg=op)
56+
elif op == "SLEEP":
57+
await ctx.sleep(timedelta(days=1024))
58+
elif op == "AWAKEABLE":
59+
name, uncompleteable = ctx.awakeable()
60+
await uncompleteable
61+
62+
@blocking_service.handler(name="isUnblocked")
63+
async def is_unblocked(ctx: ObjectContext):
64+
return None
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def reset(ctx: ObjectContext):
2828

2929
@counter.handler()
3030
async def get(ctx: ObjectContext) -> int:
31-
c: int = await ctx.get(COUNTER_KEY)
31+
c: int | None = await ctx.get(COUNTER_KEY)
3232
if c is None:
3333
return 0
3434
return c
@@ -41,7 +41,7 @@ class CounterUpdateResponse(TypedDict):
4141

4242
@counter.handler()
4343
async def add(ctx: ObjectContext, addend: int) -> CounterUpdateResponse:
44-
old_value: int = await ctx.get(COUNTER_KEY)
44+
old_value: int | None = await ctx.get(COUNTER_KEY)
4545
if old_value is None:
4646
old_value = 0
4747
new_value = old_value + addend
@@ -51,7 +51,7 @@ async def add(ctx: ObjectContext, addend: int) -> CounterUpdateResponse:
5151

5252
@counter.handler()
5353
async def addThenFail(ctx: ObjectContext, addend: int):
54-
old_value: int = await ctx.get(COUNTER_KEY)
54+
old_value: int | None = await ctx.get(COUNTER_KEY)
5555
if old_value is None:
5656
old_value = 0
5757
new_value = old_value + addend

0 commit comments

Comments
 (0)