Skip to content

Commit 798da11

Browse files
committed
Add smithy-testing package for functional testing
1 parent c0176d2 commit 798da11

File tree

15 files changed

+408
-0
lines changed

15 files changed

+408
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "feature",
3+
"description": "Added support for minimal components required for SDK functional testing"
4+
}

packages/smithy-testing/NOTICE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

packages/smithy-testing/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# smithy-testing
2+
3+
This package provides shared utilities for testing functionality in tooling generated by Smithy.
4+
5+
---
6+
7+
## MockHTTPClient
8+
9+
The `MockHTTPClient` allows you to test smithy-python clients without making actual network calls. It implements the `HTTPClient` interface and provides configurable responses for functional testing.
10+
11+
### Basic Usage
12+
13+
```python
14+
from smithy_testing import MockHTTPClient
15+
16+
# Create mock client and configure responses
17+
mock_client = MockHTTPClient()
18+
mock_client.add_response(
19+
status=200,
20+
headers=[("Content-Type", "application/json")],
21+
body=b'{"message": "success"}'
22+
)
23+
24+
# Use with your smithy-python client
25+
config = Config(transport=mock_client)
26+
client = TestSmithyServiceClient(config=config)
27+
28+
# Test your client logic
29+
result = await client.some_operation({"input": "data"})
30+
31+
# Inspect what requests were made
32+
assert mock_client.call_count == 1
33+
captured_request = mock_client.captured_requests[0]
34+
assert result.message == "success"
35+
```
36+
37+
## Utilities
38+
39+
- `create_test_request()`: Helper for creating test HTTPRequest objects
40+
- `MockHTTPClientError`: Exception raised when no responses are queued
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
[project]
2+
name = "smithy-testing"
3+
dynamic = ["version"]
4+
requires-python = ">=3.12"
5+
authors = [
6+
{name = "Amazon Web Services"},
7+
]
8+
description = "Core components for testing Smithy tooling in Python."
9+
readme = "README.md"
10+
license = {text = "Apache License 2.0"}
11+
keywords = ["smithy", "sdk"]
12+
classifiers = [
13+
"Development Status :: 2 - Pre-Alpha",
14+
"Intended Audience :: Developers",
15+
"Intended Audience :: System Administrators",
16+
"Natural Language :: English",
17+
"License :: OSI Approved :: Apache Software License",
18+
"Operating System :: OS Independent",
19+
"Programming Language :: Python",
20+
"Programming Language :: Python :: 3 :: Only",
21+
"Programming Language :: Python :: 3",
22+
"Programming Language :: Python :: 3.12",
23+
"Programming Language :: Python :: 3.13",
24+
"Programming Language :: Python :: 3.14",
25+
"Programming Language :: Python :: Implementation :: CPython",
26+
"Topic :: Software Development :: Libraries"
27+
]
28+
dependencies = []
29+
30+
[project.urls]
31+
"Changelog" = "https://github.com/smithy-lang/smithy-python/blob/develop/packages/smithy-testing/CHANGELOG.md"
32+
"Code" = "https://github.com/smithy-lang/smithy-python/blob/develop/packages/smithy-testing/"
33+
"Issue tracker" = "https://github.com/smithy-lang/smithy-python/issues"
34+
35+
[dependency-groups]
36+
typing = [
37+
"typing_extensions>=4.13.0",
38+
]
39+
40+
[build-system]
41+
requires = ["hatchling"]
42+
build-backend = "hatchling.build"
43+
44+
[tool.hatch.version]
45+
path = "src/smithy_testing/__init__.py"
46+
47+
[tool.hatch.build]
48+
exclude = [
49+
"tests",
50+
]
51+
52+
[tool.ruff]
53+
src = ["src"]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Shared testing utilities for smithy-python functional tests."""
5+
6+
from .http import MockHTTPClient, MockHTTPClientError
7+
from .utils import create_test_request
8+
9+
__version__ = "0.0.0"
10+
11+
__all__ = (
12+
"MockHTTPClient",
13+
"MockHTTPClientError",
14+
"create_test_request",
15+
)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from collections import deque
5+
from copy import copy
6+
7+
from smithy_core.aio.utils import async_list
8+
from smithy_http import tuples_to_fields
9+
from smithy_http.aio import HTTPResponse
10+
from smithy_http.aio.interfaces import HTTPClient, HTTPRequest
11+
from smithy_http.interfaces import HTTPClientConfiguration, HTTPRequestConfiguration
12+
13+
14+
class MockHTTPClient(HTTPClient):
15+
"""Implementation of :py:class:`.interfaces.HTTPClient` solely for testing purposes.
16+
17+
Simulates HTTP request/response behavior.
18+
Responses are queued in FIFO order and requests are captured for inspection.
19+
"""
20+
21+
def __init__(
22+
self,
23+
*,
24+
client_config: HTTPClientConfiguration | None = None,
25+
) -> None:
26+
"""
27+
:param client_config: Configuration that applies to all requests made with this
28+
client.
29+
"""
30+
self._client_config = client_config
31+
self._response_queue: deque[HTTPResponse] = deque()
32+
self._captured_requests: list[HTTPRequest] = []
33+
34+
def add_response(
35+
self,
36+
status: int = 200,
37+
headers: list[tuple[str, str]] | None = None,
38+
body: bytes = b"",
39+
) -> None:
40+
"""Queue a response for the next request.
41+
42+
:param status: HTTP status code (200, 404, 500, etc.)
43+
:param headers: HTTP response headers as list of (name, value) tuples
44+
:param body: Response body as bytes
45+
"""
46+
response = HTTPResponse(
47+
status=status,
48+
fields=tuples_to_fields(headers or []),
49+
body=async_list([body]),
50+
reason=None,
51+
)
52+
self._response_queue.append(response)
53+
54+
async def send(
55+
self,
56+
request: HTTPRequest,
57+
*,
58+
request_config: HTTPRequestConfiguration | None = None,
59+
) -> HTTPResponse:
60+
"""Send HTTP request and return configured response.
61+
62+
:param request: The request including destination URI, fields, payload.
63+
:param request_config: Configuration specific to this request.
64+
:returns: Pre-configured HTTP response from the queue.
65+
:raises MockHTTPClientError: If no responses are queued.
66+
"""
67+
self._captured_requests.append(copy(request))
68+
69+
# Return next queued response or raise error
70+
if self._response_queue:
71+
return self._response_queue.popleft()
72+
else:
73+
raise MockHTTPClientError(
74+
"No responses queued in MockHTTPClient. Use add_response() to queue responses."
75+
)
76+
77+
@property
78+
def call_count(self) -> int:
79+
"""The number of requests made to this client."""
80+
return len(self._captured_requests)
81+
82+
@property
83+
def captured_requests(self) -> list[HTTPRequest]:
84+
"""The list of all requests captured by this client."""
85+
return self._captured_requests.copy()
86+
87+
88+
class MockHTTPClientError(Exception):
89+
"""Exception raised by MockHTTPClient for test setup issues."""

packages/smithy-testing/src/smithy_testing/py.typed

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from smithy_core import URI
5+
from smithy_http import tuples_to_fields
6+
from smithy_http.aio import HTTPRequest
7+
8+
9+
def create_test_request(
10+
method: str = "GET",
11+
host: str = "test.aws.dev",
12+
path: str | None = None,
13+
headers: list[tuple[str, str]] | None = None,
14+
body: bytes = b"",
15+
) -> HTTPRequest:
16+
"""Create test HTTPRequest with defaults.
17+
18+
:param method: HTTP method (GET, POST, etc.)
19+
:param host: Host name (e.g., "test.aws.dev")
20+
:param path: Optional path (e.g., "/users")
21+
:param headers: Optional headers as list of (name, value) tuples
22+
:param body: Request body as bytes
23+
:return: Configured HTTPRequest for testing
24+
"""
25+
return HTTPRequest(
26+
destination=URI(host=host, path=path),
27+
method=method,
28+
fields=tuples_to_fields(headers or []),
29+
body=body,
30+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0

packages/smithy-testing/tests/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)