-
Notifications
You must be signed in to change notification settings - Fork 139
Integration tests on DaprClient responses #981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seherv
wants to merge
15
commits into
dapr:main
Choose a base branch
from
seherv:grpc-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
70ec2fd
Move old integration tests to examples/
seherv 167442d
Test DaprClient directly
seherv 1ab1320
Update docs to new test structure
seherv 1ef0c22
Address Copilot comments (1)
seherv 0708271
Address Copilot comments (2)
seherv 9f65704
Address Copilot comments (3)
seherv 7161d9c
Replace sleep() with polls when possible
seherv e6f12a2
Address Copilot comments (4)
seherv b963b1d
Address Copilot comments (5)
seherv 341fccb
Update README to include both test suites
seherv 56a780d
Document wait_until() in AGENTS.md
seherv e7db397
Update CLAUDE.md
seherv 0f1b991
Fix package name
seherv 1d128d7
Clean up entire process group
seherv c67aea2
PR cleanup (1)
seherv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| import shlex | ||
| import subprocess | ||
| import tempfile | ||
| import threading | ||
| import time | ||
| from pathlib import Path | ||
| from typing import IO, Any, Generator | ||
|
|
||
| import pytest | ||
|
|
||
| from tests._process_utils import get_kwargs_for_process_group, terminate_process_group | ||
|
|
||
| REPO_ROOT = Path(__file__).resolve().parent.parent.parent | ||
| EXAMPLES_DIR = REPO_ROOT / 'examples' | ||
|
|
||
|
|
||
| def pytest_configure(config: pytest.Config) -> None: | ||
| config.addinivalue_line('markers', 'example_dir(name): set the example directory for a test') | ||
|
|
||
|
|
||
| class DaprRunner: | ||
| """Helper to run `dapr run` commands and capture output.""" | ||
|
|
||
| def __init__(self, cwd: Path) -> None: | ||
| self._cwd = cwd | ||
| self._bg_process: subprocess.Popen[str] | None = None | ||
| self._bg_output_file: IO[str] | None = None | ||
|
|
||
| @staticmethod | ||
| def _terminate(proc: subprocess.Popen[str]) -> None: | ||
| if proc.poll() is not None: | ||
| return | ||
|
|
||
| terminate_process_group(proc) | ||
| try: | ||
| proc.wait(timeout=10) | ||
| except subprocess.TimeoutExpired: | ||
| terminate_process_group(proc, force=True) | ||
| proc.wait() | ||
|
|
||
| def run(self, args: str, *, timeout: int = 30, until: list[str] | None = None) -> str: | ||
| """Run a foreground command, block until it finishes, and return output. | ||
|
|
||
| Use this for short-lived processes (e.g. a publisher that exits on its | ||
| own). For long-lived background services, use ``start()``/``stop()``. | ||
|
|
||
| Args: | ||
| args: Arguments passed to ``dapr run``. | ||
| timeout: Maximum seconds to wait before killing the process. | ||
| until: If provided, the process is terminated as soon as every | ||
| string in this list has appeared in the accumulated output. | ||
| """ | ||
| proc = subprocess.Popen( | ||
| args=('dapr', 'run', *shlex.split(args)), | ||
| cwd=self._cwd, | ||
| stdout=subprocess.PIPE, | ||
| stderr=subprocess.STDOUT, | ||
| text=True, | ||
| **get_kwargs_for_process_group(), | ||
| ) | ||
| lines: list[str] = [] | ||
| assert proc.stdout is not None | ||
|
|
||
| # Kill the process if it exceeds the timeout. A background timer is | ||
| # needed because `for line in proc.stdout` blocks indefinitely when | ||
| # the child never exits. | ||
| timer = threading.Timer( | ||
| interval=timeout, function=lambda: terminate_process_group(proc, force=True) | ||
| ) | ||
| timer.start() | ||
|
|
||
| try: | ||
| for line in proc.stdout: | ||
| print(line, end='', flush=True) | ||
| lines.append(line) | ||
| if until and all(exp in ''.join(lines) for exp in until): | ||
| break | ||
| finally: | ||
| timer.cancel() | ||
| self._terminate(proc) | ||
|
|
||
| return ''.join(lines) | ||
|
|
||
| def start(self, args: str, *, wait: int = 5) -> None: | ||
| """Start a long-lived background service. | ||
|
|
||
| Use this for servers/subscribers that must stay alive while a second | ||
| process runs via ``run()``. Call ``stop()`` to terminate and collect | ||
| output. Stdout is written to a temp file to avoid pipe-buffer deadlocks. | ||
| """ | ||
| output_file = tempfile.NamedTemporaryFile(mode='w+', suffix='.log') | ||
| proc = subprocess.Popen( | ||
| args=('dapr', 'run', *shlex.split(args)), | ||
| cwd=self._cwd, | ||
| stdout=output_file, | ||
| stderr=subprocess.STDOUT, | ||
| text=True, | ||
| **get_kwargs_for_process_group(), | ||
| ) | ||
| self._bg_process = proc | ||
| self._bg_output_file = output_file | ||
| time.sleep(wait) | ||
|
|
||
| def stop(self) -> str: | ||
| """Stop the background service and return its captured output.""" | ||
| if self._bg_process is None: | ||
| return '' | ||
| self._terminate(self._bg_process) | ||
| self._bg_process = None | ||
| return self._read_and_close_output() | ||
|
|
||
| def _read_and_close_output(self) -> str: | ||
| if self._bg_output_file is None: | ||
| return '' | ||
| self._bg_output_file.seek(0) | ||
| output = self._bg_output_file.read() | ||
| self._bg_output_file.close() | ||
| self._bg_output_file = None | ||
| print(output, end='', flush=True) | ||
| return output | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def dapr(request: pytest.FixtureRequest) -> Generator[DaprRunner, Any, None]: | ||
| """Provides a DaprRunner scoped to an example directory. | ||
|
|
||
| Use the ``example_dir`` marker to select which example: | ||
|
|
||
| @pytest.mark.example_dir('state_store') | ||
| def test_something(dapr): | ||
| ... | ||
|
|
||
| Defaults to the examples root if no marker is set. | ||
| """ | ||
| marker = request.node.get_closest_marker('example_dir') | ||
| cwd = EXAMPLES_DIR / marker.args[0] if marker else EXAMPLES_DIR | ||
|
|
||
| runner = DaprRunner(cwd) | ||
| yield runner | ||
| runner.stop() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import subprocess | ||
| import time | ||
|
|
||
| import pytest | ||
|
|
||
| REDIS_CONTAINER = 'dapr_redis' | ||
|
|
||
| EXPECTED_LINES = [ | ||
| 'Got key=orderId1 value=100 version=1 metadata={}', | ||
| 'Got key=orderId2 value=200 version=1 metadata={}', | ||
| 'Subscribe key=orderId2 value=210 version=2 metadata={}', | ||
| 'Unsubscribed successfully? True', | ||
| ] | ||
|
|
||
|
|
||
| @pytest.fixture() | ||
| def redis_config(): | ||
| """Seed configuration values in Redis before the test.""" | ||
| subprocess.run( | ||
| ('docker', 'exec', 'dapr_redis', 'redis-cli', 'SET', 'orderId1', '100||1'), | ||
| check=True, | ||
| capture_output=True, | ||
| ) | ||
| subprocess.run( | ||
| ('docker', 'exec', 'dapr_redis', 'redis-cli', 'SET', 'orderId2', '200||1'), | ||
| check=True, | ||
| capture_output=True, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.example_dir('configuration') | ||
| def test_configuration(dapr, redis_config): | ||
| dapr.start( | ||
| '--app-id configexample --resources-path components/ -- python3 configuration.py', | ||
| wait=5, | ||
| ) | ||
| # Update Redis to trigger the subscription notification | ||
| subprocess.run( | ||
| ('docker', 'exec', 'dapr_redis', 'redis-cli', 'SET', 'orderId2', '210||2'), | ||
| check=True, | ||
| capture_output=True, | ||
| ) | ||
| # configuration.py sleeps 10s after subscribing before it unsubscribes. | ||
| # Wait long enough for the full script to finish. | ||
| time.sleep(10) | ||
|
|
||
| output = dapr.stop() | ||
| for line in EXPECTED_LINES: | ||
| assert line in output, f'Missing in output: {line}' | ||
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import pytest | ||
|
|
||
| EXPECTED_LINES = [ | ||
| 'Will try to acquire a lock from lock store named [lockstore]', | ||
| 'The lock is for a resource named [example-lock-resource]', | ||
| 'The client identifier is [example-client-id]', | ||
| 'The lock will expire in 60 seconds.', | ||
| 'Lock acquired successfully!!!', | ||
| 'We already released the lock so unlocking will not work.', | ||
| 'We tried to unlock it anyway and got back [UnlockResponseStatus.lock_does_not_exist]', | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.example_dir('distributed_lock') | ||
| def test_distributed_lock(dapr): | ||
| output = dapr.run( | ||
| '--app-id=locksapp --app-protocol grpc --resources-path components/ -- python3 lock.py', | ||
| timeout=10, | ||
| ) | ||
| for line in EXPECTED_LINES: | ||
| assert line in output, f'Missing in output: {line}' |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.