Skip to content

Commit 61ff701

Browse files
Add contract tests
1 parent df0aaa6 commit 61ff701

File tree

3 files changed

+179
-5
lines changed

3 files changed

+179
-5
lines changed

contract-tests/images/applications/aws-sdk/server.js

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const fetch = require('node-fetch');
99
const JSZip = require('jszip');
1010

1111
const { S3Client, CreateBucketCommand, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
12-
const { DynamoDBClient, CreateTableCommand, PutItemCommand } = require('@aws-sdk/client-dynamodb');
12+
const { DynamoDBClient, CreateTableCommand, PutItemCommand, DescribeTableCommand } = require('@aws-sdk/client-dynamodb');
1313
const { SQSClient, CreateQueueCommand, SendMessageCommand, ReceiveMessageCommand } = require('@aws-sdk/client-sqs');
14-
const { KinesisClient, CreateStreamCommand, PutRecordCommand } = require('@aws-sdk/client-kinesis');
14+
const { KinesisClient, CreateStreamCommand, PutRecordCommand, DescribeStreamCommand } = require('@aws-sdk/client-kinesis');
1515
const { BedrockClient, GetGuardrailCommand } = require('@aws-sdk/client-bedrock');
1616
const { BedrockAgentClient, GetKnowledgeBaseCommand, GetDataSourceCommand, GetAgentCommand } = require('@aws-sdk/client-bedrock-agent');
1717
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');
@@ -194,6 +194,8 @@ async function handleGetRequest(req, res, path) {
194194
await handleSnsRequest(req, res, path);
195195
} else if (path.includes('lambda')) {
196196
await handleLambdaRequest(req, res, path);
197+
} else if (path.includes('cross-account')) {
198+
await handleCrossAccountRequest(req, res, path);
197199
} else {
198200
res.writeHead(404);
199201
res.end();
@@ -383,6 +385,19 @@ async function handleDdbRequest(req, res, path) {
383385
res.statusCode = 500;
384386
}
385387
res.end();
388+
} else if (path.includes('describe/some-table')) {
389+
try {
390+
const response = await ddbClient.send(
391+
new DescribeTableCommand({
392+
TableName: 'put_test_table'
393+
})
394+
);
395+
res.statusCode = 200;
396+
} catch (err) {
397+
console.log('Error describing the table', err);
398+
res.statusCode = 500;
399+
}
400+
res.end();
386401
} else {
387402
res.statusCode = 404;
388403
res.end();
@@ -532,6 +547,20 @@ async function handleKinesisRequest(req, res, path) {
532547
res.statusCode = 500;
533548
}
534549
res.end();
550+
} else if (path.includes('describe/my-stream')) {
551+
try {
552+
await kinesisClient.send(
553+
new DescribeStreamCommand ({
554+
StreamName: 'test_stream',
555+
StreamARN: 'arn:aws:kinesis:us-west-2:000000000000:stream/test_stream'
556+
})
557+
);
558+
res.statusCode = 200;
559+
} catch (err) {
560+
console.log('Error describing the stream', err);
561+
res.statusCode = 500;
562+
}
563+
res.end();
535564
} else {
536565
res.statusCode = 404;
537566
res.end();
@@ -1041,6 +1070,35 @@ async function handleLambdaRequest(req, res, path) {
10411070
res.end();
10421071
}
10431072

1073+
async function handleCrossAccountRequest(req, res, path) {
1074+
const s3Client = new S3Client({
1075+
endpoint: _AWS_SDK_S3_ENDPOINT,
1076+
forcePathStyle: true,
1077+
region: "eu-central-1",
1078+
credentials: {
1079+
accessKeyId: "account_b_access_key_id",
1080+
secretAccessKey: "account_b_secret_access_key",
1081+
sessionToken: "account_b_token"
1082+
}
1083+
});
1084+
1085+
if (path.includes('createbucket/account_b')) {
1086+
try {
1087+
await s3Client.send(
1088+
new CreateBucketCommand({
1089+
Bucket: "cross-account-bucket",
1090+
CreateBucketConfiguration: { LocationConstraint: "eu-central-1" }
1091+
})
1092+
);
1093+
res.statusCode = 200;
1094+
} catch (err) {
1095+
console.log('Error creating a bucket in another account', err);
1096+
res.statusCode = 500;
1097+
}
1098+
res.end();
1099+
}
1100+
}
1101+
10441102
async function setupLambda() {
10451103
const lambdaClient = new LambdaClient({
10461104
endpoint: _AWS_SDK_ENDPOINT,

contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
AWS_REMOTE_RESOURCE_TYPE,
2020
AWS_REMOTE_SERVICE,
2121
AWS_SPAN_KIND,
22-
AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER
22+
AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER,
23+
AWS_REMOTE_RESOURCE_ACCESS_KEY,
24+
AWS_REMOTE_RESOURCE_ACCOUNT_ID,
25+
AWS_REMOTE_RESOURCE_REGION
2326
)
2427
from opentelemetry.proto.common.v1.common_pb2 import AnyValue, KeyValue
2528
from opentelemetry.proto.metrics.v1.metrics_pb2 import ExponentialHistogramDataPoint, Metric
@@ -31,6 +34,7 @@
3134

3235
_AWS_SQS_QUEUE_URL: str = "aws.sqs.queue.url"
3336
_AWS_SQS_QUEUE_NAME: str = "aws.sqs.queue.name"
37+
_AWS_KINESIS_STREAM_ARN: str = "aws.kinesis.stream.arn"
3438
_AWS_KINESIS_STREAM_NAME: str = "aws.kinesis.stream.name"
3539
_AWS_BEDROCK_AGENT_ID: str = "aws.bedrock.agent.id"
3640
_AWS_BEDROCK_GUARDRAIL_ID: str = "aws.bedrock.guardrail.id"
@@ -49,6 +53,7 @@
4953
_GEN_AI_RESPONSE_FINISH_REASONS: str = "gen_ai.response.finish_reasons"
5054
_GEN_AI_USAGE_INPUT_TOKENS: str = 'gen_ai.usage.input_tokens'
5155
_GEN_AI_USAGE_OUTPUT_TOKENS: str = 'gen_ai.usage.output_tokens'
56+
_AWS_DYNAMODB_TABLE_ARN: str = "aws.dynamodb.table.arn"
5257

5358
# pylint: disable=too-many-public-methods
5459
class AWSSDKTest(ContractTestBase):
@@ -236,6 +241,30 @@ def test_dynamodb_put_item(self):
236241
span_name="DynamoDB.PutItem",
237242
)
238243

244+
def test_dynamodb_describe_table(self):
245+
self.do_test_requests(
246+
"ddb/describe/some-table",
247+
"GET",
248+
200,
249+
0,
250+
0,
251+
local_operation="GET /ddb",
252+
remote_service="AWS::DynamoDB",
253+
remote_operation="DescribeTable",
254+
remote_resource_type="AWS::DynamoDB::Table",
255+
remote_resource_identifier="put_test_table",
256+
cloudformation_primary_identifier="put_test_table",
257+
remote_resource_account_id="000000000000",
258+
remote_resource_region="us-west-2",
259+
request_specific_attributes={
260+
SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["put_test_table"],
261+
},
262+
response_specific_attributes={
263+
_AWS_DYNAMODB_TABLE_ARN: r"arn:aws:dynamodb:us-west-2:000000000000:table/put_test_table",
264+
},
265+
span_name="DynamoDB.DescribeTable",
266+
)
267+
239268
def test_dynamodb_error(self):
240269
self.do_test_requests(
241270
"ddb/error",
@@ -397,6 +426,28 @@ def test_kinesis_put_record(self):
397426
},
398427
span_name="Kinesis.PutRecord",
399428
)
429+
430+
def test_kinesis_describe_stream(self):
431+
self.do_test_requests(
432+
"kinesis/describe/my-stream",
433+
"GET",
434+
200,
435+
0,
436+
0,
437+
local_operation="GET /kinesis",
438+
remote_service="AWS::Kinesis",
439+
remote_operation="DescribeStream",
440+
remote_resource_type="AWS::Kinesis::Stream",
441+
remote_resource_identifier="test_stream",
442+
cloudformation_primary_identifier="test_stream",
443+
remote_resource_account_id="000000000000",
444+
remote_resource_region="us-west-2",
445+
request_specific_attributes={
446+
_AWS_KINESIS_STREAM_NAME: "test_stream",
447+
_AWS_KINESIS_STREAM_ARN: "arn:aws:kinesis:us-west-2:000000000000:stream/test_stream"
448+
},
449+
span_name="Kinesis.DescribeStream",
450+
)
400451

401452
def test_kinesis_error(self):
402453
self.do_test_requests(
@@ -1034,6 +1085,27 @@ def test_lambda_error(self):
10341085
span_name="Lambda.GetEventSourceMapping",
10351086
)
10361087

1088+
def test_cross_account(self):
1089+
self.do_test_requests(
1090+
"cross-account/createbucket/account_b",
1091+
"GET",
1092+
200,
1093+
0,
1094+
0,
1095+
local_operation="GET /cross-account",
1096+
remote_service="AWS::S3",
1097+
remote_operation="CreateBucket",
1098+
remote_resource_type="AWS::S3::Bucket",
1099+
remote_resource_identifier="cross-account-bucket",
1100+
cloudformation_primary_identifier="cross-account-bucket",
1101+
request_specific_attributes={
1102+
SpanAttributes.AWS_S3_BUCKET: "cross-account-bucket",
1103+
},
1104+
remote_resource_access_key="account_b_access_key_id",
1105+
remote_resource_region="eu-central-1",
1106+
span_name="S3.CreateBucket",
1107+
)
1108+
10371109
#TODO: Need to add test_lambda_get_event_source_mapping once workaround is figured out for storing UUID between tests
10381110

10391111
@override
@@ -1062,6 +1134,9 @@ def _assert_aws_span_attributes(self, resource_scope_spans: List[ResourceScopeSp
10621134
kwargs.get("remote_resource_type", "None"),
10631135
kwargs.get("remote_resource_identifier", "None"),
10641136
kwargs.get("cloudformation_primary_identifier", "None"),
1137+
kwargs.get("remote_resource_account_id", "None"),
1138+
kwargs.get("remote_resource_region", "None"),
1139+
kwargs.get("remote_resource_account_access_key", "None"),
10651140
)
10661141

10671142
def _assert_aws_attributes(
@@ -1073,7 +1148,10 @@ def _assert_aws_attributes(
10731148
span_kind: str,
10741149
remote_resource_type: str,
10751150
remote_resource_identifier: str,
1076-
cloudformation_primary_identifier: str
1151+
cloudformation_primary_identifier: str,
1152+
remote_resource_account_id: str,
1153+
remote_resource_region: str,
1154+
remote_resource_account_access_key: str
10771155
) -> None:
10781156
attributes_dict: Dict[str, AnyValue] = self._get_attributes_dict(attributes_list)
10791157
self._assert_str_attribute(attributes_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name())
@@ -1086,7 +1164,16 @@ def _assert_aws_attributes(
10861164
self._assert_attribute(attributes_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier)
10871165
if cloudformation_primary_identifier != "None":
10881166
self._assert_attribute(attributes_dict, AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER, cloudformation_primary_identifier)
1089-
1167+
if remote_resource_account_id != "None":
1168+
assert remote_resource_identifier != "None"
1169+
self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_ACCOUNT_ID, remote_resource_account_id)
1170+
self.assertIsNone(attributes_dict.get(AWS_REMOTE_RESOURCE_ACCESS_KEY))
1171+
if remote_resource_account_access_key != "None":
1172+
assert remote_resource_identifier != "None"
1173+
self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_ACCESS_KEY, remote_resource_account_access_key)
1174+
self.assertIsNone(attributes_dict.get(AWS_REMOTE_RESOURCE_ACCOUNT_ID))
1175+
if remote_resource_region != "None":
1176+
self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_REGION, remote_resource_region)
10901177
self._assert_str_attribute(attributes_dict, AWS_SPAN_KIND, span_kind)
10911178

10921179
@override
@@ -1187,10 +1274,23 @@ def _assert_metric_attributes(
11871274
self._assert_attribute(attribute_dict, AWS_SPAN_KIND, kwargs.get("dependency_metric_span_kind") or "CLIENT")
11881275
remote_resource_type = kwargs.get("remote_resource_type", "None")
11891276
remote_resource_identifier = kwargs.get("remote_resource_identifier", "None")
1277+
remote_resource_account_id = kwargs.get("remote_resource_account_id", "None")
1278+
remote_resource_account_access_key = kwargs.get("remote_resource_account_access_key", "None")
1279+
remote_resource_region = kwargs.get("remote_resource_region", "None")
11901280
if remote_resource_type != "None":
11911281
self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type)
11921282
if remote_resource_identifier != "None":
11931283
self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier)
1284+
if remote_resource_account_id != "None":
1285+
assert remote_resource_identifier != "None"
1286+
self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_ACCOUNT_ID, remote_resource_account_id)
1287+
self.assertIsNone(attribute_dict.get(AWS_REMOTE_RESOURCE_ACCESS_KEY))
1288+
if remote_resource_account_access_key != "None":
1289+
assert remote_resource_identifier != "None"
1290+
self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_ACCESS_KEY, remote_resource_account_access_key)
1291+
self.assertIsNone(attribute_dict.get(AWS_REMOTE_RESOURCE_ACCOUNT_ID))
1292+
if remote_resource_region != "None":
1293+
self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_REGION, remote_resource_region)
11941294
self.check_sum(metric_name, dependency_dp.sum, expected_sum)
11951295

11961296
attribute_dict: Dict[str, AnyValue] = self._get_attributes_dict(service_dp.attributes)
@@ -1212,10 +1312,23 @@ def _assert_metric_attributes(
12121312
self._assert_str_attribute(attribute_dict, AWS_SPAN_KIND, kwargs.get("dependency_metric_span_kind") or "CLIENT")
12131313
remote_resource_type = kwargs.get("remote_resource_type", "None")
12141314
remote_resource_identifier = kwargs.get("remote_resource_identifier", "None")
1315+
remote_resource_account_id = kwargs.get("remote_resource_account_id", "None")
1316+
remote_resource_account_access_key = kwargs.get("remote_resource_account_access_key", "None")
1317+
remote_resource_region = kwargs.get("remote_resource_region", "None")
12151318
if remote_resource_type != "None":
12161319
self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type)
12171320
if remote_resource_identifier != "None":
12181321
self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier)
1322+
if remote_resource_account_id != "None":
1323+
assert remote_resource_identifier != "None"
1324+
self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_ACCOUNT_ID, remote_resource_account_id)
1325+
self.assertIsNone(attribute_dict.get(AWS_REMOTE_RESOURCE_ACCESS_KEY))
1326+
if remote_resource_account_access_key != "None":
1327+
assert remote_resource_identifier != "None"
1328+
self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_ACCESS_KEY, remote_resource_account_access_key)
1329+
self.assertIsNone(attribute_dict.get(AWS_REMOTE_RESOURCE_ACCOUNT_ID))
1330+
if remote_resource_region != "None":
1331+
self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_REGION, remote_resource_region)
12191332
self.check_sum(metric_name, dependency_dp.sum, expected_sum)
12201333

12211334
attribute_dict_service: Dict[str, AnyValue] = self._get_attributes_dict(service_dp.attributes)

contract-tests/tests/test/amazon/utils/application_signals_constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919
AWS_REMOTE_RESOURCE_IDENTIFIER: str = "aws.remote.resource.identifier"
2020
AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: str = 'aws.remote.resource.cfn.primary.identifier'
2121
AWS_SPAN_KIND: str = "aws.span.kind"
22+
AWS_REMOTE_RESOURCE_ACCESS_KEY: str = "aws.remote.resource.account.access_key"
23+
AWS_REMOTE_RESOURCE_ACCOUNT_ID: str = "aws.remote.resource.account.id"
24+
AWS_REMOTE_RESOURCE_REGION: str = "aws.remote.resource.region"

0 commit comments

Comments
 (0)