Skip to content

Commit 718266d

Browse files
authored
Create cloud-specific tests (#1389)
1 parent ac842fd commit 718266d

3 files changed

Lines changed: 131 additions & 24 deletions

File tree

.github/workflows/ci.yml

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ jobs:
2626
- os: ubuntu-latest
2727
python: "3.14"
2828
docsTarget: true
29-
cloudTestTarget: true
3029
openaiTestTarget: true
3130
clippyLinter: true
3231
- os: ubuntu-latest
@@ -73,14 +72,6 @@ jobs:
7372
- if: ${{ !endsWith(matrix.os, '-arm') }}
7473
run: poe test ${{matrix.pytestExtraArgs}} -s --workflow-environment time-skipping --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--time-skipping.xml
7574
timeout-minutes: 10
76-
# Check cloud if proper target and not on fork
77-
- if: ${{ matrix.cloudTestTarget && (github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python') }}
78-
run: poe test ${{matrix.pytestExtraArgs}} -s -k test_cloud_client --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--cloud.xml
79-
timeout-minutes: 10
80-
env:
81-
TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }}
82-
TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00
83-
TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6
8475
- if: ${{ matrix.openaiTestTarget && (github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python') }}
8576
run: poe test tests/contrib/openai_agents/test_openai.py ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--openai.xml
8677
timeout-minutes: 10
@@ -169,6 +160,49 @@ jobs:
169160
path: junit-xml
170161
retention-days: 14
171162

163+
# Run tests against Temporal Cloud (skipped on forks)
164+
cloud-test:
165+
if: ${{ github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python' }}
166+
timeout-minutes: 15
167+
runs-on: ubuntu-latest
168+
steps:
169+
- uses: actions/checkout@v4
170+
with:
171+
submodules: recursive
172+
- uses: dtolnay/rust-toolchain@stable
173+
- uses: Swatinem/rust-cache@v2
174+
with:
175+
workspaces: temporalio/bridge -> target
176+
- uses: actions/setup-python@v5
177+
with:
178+
python-version: "3.14"
179+
- uses: arduino/setup-protoc@v3
180+
with:
181+
# TODO(cretz): Can upgrade proto when https://github.com/arduino/setup-protoc/issues/99 fixed
182+
version: "23.x"
183+
repo-token: ${{ secrets.GITHUB_TOKEN }}
184+
- uses: astral-sh/setup-uv@v5
185+
- run: uv tool install poethepoet
186+
- run: uv sync --all-extras
187+
- run: poe build-develop
188+
- run: poe test -s tests/test_cloud.py --junit-xml=junit-xml/cloud.xml
189+
timeout-minutes: 10
190+
env:
191+
TEMPORAL_IS_CLOUD_TESTS: true
192+
TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }}
193+
TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00
194+
TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6
195+
TEMPORAL_CLIENT_CLOUD_TARGET: sdk-ci.a2dd6.tmprl.cloud:7233
196+
TEMPORAL_CLIENT_CERT: ${{ secrets.TEMPORAL_CLIENT_CERT }}
197+
TEMPORAL_CLIENT_KEY: ${{ secrets.TEMPORAL_CLIENT_KEY }}
198+
- name: "Upload junit-xml artifacts"
199+
uses: actions/upload-artifact@v4
200+
if: always()
201+
with:
202+
name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--cloud
203+
path: junit-xml
204+
retention-days: 14
205+
172206
# Runs the sdk features repo tests with this repo's current SDK code
173207
features-tests:
174208
uses: temporalio/features/.github/workflows/python.yaml@main

tests/test_client.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import temporalio.common
1919
import temporalio.exceptions
2020
from temporalio import workflow
21-
from temporalio.api.cloud.cloudservice.v1 import GetNamespaceRequest
2221
from temporalio.api.enums.v1 import (
2322
CancelExternalWorkflowExecutionFailedCause,
2423
ContinueAsNewInitiator,
@@ -42,7 +41,6 @@
4241
BuildIdOpPromoteSetByBuildId,
4342
CancelWorkflowInput,
4443
Client,
45-
CloudOperationsClient,
4644
Interceptor,
4745
OutboundInterceptor,
4846
QueryWorkflowInput,
@@ -1481,19 +1479,6 @@ async def test_build_id_interactions(client: Client, env: WorkflowEnvironment):
14811479
assert reachability.build_id_reachability["1.1"].task_queue_reachability[tq] == []
14821480

14831481

1484-
async def test_cloud_client_simple():
1485-
if "TEMPORAL_CLIENT_CLOUD_API_KEY" not in os.environ:
1486-
pytest.skip("No cloud API key")
1487-
client = await CloudOperationsClient.connect(
1488-
api_key=os.environ["TEMPORAL_CLIENT_CLOUD_API_KEY"],
1489-
version=os.environ["TEMPORAL_CLIENT_CLOUD_API_VERSION"],
1490-
)
1491-
result = await client.cloud_service.get_namespace(
1492-
GetNamespaceRequest(namespace=os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"])
1493-
)
1494-
assert os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"] == result.namespace.namespace
1495-
1496-
14971482
@workflow.defn
14981483
class LastCompletionResultWorkflow:
14991484
@workflow.run

tests/test_cloud.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""Tests that run against Temporal Cloud."""
2+
3+
import multiprocessing
4+
import os
5+
from collections.abc import AsyncGenerator, Iterator
6+
7+
import pytest
8+
import pytest_asyncio
9+
10+
from temporalio.api.cloud.cloudservice.v1 import GetNamespaceRequest
11+
from temporalio.client import Client, CloudOperationsClient
12+
from temporalio.service import TLSConfig
13+
from temporalio.testing import WorkflowEnvironment
14+
from temporalio.worker import SharedStateManager
15+
from tests.helpers.worker import ExternalPythonWorker, ExternalWorker
16+
17+
# Skip entire module unless explicitly enabled
18+
pytestmark = pytest.mark.skipif(
19+
"TEMPORAL_IS_CLOUD_TESTS" not in os.environ,
20+
reason="Cloud tests not enabled",
21+
)
22+
23+
24+
@pytest_asyncio.fixture(scope="module") # type: ignore[reportUntypedFunctionDecorator]
25+
async def env() -> AsyncGenerator[WorkflowEnvironment, None]:
26+
tls_config: bool | TLSConfig = True
27+
client_cert = os.environ.get("TEMPORAL_CLIENT_CERT")
28+
client_key = os.environ.get("TEMPORAL_CLIENT_KEY")
29+
if client_cert and client_key:
30+
tls_config = TLSConfig(
31+
client_cert=client_cert.encode(),
32+
client_private_key=client_key.encode(),
33+
)
34+
client = await Client.connect(
35+
os.environ["TEMPORAL_CLIENT_CLOUD_TARGET"],
36+
namespace=os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"],
37+
api_key=os.environ.get("TEMPORAL_CLIENT_CLOUD_API_KEY"),
38+
tls=tls_config,
39+
)
40+
env = WorkflowEnvironment.from_client(client)
41+
yield env
42+
await env.shutdown()
43+
44+
45+
@pytest_asyncio.fixture # type: ignore[reportUntypedFunctionDecorator]
46+
async def client(env: WorkflowEnvironment) -> Client:
47+
return env.client
48+
49+
50+
@pytest_asyncio.fixture(scope="module") # type: ignore[reportUntypedFunctionDecorator]
51+
async def worker(
52+
env: WorkflowEnvironment,
53+
) -> AsyncGenerator[ExternalWorker, None]:
54+
w = ExternalPythonWorker(env)
55+
yield w
56+
await w.close()
57+
58+
59+
@pytest.fixture(scope="module")
60+
def shared_state_manager() -> Iterator[SharedStateManager]:
61+
mp_mgr = multiprocessing.Manager()
62+
mgr = SharedStateManager.create_from_multiprocessing(mp_mgr)
63+
try:
64+
yield mgr
65+
finally:
66+
mp_mgr.shutdown()
67+
68+
69+
# --- Cloud-specific tests ---
70+
71+
72+
async def test_cloud_client_simple():
73+
client = await CloudOperationsClient.connect(
74+
api_key=os.environ["TEMPORAL_CLIENT_CLOUD_API_KEY"],
75+
version=os.environ["TEMPORAL_CLIENT_CLOUD_API_VERSION"],
76+
)
77+
result = await client.cloud_service.get_namespace(
78+
GetNamespaceRequest(namespace=os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"])
79+
)
80+
assert os.environ["TEMPORAL_CLIENT_CLOUD_NAMESPACE"] == result.namespace.namespace
81+
82+
83+
# --- Delegated tests ---
84+
# Import test functions to re-run them against cloud fixtures.
85+
86+
from tests.worker.test_activity import ( # noqa: E402
87+
test_activity_info, # pyright: ignore[reportUnusedImport] # noqa: F401
88+
)

0 commit comments

Comments
 (0)