Skip to content

Commit 1a9fa00

Browse files
committed
add proxy tests for Cognito-IDP
1 parent 7640e44 commit 1a9fa00

3 files changed

Lines changed: 186 additions & 3 deletions

File tree

aws-proxy/AGENTS.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
1-
# AI Agent Instructions for AWS Proxy Extension
1+
# AI Agent Instructions for LocalStack AWS Proxy Extension
22

33
This repo is a LocalStack extension (plugin) that enables a "proxy mode" - proxying requests for certain AWS services (e.g., S3) to the upstream real AWS cloud, while handling the remaining services locally.
44

5+
You are an AI agent tasked with adding additional functionality or test coverage to this repo.
6+
7+
## General Instructions
8+
9+
* You can assume that the LocalStack container is running locally, with the proxy extension installed and enabled.
10+
* You can assume that test AWS credentials are configured in the shell environment where the AI agent is running.
11+
* Do *not* touch any files outside the working directory (basedir of this file)!
12+
* You can create new files (no need to prompt for confirmation)
13+
* You can make modifications to files (no need to prompt for confirmation)
14+
* You can delete existing files if needed, but only after user confirmation
15+
* You can call different `make` targets (e.g., `make test`) in this repo (no need to prompt for confirmation)
16+
* For each new file created or existing file modified, add a header comment to the file, something like `# Note/disclosure: This file has been (partially or fully) generated by an AI agent.`
17+
* The proxy tests are executed against real AWS and may incur some costs, so rather than executing the entire test suite or entire modules, focus the testing on individual test functions within a module only.
18+
* To format/lint the codebase you can run `make format` and `make lint`
19+
520
## Testing
621

7-
The proxy functionality is covered by integration tests in the `tests/` folder, one file for each different service.
22+
The proxy functionality is covered by integration tests in the `tests/` folder, one file for each different AWS service (e.g., SQS, S3, etc).
823

924
To add a test, follow the pattern in the existing tests.
1025
It usually involves creating two boto3 clients, one for the LocalStack connection, and one for the real upstream AWS cloud.
1126
We then run API requests with both clients and assert that the results are identical, thereby ensuring that the proxy functionality is working properly.
1227

13-
You can assume that test AWS credentials are configured in the shell environment where the AI agent is running.
28+
To run a single test via `pytest` (say, `test_my_logic` in `test_s3.py`), use the following command:
29+
```
30+
TEST_PATH=tests/test_s3.py::test_my_logic make test
31+
```

aws-proxy/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ test=[
2828
"pytest-httpserver",
2929
"ruff",
3030
# build tools
31+
"build",
3132
"setuptools",
3233
"setuptools_scm",
3334
"wheel"
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Note/disclosure: This file has been (partially or fully) generated by an AI agent.
2+
3+
import boto3
4+
import pytest
5+
from botocore.exceptions import ClientError
6+
from localstack.aws.connect import connect_to
7+
from localstack.utils.sync import retry
8+
9+
from aws_proxy.shared.models import ProxyConfig
10+
11+
12+
def _compare_user_pool_lists(user_pools_aws: list, user_pools_proxied: list):
13+
# Helper to compare user pool lists, ignoring some dynamic attributes
14+
# We'll need to refine this based on actual Cognito-IDP response structure
15+
def normalize_user_pool(user_pool):
16+
normalized = user_pool.copy()
17+
normalized.pop("LastModifiedDate", None)
18+
normalized.pop("CreationDate", None)
19+
return normalized
20+
21+
normalized_aws = sorted(
22+
[normalize_user_pool(up) for up in user_pools_aws], key=lambda x: x["Id"]
23+
)
24+
normalized_proxied = sorted(
25+
[normalize_user_pool(up) for up in user_pools_proxied], key=lambda x: x["Id"]
26+
)
27+
28+
assert normalized_proxied == normalized_aws
29+
30+
31+
def test_cognito_idp_user_pool_operations(start_aws_proxy):
32+
# Start proxy for cognito-idp service
33+
config = ProxyConfig(services={"cognito-idp": {"resources": ".*"}})
34+
start_aws_proxy(config)
35+
36+
# Create clients
37+
cognito_client = connect_to().cognito_idp
38+
cognito_client_aws = boto3.client("cognito-idp")
39+
40+
# List user pools initially to ensure clean state and compare
41+
user_pools_proxied_initial = cognito_client.list_user_pools(MaxResults=10)[
42+
"UserPools"
43+
]
44+
user_pools_aws_initial = cognito_client_aws.list_user_pools(MaxResults=10)[
45+
"UserPools"
46+
]
47+
_compare_user_pool_lists(user_pools_aws_initial, user_pools_proxied_initial)
48+
49+
# Create a user pool
50+
user_pool_name = "test-user-pool-proxy"
51+
create_response_proxied = cognito_client.create_user_pool(PoolName=user_pool_name)
52+
user_pool_id_proxied = create_response_proxied["UserPool"]["Id"]
53+
54+
# List user pools and compare
55+
user_pools_proxied_after_create = cognito_client.list_user_pools(MaxResults=10)[
56+
"UserPools"
57+
]
58+
user_pools_aws_after_create = cognito_client_aws.list_user_pools(MaxResults=10)[
59+
"UserPools"
60+
]
61+
_compare_user_pool_lists(
62+
user_pools_aws_after_create, user_pools_proxied_after_create
63+
)
64+
65+
# Describe user pool and compare
66+
describe_proxied = cognito_client.describe_user_pool(
67+
UserPoolId=user_pool_id_proxied
68+
)["UserPool"]
69+
describe_aws = cognito_client_aws.describe_user_pool(
70+
UserPoolId=user_pool_id_proxied
71+
)["UserPool"]
72+
# Normalize for comparison (remove dynamic fields)
73+
describe_proxied.pop("LastModifiedDate", None)
74+
describe_proxied.pop("CreationDate", None)
75+
describe_aws.pop("LastModifiedDate", None)
76+
describe_aws.pop("CreationDate", None)
77+
assert describe_proxied == describe_aws
78+
79+
# Delete the user pool
80+
cognito_client.delete_user_pool(UserPoolId=user_pool_id_proxied)
81+
82+
def _assert_user_pool_deleted():
83+
with pytest.raises(ClientError) as aws_exc:
84+
cognito_client_aws.describe_user_pool(UserPoolId=user_pool_id_proxied)
85+
with pytest.raises(ClientError) as proxied_exc:
86+
cognito_client.describe_user_pool(UserPoolId=user_pool_id_proxied)
87+
assert aws_exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
88+
assert (
89+
proxied_exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
90+
)
91+
92+
retry(_assert_user_pool_deleted, retries=5, sleep=5)
93+
94+
# Verify that the user pool is no longer in the list
95+
user_pools_proxied_final = cognito_client.list_user_pools(MaxResults=10)[
96+
"UserPools"
97+
]
98+
user_pools_aws_final = cognito_client_aws.list_user_pools(MaxResults=10)[
99+
"UserPools"
100+
]
101+
_compare_user_pool_lists(user_pools_aws_final, user_pools_proxied_final)
102+
103+
104+
def test_cognito_idp_user_operations(start_aws_proxy):
105+
# Start proxy for cognito-idp service
106+
config = ProxyConfig(services={"cognito-idp": {"resources": ".*"}})
107+
start_aws_proxy(config)
108+
109+
# Create clients
110+
cognito_client = connect_to().cognito_idp
111+
cognito_client_aws = boto3.client("cognito-idp")
112+
113+
# Create a user pool
114+
user_pool_name = "test-user-pool-users-proxy"
115+
user_pool = cognito_client.create_user_pool(PoolName=user_pool_name)
116+
user_pool_id = user_pool["UserPool"]["Id"]
117+
118+
try:
119+
# Create a user pool client
120+
user_pool_client = cognito_client.create_user_pool_client(
121+
UserPoolId=user_pool_id,
122+
ClientName="test-client",
123+
ExplicitAuthFlows=["ADMIN_NO_SRP_AUTH", "USER_PASSWORD_AUTH"],
124+
)
125+
client_id = user_pool_client["UserPoolClient"]["ClientId"]
126+
127+
# Sign up a user
128+
username = "testuser"
129+
password = "Password123!"
130+
cognito_client.sign_up(
131+
ClientId=client_id,
132+
Username=username,
133+
Password=password,
134+
UserAttributes=[{"Name": "email", "Value": "test@example.com"}],
135+
)
136+
137+
# Confirm the user
138+
cognito_client.admin_confirm_sign_up(UserPoolId=user_pool_id, Username=username)
139+
140+
# Initiate auth
141+
auth_response_proxied = cognito_client.initiate_auth(
142+
ClientId=client_id,
143+
AuthFlow="USER_PASSWORD_AUTH",
144+
AuthParameters={"USERNAME": username, "PASSWORD": password},
145+
)
146+
auth_response_aws = cognito_client_aws.initiate_auth(
147+
ClientId=client_id,
148+
AuthFlow="USER_PASSWORD_AUTH",
149+
AuthParameters={"USERNAME": username, "PASSWORD": password},
150+
)
151+
assert auth_response_proxied["AuthenticationResult"]["AccessToken"]
152+
assert auth_response_aws["AuthenticationResult"]["AccessToken"]
153+
154+
# Admin delete user
155+
cognito_client.admin_delete_user(UserPoolId=user_pool_id, Username=username)
156+
157+
# Verify user is deleted
158+
with pytest.raises(ClientError) as exc_info:
159+
cognito_client.admin_get_user(UserPoolId=user_pool_id, Username=username)
160+
assert exc_info.value.response["Error"]["Code"] == "UserNotFoundException"
161+
162+
finally:
163+
# Clean up resources
164+
cognito_client.delete_user_pool(UserPoolId=user_pool_id)

0 commit comments

Comments
 (0)