Skip to content

Commit 8937a4c

Browse files
authored
Add conftest fixture to transcribe-streaming integration tests (#55)
* Add conftest fixture to transcribe-streaming integration tests * Ensure fixture cleanup on partial setup failure * Address reviewer feedback regarding IAM role propagation and retry handling * Add tags and prefix to integ-test resources for orphan cleanup
1 parent e643928 commit 8937a4c

3 files changed

Lines changed: 187 additions & 115 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Pytest fixtures for Transcribe Streaming integration tests.
5+
6+
Creates and tears down an IAM role and S3 bucket needed for medical scribe
7+
integration tests once per test session. The ``healthscribe_resources``
8+
fixture provides the role ARN and bucket name.
9+
"""
10+
11+
import json
12+
import uuid
13+
from typing import Any
14+
15+
import boto3
16+
import pytest
17+
18+
REGION = "us-east-1"
19+
20+
# Tags applied to all resources so orphaned resources from interrupted
21+
# test runs can be discovered and cleaned up.
22+
_TAGS = [{"Key": "Purpose", "Value": "IntegTest"}]
23+
24+
25+
def _create_iam_role(iam_client: Any, role_name: str, bucket_name: str) -> None:
26+
"""Create an IAM role with S3 PutObject access for Transcribe Streaming.
27+
28+
Args:
29+
iam_client: A boto3 IAM client.
30+
role_name: The name of the IAM role to create.
31+
bucket_name: The name of the S3 bucket the role is allowed to write to.
32+
"""
33+
trust_policy = {
34+
"Version": "2012-10-17",
35+
"Statement": [
36+
{
37+
"Effect": "Allow",
38+
"Principal": {"Service": ["transcribe.streaming.amazonaws.com"]},
39+
"Action": "sts:AssumeRole",
40+
}
41+
],
42+
}
43+
44+
iam_client.create_role(
45+
RoleName=role_name,
46+
AssumeRolePolicyDocument=json.dumps(trust_policy),
47+
Tags=_TAGS,
48+
)
49+
50+
permissions_policy = {
51+
"Version": "2012-10-17",
52+
"Statement": [
53+
{
54+
"Action": ["s3:PutObject"],
55+
"Resource": [
56+
f"arn:aws:s3:::{bucket_name}",
57+
f"arn:aws:s3:::{bucket_name}/*",
58+
],
59+
"Effect": "Allow",
60+
}
61+
],
62+
}
63+
64+
iam_client.put_role_policy(
65+
RoleName=role_name,
66+
PolicyName="healthscribe-s3-access",
67+
PolicyDocument=json.dumps(permissions_policy),
68+
)
69+
70+
71+
def _create_healthscribe_resources(
72+
iam_client: Any, s3_client: Any, sts_client: Any, role_name: str, bucket_name: str
73+
) -> str:
74+
"""Create an IAM role and S3 bucket for medical scribe tests.
75+
76+
Args:
77+
iam_client: A boto3 IAM client.
78+
s3_client: A boto3 S3 client.
79+
sts_client: A boto3 STS client.
80+
role_name: The name of the IAM role to create.
81+
bucket_name: The name of the S3 bucket to create.
82+
83+
Returns:
84+
The IAM role ARN.
85+
"""
86+
account_id = sts_client.get_caller_identity()["Account"]
87+
88+
s3_client.create_bucket(Bucket=bucket_name)
89+
s3_client.put_bucket_tagging(Bucket=bucket_name, Tagging={"TagSet": _TAGS})
90+
_create_iam_role(iam_client, role_name, bucket_name)
91+
92+
return f"arn:aws:iam::{account_id}:role/{role_name}"
93+
94+
95+
def _delete_healthscribe_resources(
96+
iam_client: Any, s3_client: Any, role_name: str, bucket_name: str
97+
) -> None:
98+
"""Delete the IAM role and S3 bucket created for tests.
99+
100+
Args:
101+
iam_client: A boto3 IAM client.
102+
s3_client: A boto3 S3 client.
103+
role_name: The name of the IAM role to delete.
104+
bucket_name: The name of the S3 bucket to delete.
105+
"""
106+
# Empty and delete the bucket
107+
try:
108+
paginator = s3_client.get_paginator("list_objects_v2")
109+
for page in paginator.paginate(Bucket=bucket_name):
110+
objects = page.get("Contents")
111+
if not objects:
112+
continue
113+
s3_client.delete_objects(
114+
Bucket=bucket_name,
115+
Delete={"Objects": [{"Key": o["Key"]} for o in objects]},
116+
)
117+
s3_client.delete_bucket(Bucket=bucket_name)
118+
except s3_client.exceptions.NoSuchBucket:
119+
pass
120+
121+
# Delete inline policy then role
122+
try:
123+
iam_client.delete_role_policy(
124+
RoleName=role_name, PolicyName="healthscribe-s3-access"
125+
)
126+
except iam_client.exceptions.NoSuchEntityException:
127+
pass
128+
129+
try:
130+
iam_client.delete_role(RoleName=role_name)
131+
except iam_client.exceptions.NoSuchEntityException:
132+
pass
133+
134+
135+
@pytest.fixture(scope="session")
136+
def healthscribe_resources():
137+
"""Create HealthScribe resources for the test session and delete them after."""
138+
# Shortened UUID to keep IAM role name under the 64-character limit.
139+
unique_suffix = uuid.uuid4().hex[:16]
140+
role_name = f"integ-test-transcribe-streaming-role-{unique_suffix}"
141+
bucket_name = f"integ-test-transcribe-streaming-bucket-{unique_suffix}"
142+
143+
iam_client = boto3.client("iam")
144+
s3_client = boto3.client("s3", region_name=REGION)
145+
sts_client = boto3.client("sts")
146+
147+
try:
148+
role_arn = _create_healthscribe_resources(
149+
iam_client, s3_client, sts_client, role_name, bucket_name
150+
)
151+
yield role_arn, bucket_name
152+
finally:
153+
_delete_healthscribe_resources(iam_client, s3_client, role_name, bucket_name)

clients/aws-sdk-transcribe-streaming/tests/integration/test_non_streaming.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4-
"""Test non-streaming output type handling.
5-
6-
This test requires AWS resources (an IAM role and an S3 bucket).
7-
To set them up locally, run:
8-
9-
uv run scripts/setup_resources.py
10-
11-
Then export the environment variables shown in the output.
12-
"""
4+
"""Test non-streaming output type handling using Medical Scribe."""
135

146
import asyncio
15-
import os
167
import time
178
import uuid
189

19-
import pytest
20-
2110
from aws_sdk_transcribe_streaming.models import (
11+
BadRequestException,
2212
ClinicalNoteGenerationSettings,
2313
GetMedicalScribeStreamInput,
2414
GetMedicalScribeStreamOutput,
@@ -42,14 +32,14 @@
4232
CHANNEL_NUMS = 1
4333
CHUNK_SIZE = 1024 * 8
4434

35+
# Maximum time to wait for IAM role propagation across services.
36+
ROLE_PROPAGATION_TIMEOUT = 300
37+
# Delay between retries while waiting for IAM role propagation.
38+
ROLE_PROPAGATION_RETRY_DELAY = 5
4539

46-
async def test_get_medical_scribe_stream() -> None:
47-
role_arn = os.environ.get("HEALTHSCRIBE_ROLE_ARN")
48-
s3_bucket = os.environ.get("HEALTHSCRIBE_S3_BUCKET")
49-
50-
if not role_arn or not s3_bucket:
51-
pytest.fail("HEALTHSCRIBE_ROLE_ARN or HEALTHSCRIBE_S3_BUCKET not set")
5240

41+
async def _run_medical_scribe_session(role_arn: str, s3_bucket: str) -> None:
42+
"""Run a full Medical Scribe streaming session and verify its completion."""
5343
transcribe_client = create_transcribe_client("us-east-1")
5444
session_id = str(uuid.uuid4())
5545

@@ -121,3 +111,29 @@ async def test_get_medical_scribe_stream() -> None:
121111
assert details.language_code == "en-US"
122112
assert details.media_encoding == "pcm"
123113
assert details.media_sample_rate_hertz == SAMPLE_RATE
114+
115+
116+
async def test_get_medical_scribe_stream(
117+
healthscribe_resources: tuple[str, str],
118+
) -> None:
119+
"""Test non-streaming GetMedicalScribeStream operation.
120+
121+
IAM is eventually consistent, so Transcribe may not be able to assume the
122+
newly created role immediately. Retry on BadRequestException until the
123+
role has propagated, or until the timeout is reached.
124+
"""
125+
role_arn, s3_bucket = healthscribe_resources
126+
127+
last_error: BadRequestException | None = None
128+
try:
129+
async with asyncio.timeout(ROLE_PROPAGATION_TIMEOUT):
130+
while True:
131+
try:
132+
await _run_medical_scribe_session(role_arn, s3_bucket)
133+
return
134+
except BadRequestException as e:
135+
last_error = e
136+
await asyncio.sleep(ROLE_PROPAGATION_RETRY_DELAY)
137+
except TimeoutError:
138+
assert last_error is not None
139+
raise last_error

clients/aws-sdk-transcribe-streaming/tests/setup_resources.py

Lines changed: 0 additions & 97 deletions
This file was deleted.

0 commit comments

Comments
 (0)