Skip to content

Commit b1f31dd

Browse files
authored
test: add assorted integ/e2e tests (#7230)
* test: add assorted integ/e2e tests * test(packages): integration tests for signers
1 parent a746aa8 commit b1f31dd

File tree

16 files changed

+400
-9
lines changed

16 files changed

+400
-9
lines changed

clients/client-s3/test/e2e/s3-object-features.e2e.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,67 @@ describe("@aws-sdk/client-s3", () => {
9191
await deleteObject("hello");
9292
expect(await objectExists("hello")).toBe(false);
9393
});
94+
95+
describe("keys with special characters", () => {
96+
const keys = [
97+
`\r`,
98+
`\n`,
99+
`\x85`,
100+
`\u2028`,
101+
"\n \n",
102+
"a\r\n b\n c\r",
103+
"a\r\u0085 b\u0085",
104+
"a\r\u2028 b\u0085 c\u2028",
105+
];
106+
107+
beforeAll(async () => {
108+
await Promise.all(
109+
keys.map(async (Key) => {
110+
await client.putObject({
111+
Bucket,
112+
Key,
113+
Body: Key,
114+
});
115+
})
116+
);
117+
});
118+
119+
afterAll(async () => {
120+
for (const key of keys) {
121+
await client
122+
.deleteObject({
123+
Bucket,
124+
Key: key,
125+
})
126+
.catch(() => {});
127+
}
128+
});
129+
130+
it("can delete keys containing special characters", async () => {
131+
await client.deleteObjects({
132+
Bucket,
133+
Delete: {
134+
Objects: keys.map((Key) => ({
135+
Key,
136+
})),
137+
},
138+
});
139+
140+
await Promise.all(
141+
keys.map(async (Key) => {
142+
return client
143+
.headObject({
144+
Bucket,
145+
Key,
146+
})
147+
.catch((e: any) => e)
148+
.then((r) => {
149+
expect((r ?? r.$response).$metadata.httpStatusCode).toEqual(404);
150+
});
151+
})
152+
);
153+
});
154+
});
94155
});
95156

96157
describe("Content length", () => {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, test as it } from "vitest";
2+
3+
import { S3 } from "@aws-sdk/client-s3";
4+
import { AwsCredentialIdentity } from "@smithy/types";
5+
import { requireRequestsFrom } from "@aws-sdk/aws-util-test/src";
6+
7+
describe("S3 Object Lambda", () => {
8+
const region = "us-west-2";
9+
const credentials: AwsCredentialIdentity = {
10+
accessKeyId: "",
11+
secretAccessKey: "",
12+
};
13+
14+
it("can make a GET request to an S3 Object Lambda ARN", async () => {
15+
const s3 = new S3({
16+
region,
17+
credentials,
18+
});
19+
20+
requireRequestsFrom(s3).toMatch({
21+
hostname: "my-access-point-123456789012.s3-object-lambda.us-west-2.amazonaws.com",
22+
query: {
23+
"x-id": "GetObject",
24+
},
25+
headers: {
26+
authorization: /=\/\d+\/us-west-2\/s3-object-lambda\/aws4_request/,
27+
},
28+
path: "/a",
29+
});
30+
31+
// slash delimiter
32+
await s3.getObject({
33+
Bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint/my-access-point",
34+
Key: "a",
35+
});
36+
37+
// colon delimiter
38+
await s3.getObject({
39+
Bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:my-access-point",
40+
Key: "a",
41+
});
42+
});
43+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, test as it, expect } from "vitest";
2+
3+
import { S3Client } from "@aws-sdk/client-s3";
4+
import { LambdaClient } from "@aws-sdk/client-lambda";
5+
import { EMRClient } from "@aws-sdk/client-emr";
6+
import { SageMakerClient } from "@aws-sdk/client-sagemaker";
7+
import { CloudWatchClient } from "@aws-sdk/client-cloudwatch";
8+
import { STSClient } from "@aws-sdk/client-sts";
9+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
10+
import { CloudFormationClient } from "@aws-sdk/client-cloudformation";
11+
import { SFNClient } from "@aws-sdk/client-sfn";
12+
13+
const clients = [
14+
new S3Client(),
15+
new LambdaClient(),
16+
new EMRClient(),
17+
new SageMakerClient(),
18+
new CloudFormationClient(),
19+
new CloudWatchClient(),
20+
new STSClient(),
21+
new DynamoDBClient(),
22+
new SFNClient(),
23+
];
24+
25+
const serviceIds = [
26+
"S3",
27+
"Lambda",
28+
"EMR",
29+
"SageMaker",
30+
"CloudFormation",
31+
"CloudWatch",
32+
"STS",
33+
"DynamoDB",
34+
"SFN",
35+
] as string[];
36+
37+
describe("service ids (various clients)", () => {
38+
for (const [i, client] of Object.entries(clients)) {
39+
it("should have a service id matching its class name", () => {
40+
const index = parseInt(i);
41+
42+
expect(client.config.serviceId).toEqual(serviceIds[index]);
43+
expect(client.constructor.name.replace(/Client$/, "")).toEqual(serviceIds[index]);
44+
});
45+
}
46+
});

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
1414
"extract:docs": "api-extractor run --local",
1515
"test": "yarn g:vitest run",
16-
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
1716
"test:watch": "yarn g:vitest watch",
17+
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
1818
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.ts"
1919
},
2020
"main": "./dist-cjs/index.js",

packages/core/src/submodules/client/pagination.integ.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ describe("pagination", () => {
8484
}
8585

8686
expect(pages).toEqual(2);
87+
/**
88+
* As of writing, paginators mutate the input object.
89+
* In case anyone is relying on this behavior to observe the pagination token,
90+
* we are enforcing it with this assertion.
91+
*/
8792
expect(requestParams.ExclusiveStartKey).toEqual({
8893
id: { S: "2" },
8994
});
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { requireRequestsFrom } from "@aws-sdk/aws-util-test/src";
2+
import {
3+
type DescribeClusterCommandInput,
4+
Route53RecoveryControlConfig,
5+
waitUntilClusterCreated,
6+
} from "@aws-sdk/client-route53-recovery-control-config";
7+
import { type HeadObjectCommandInput, S3, waitUntilObjectExists } from "@aws-sdk/client-s3";
8+
import { HttpResponse } from "@smithy/protocol-http";
9+
import { Readable } from "node:stream";
10+
import { describe, test as it } from "vitest";
11+
12+
describe("waiters", () => {
13+
it("should poll until a return condition is met (http status)", async () => {
14+
const s3 = new S3({
15+
credentials: {
16+
accessKeyId: "INTEG_TEST",
17+
secretAccessKey: "INTEG_TEST",
18+
},
19+
region: "us-west-2",
20+
});
21+
22+
requireRequestsFrom(s3)
23+
.toMatch(
24+
{
25+
hostname: /s3\.us-west-2\.amazonaws\.com/,
26+
},
27+
{
28+
hostname: /s3\.us-west-2\.amazonaws\.com/,
29+
}
30+
)
31+
.respondWith(
32+
new HttpResponse({
33+
statusCode: 404,
34+
headers: {},
35+
}),
36+
new HttpResponse({
37+
statusCode: 200,
38+
headers: {},
39+
})
40+
);
41+
42+
const waiterParams: HeadObjectCommandInput = {
43+
Bucket: "@@@@",
44+
Key: "####",
45+
};
46+
47+
await waitUntilObjectExists({ client: s3, maxDelay: 5, maxWaitTime: 20 }, waiterParams);
48+
});
49+
50+
it("should poll until a return condition is met (nested property)", async () => {
51+
const r53 = new Route53RecoveryControlConfig({
52+
credentials: {
53+
accessKeyId: "INTEG_TEST",
54+
secretAccessKey: "INTEG_TEST",
55+
},
56+
region: "us-west-2",
57+
});
58+
59+
requireRequestsFrom(r53)
60+
.toMatch(
61+
{
62+
hostname: /amazon/,
63+
},
64+
{
65+
hostname: /amazon/,
66+
}
67+
)
68+
.respondWith(
69+
new HttpResponse({
70+
statusCode: 200,
71+
headers: {},
72+
body: Readable.from(
73+
JSON.stringify({
74+
Cluster: {
75+
Status: "PENDING",
76+
},
77+
})
78+
),
79+
}),
80+
new HttpResponse({
81+
statusCode: 200,
82+
headers: {},
83+
body: Readable.from(
84+
JSON.stringify({
85+
Cluster: {
86+
Status: "DEPLOYED",
87+
},
88+
})
89+
),
90+
})
91+
);
92+
93+
const waiterParams: DescribeClusterCommandInput = {
94+
ClusterArn: "@@@@",
95+
};
96+
97+
await waitUntilClusterCreated({ client: r53, maxDelay: 5, maxWaitTime: 20 }, waiterParams);
98+
});
99+
}, 60_000);

packages/dsql-signer/README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,31 +32,36 @@ const { DsqlSigner } = require("@aws-sdk/dsql-signer");
3232
### Generate Authentication Token for Dsql IAM Authentication
3333

3434
```js
35+
import { Hash } from "@smithy/hash-node";
36+
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
37+
3538
const signer = new DsqlSigner({
3639
/**
3740
* Required: The hostname of the database to connect to.
3841
*/
39-
hostname: "foo0bar1baz2quux3quux4.dsql.us-east-1.on.aws";
42+
hostname: "foo0bar1baz2quux3quux4.dsql.us-east-1.on.aws",
4043

4144
/**
4245
* Optional: The region the database is located in. Uses the region inferred from the runtime if omitted.
4346
*/
44-
region?: "us-east-1";
47+
region: "us-east-1",
4548

4649
/**
4750
* Optional: The SHA256 hasher constructor to sign the request.
4851
*/
49-
sha256?: HashCtor;
52+
sha256: Hash.bind(null, "sha256"),
5053

5154
/**
5255
* Optional: The amount of time in seconds the generated token is valid
5356
*/
54-
expiresIn?: 3600;
57+
expiresIn: 3600,
5558

5659
/**
5760
* Optional: The AWS credentials to sign requests with. Uses the default credential provider chain if not specified.
61+
* You can use any credential provider from https://www.npmjs.com/package/@aws-sdk/credential-providers,
62+
* or provide a credentials object.
5863
*/
59-
credentials?: fromNodeCredentialProvider();
64+
credentials: fromNodeProviderChain(),
6065
});
6166

6267
// Creates auth token.

packages/dsql-signer/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
1515
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
1616
"extract:docs": "api-extractor run --local",
17-
"test": "yarn g:vitest run"
17+
"test": "yarn g:vitest run",
18+
"test:watch": "yarn g:vitest watch",
19+
"test:integration": "yarn g:vitest run -c vitest.config.integ.ts",
20+
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.ts"
1821
},
1922
"engines": {
2023
"node": ">=18.0.0"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Sha256 } from "@aws-crypto/sha256-js";
2+
import { describe, expect, test as it } from "vitest";
3+
4+
import { DsqlSigner, DsqlSignerConfig } from "./Signer";
5+
6+
describe("dsql-signer integration", () => {
7+
it("creates a token", async () => {
8+
const allParamsConfig: DsqlSignerConfig = {
9+
hostname: "localhost",
10+
region: "us-east-2",
11+
credentials: {
12+
accessKeyId: "allParamsAccessKey",
13+
secretAccessKey: "allParamsSecretAccessKey",
14+
sessionToken: "allParamsSessionToken",
15+
},
16+
expiresIn: 1000,
17+
sha256: Sha256,
18+
};
19+
const signer = new DsqlSigner(allParamsConfig);
20+
const token = await signer.getDbConnectAdminAuthToken();
21+
22+
expect(token.split("&").sort()).toMatchObject([
23+
/localhost\/\?Action=DbConnectAdmin/,
24+
/X-Amz-Algorithm=AWS4-HMAC-SHA256/,
25+
/X-Amz-Credential=allParamsAccessKey%2F(\d+)%2Fus-east-2%2Fdsql%2Faws4_request/,
26+
/X-Amz-Date=(\d+T\d+Z)/,
27+
/X-Amz-Expires=1000/,
28+
/X-Amz-Security-Token=allParamsSessionToken/,
29+
/X-Amz-Signature=(.*?)/,
30+
/X-Amz-SignedHeaders=host/,
31+
]);
32+
});
33+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from "vitest/config";
2+
3+
export default defineConfig({
4+
test: {
5+
include: ["**/*.integ.spec.ts"],
6+
environment: "node",
7+
},
8+
});

0 commit comments

Comments
 (0)