Skip to content

feat(test): activity test environment#151

Open
shijiesheng wants to merge 1 commit into
cadence-workflow:mainfrom
shijiesheng:test-activity
Open

feat(test): activity test environment#151
shijiesheng wants to merge 1 commit into
cadence-workflow:mainfrom
shijiesheng:test-activity

Conversation

@shijiesheng

Copy link
Copy Markdown
Member

What changed?

Why?

How did you test it?

Potential risks

Release notes

Documentation Changes

Signed-off-by: Shijie Sheng <liouvetren@gmail.com>
Comment on lines +175 to +177
self._thread_pool = ThreadPoolExecutor(
max_workers=1, thread_name_prefix="cadence-test-activity"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: Timeout on non-cooperative sync activity leaks the worker thread

The thread pool is created with max_workers=1, and the set_test_timeout path cancels the asyncio task wrapping context.execute. For a sync activity, execution runs via loop.run_in_executor on that single thread; cancelling the awaiting coroutine does not stop the underlying blocking thread. After a timeout on a sync activity, the thread stays occupied and close() uses shutdown(wait=False), so the thread leaks and any subsequent execute_activity on the same env blocks forever waiting for a free worker. Only async timeouts are covered by tests. Consider documenting this limitation, or noting in set_test_timeout that sync activities that ignore is_cancelled() cannot be interrupted.

Was this helpful? React with 👍 / 👎

Comment on lines +200 to +206
def set_heartbeat_details(self, *details: Any) -> None:
"""Seed the details returned from ``activity.heartbeat_details()``.

Mirrors Go's ``SetHeartbeatDetails`` and simulates a retry where the
server hands back the details recorded by a previous attempt.
"""
self._seeded_heartbeat_details = self._data_converter.to_data(list(details))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Confusingly similar set_heartbeat_details vs get_heartbeat_details

set_heartbeat_details seeds the values that the activity reads back via activity.heartbeat_details() (simulated prior-attempt input), while get_heartbeat_details returns the details the activity emitted via activity.heartbeat() during this run (output). The near-identical names obscure that they operate on opposite data flows. Consider renaming get_heartbeat_details to something like get_last_heartbeat_details / get_recorded_heartbeat_details, or clarifying the distinction more prominently in the docstrings.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown
CI failed: The CI build failed due to a code formatting violation in a newly added file. The linting step identified that 'cadence/testing/_activity_environment.py' does not match the project's style requirements.

Overview

The build failed in the linting/formatting stage because a file added in this PR does not conform to the project's formatting standards. 1 log was analyzed and confirmed that the CI exited with code 1 due to this formatting mismatch.

Failures

Formatting Violation (confidence: high)

  • Type: build
  • Affected jobs: 86907149464
  • Related to change: yes
  • Root cause: The file 'cadence/testing/_activity_environment.py' contains code that deviates from the project's style guide, causing the automated formatter/linter check to fail.
  • Suggested fix: Run the project's formatting command locally (as indicated in the CI logs) to automatically apply the required style, then commit and push the changes: uv run python scripts/format.py (or equivalent project formatter).

Summary

  • Change-related failures: 1 (Formatting check failed for a new file)
  • Infrastructure/flaky failures: 0
  • Recommended action: The developer should execute the project's formatting script locally and commit the corrected file to resolve the CI failure.
Code Review 👍 Approved with suggestions 0 resolved / 2 findings

Introduces TestActivityEnvironment for headless activity testing with support for heartbeats and cancellation. Resolve the potential thread leak on non-cooperative sync activity timeouts and clarify the naming distinction between heartbeat detail setters and getters.

💡 Edge Case: Timeout on non-cooperative sync activity leaks the worker thread

📄 cadence/testing/_activity_environment.py:175-177 📄 cadence/testing/_activity_environment.py:288-302

The thread pool is created with max_workers=1, and the set_test_timeout path cancels the asyncio task wrapping context.execute. For a sync activity, execution runs via loop.run_in_executor on that single thread; cancelling the awaiting coroutine does not stop the underlying blocking thread. After a timeout on a sync activity, the thread stays occupied and close() uses shutdown(wait=False), so the thread leaks and any subsequent execute_activity on the same env blocks forever waiting for a free worker. Only async timeouts are covered by tests. Consider documenting this limitation, or noting in set_test_timeout that sync activities that ignore is_cancelled() cannot be interrupted.

💡 Quality: Confusingly similar set_heartbeat_details vs get_heartbeat_details

📄 cadence/testing/_activity_environment.py:200-206 📄 cadence/testing/_activity_environment.py:232-245

set_heartbeat_details seeds the values that the activity reads back via activity.heartbeat_details() (simulated prior-attempt input), while get_heartbeat_details returns the details the activity emitted via activity.heartbeat() during this run (output). The near-identical names obscure that they operate on opposite data flows. Consider renaming get_heartbeat_details to something like get_last_heartbeat_details / get_recorded_heartbeat_details, or clarifying the distinction more prominently in the docstrings.

🤖 Prompt for agents
Code Review: Introduces `TestActivityEnvironment` for headless activity testing with support for heartbeats and cancellation. Resolve the potential thread leak on non-cooperative sync activity timeouts and clarify the naming distinction between heartbeat detail setters and getters.

1. 💡 Edge Case: Timeout on non-cooperative sync activity leaks the worker thread
   Files: cadence/testing/_activity_environment.py:175-177, cadence/testing/_activity_environment.py:288-302

   The thread pool is created with max_workers=1, and the set_test_timeout path cancels the asyncio task wrapping context.execute. For a sync activity, execution runs via loop.run_in_executor on that single thread; cancelling the awaiting coroutine does not stop the underlying blocking thread. After a timeout on a sync activity, the thread stays occupied and close() uses shutdown(wait=False), so the thread leaks and any subsequent execute_activity on the same env blocks forever waiting for a free worker. Only async timeouts are covered by tests. Consider documenting this limitation, or noting in set_test_timeout that sync activities that ignore is_cancelled() cannot be interrupted.

2. 💡 Quality: Confusingly similar set_heartbeat_details vs get_heartbeat_details
   Files: cadence/testing/_activity_environment.py:200-206, cadence/testing/_activity_environment.py:232-245

   set_heartbeat_details seeds the values that the activity reads back via activity.heartbeat_details() (simulated prior-attempt input), while get_heartbeat_details returns the details the activity emitted via activity.heartbeat() during this run (output). The near-identical names obscure that they operate on opposite data flows. Consider renaming get_heartbeat_details to something like get_last_heartbeat_details / get_recorded_heartbeat_details, or clarifying the distinction more prominently in the docstrings.

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.68293% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cadence/testing/_activity_environment.py 92.62% 7 Missing and 2 partials ⚠️
Files with missing lines Coverage Δ
cadence/testing/__init__.py 100.00% <100.00%> (ø)
cadence/testing/_activity_environment.py 92.62% <92.62%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

_TASK_TOKEN = b"test-task-token"


class _FakeWorkerStub:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: instead of fakeworkerstub, SimulatedWorkerStub might be a better name here since we are simulating a worker grpc stub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants