Skip to content

Commit 2bd6ec2

Browse files
authored
test(signature-v4-multi-region): use long-lived resources for sigv4a events test (#7039)
* test(signature-v4-multi-region): use long-lived resources for sigv4a events test * test(signature-v4-multi-region): beforeAll update * test(signature-v4-multi-region): timeouts
1 parent 2025aba commit 2bd6ec2

File tree

1 file changed

+56
-156
lines changed

1 file changed

+56
-156
lines changed

packages/signature-v4-multi-region/src/eventbridge-sigv4a-put-api.e2e.spec.ts

+56-156
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { Sha256 } from "@aws-crypto/sha256-js";
55
import {
66
CreateEndpointCommand,
77
CreateEventBusCommand,
8-
DeleteEndpointCommand,
9-
DeleteEventBusCommand,
108
DescribeEndpointCommand,
119
EndpointState,
1210
EventBridgeClient,
@@ -15,27 +13,24 @@ import {
1513
PutEventsCommandOutput,
1614
ReplicationState,
1715
} from "@aws-sdk/client-eventbridge";
18-
import { CreateHealthCheckCommand, DeleteHealthCheckCommand, Route53Client } from "@aws-sdk/client-route-53";
19-
import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
16+
import { CreateHealthCheckCommand, Route53Client } from "@aws-sdk/client-route-53";
2017
import { defaultProvider } from "@aws-sdk/credential-provider-node";
2118
import { SignatureV4MultiRegion } from "@aws-sdk/signature-v4-multi-region";
2219
import { HttpRequest } from "@smithy/protocol-http";
2320
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
2421

2522
const LONG_TIMEOUT = 5 * 60 * 1000;
26-
const POLLING_DELAY = 5000;
27-
const MAX_POLLING_ATTEMPTS = 40;
23+
24+
// Long-lived resources
25+
const RESOURCE_PREFIX = "jsv3-e2e-global";
2826

2927
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
3028

3129
const getSubdomainFromUrl = (url: string | undefined): string | undefined => {
3230
if (!url) return undefined;
3331
try {
3432
const parsedUrl = new URL(url);
35-
// Hostname will be like abcde.xyz.endpoint.events.amazonaws.com
3633
const hostnameParts = parsedUrl.hostname.split(".");
37-
// We can expect at least 5 parts: subdomain[0].subdomain[1].endpoint.events.amazonaws.com
38-
// Check if the 3rd part from the end is 'events'
3934
if (hostnameParts.length >= 5 && hostnameParts[hostnameParts.length - 3] === "events") {
4035
return `${hostnameParts[0]}.${hostnameParts[1]}`;
4136
}
@@ -46,71 +41,26 @@ const getSubdomainFromUrl = (url: string | undefined): string | undefined => {
4641
};
4742

4843
describe("EventBridge Client with SignatureV4a", () => {
49-
let stsClient: STSClient;
5044
let primaryEbClient: EventBridgeClient;
5145
let secondaryEbClient: EventBridgeClient;
5246
let route53Client: Route53Client;
5347
let signer: SignatureV4MultiRegion;
5448
let globalEbClient: EventBridgeClient;
55-
56-
let accountId: string;
57-
let timestamp: number;
5849
let primaryRegion: string;
5950
let secondaryRegion: string;
6051
let eventBusName: string;
52+
let endpointName: string;
6153
let primaryEventBusArn: string | undefined;
6254
let secondaryEventBusArn: string | undefined;
6355
let healthCheckId: string | undefined;
64-
let endpointName: string;
6556
let endpointArn: string | undefined;
6657

67-
const cleanupResources = async () => {
68-
try {
69-
if (endpointArn) {
70-
const managementClient = new EventBridgeClient({ region: primaryRegion });
71-
await managementClient.send(new DeleteEndpointCommand({ Name: endpointName }));
72-
managementClient.destroy();
73-
endpointArn = undefined; // Mark as deleted
74-
}
75-
} catch (error) {
76-
console.error(`Error deleting endpoint ${endpointName}:`, error);
77-
}
78-
79-
try {
80-
if (healthCheckId) {
81-
await route53Client.send(new DeleteHealthCheckCommand({ HealthCheckId: healthCheckId }));
82-
healthCheckId = undefined;
83-
}
84-
} catch (error) {
85-
console.error(`Error deleting health check ${healthCheckId}:`, error);
86-
}
87-
88-
try {
89-
if (primaryEventBusArn) {
90-
await primaryEbClient.send(new DeleteEventBusCommand({ Name: eventBusName }));
91-
primaryEventBusArn = undefined;
92-
}
93-
} catch (error) {
94-
console.error(`Error deleting event bus ${eventBusName} from ${primaryRegion}:`, error);
95-
}
96-
97-
try {
98-
if (secondaryEventBusArn) {
99-
await secondaryEbClient.send(new DeleteEventBusCommand({ Name: eventBusName }));
100-
secondaryEventBusArn = undefined;
101-
}
102-
} catch (error) {
103-
console.error(`Error deleting event bus ${eventBusName} from ${secondaryRegion}:`, error);
104-
}
105-
};
106-
10758
beforeAll(async () => {
10859
vi.setConfig({ hookTimeout: LONG_TIMEOUT });
10960

11061
primaryRegion = "us-west-2";
11162
secondaryRegion = "us-east-1";
11263

113-
stsClient = new STSClient({ region: primaryRegion });
11464
primaryEbClient = new EventBridgeClient({ region: primaryRegion });
11565
secondaryEbClient = new EventBridgeClient({ region: secondaryRegion });
11666
route53Client = new Route53Client({ region: "us-west-2" });
@@ -127,82 +77,60 @@ describe("EventBridge Client with SignatureV4a", () => {
12777
signer,
12878
});
12979

130-
// Account ID & Timestamp for Unique Names
80+
eventBusName = `${RESOURCE_PREFIX}-bus`;
81+
endpointName = `${RESOURCE_PREFIX}-endpoint`;
82+
83+
const managementClient = new EventBridgeClient({ region: primaryRegion });
13184
try {
132-
const identity = await stsClient.send(new GetCallerIdentityCommand({}));
133-
accountId = identity.Account!;
134-
timestamp = Date.now();
135-
136-
eventBusName = `test-global-bus-${accountId}-${timestamp}`;
137-
endpointName = `test-global-endpoint-${accountId}-${timestamp}`;
138-
139-
const primaryBus = await primaryEbClient.send(new CreateEventBusCommand({ Name: eventBusName }));
140-
primaryEventBusArn = primaryBus.EventBusArn;
141-
expect(primaryEventBusArn).toBeDefined();
142-
143-
const secondaryBus = await secondaryEbClient.send(new CreateEventBusCommand({ Name: eventBusName }));
144-
secondaryEventBusArn = secondaryBus.EventBusArn;
145-
expect(secondaryEventBusArn).toBeDefined();
146-
147-
// Create Route 53 Health Check: a basic one against a dummy target.
148-
const healthCheckCallerReference = `eb-putevents-test-${timestamp}`;
149-
const healthCheck = await route53Client.send(
150-
new CreateHealthCheckCommand({
151-
CallerReference: healthCheckCallerReference,
152-
HealthCheckConfig: {
153-
Type: "HTTP",
154-
FullyQualifiedDomainName: "example.com",
155-
Port: 80,
156-
ResourcePath: "/",
157-
RequestInterval: 30,
158-
FailureThreshold: 3,
159-
},
160-
})
161-
);
162-
healthCheckId = healthCheck.HealthCheck?.Id;
163-
expect(healthCheckId).toBeDefined();
164-
await wait(10000);
165-
166-
let managementClient: EventBridgeClient | undefined;
167-
try {
168-
managementClient = new EventBridgeClient({ region: primaryRegion });
169-
const createInput = {
170-
Name: endpointName,
171-
RoutingConfig: {
172-
FailoverConfig: {
173-
Primary: { HealthCheck: `arn:aws:route53:::healthcheck/${healthCheckId}` },
174-
Secondary: { Route: secondaryRegion },
85+
const existingEndpoint = await managementClient.send(new DescribeEndpointCommand({ Name: endpointName }));
86+
endpointArn = existingEndpoint.Arn;
87+
} catch (error: any) {
88+
if (error.name === "ResourceNotFoundException") {
89+
const primaryBus = await primaryEbClient.send(new CreateEventBusCommand({ Name: eventBusName }));
90+
primaryEventBusArn = primaryBus.EventBusArn;
91+
92+
const secondaryBus = await secondaryEbClient.send(new CreateEventBusCommand({ Name: eventBusName }));
93+
secondaryEventBusArn = secondaryBus.EventBusArn;
94+
95+
const healthCheck = await route53Client.send(
96+
new CreateHealthCheckCommand({
97+
CallerReference: `${RESOURCE_PREFIX}-${Date.now()}`,
98+
HealthCheckConfig: {
99+
Type: "HTTP",
100+
FullyQualifiedDomainName: "example.com",
101+
Port: 80,
102+
ResourcePath: "/",
103+
RequestInterval: 30,
104+
FailureThreshold: 3,
175105
},
176-
},
177-
ReplicationConfig: { State: ReplicationState.DISABLED },
178-
EventBuses: [{ EventBusArn: primaryEventBusArn }, { EventBusArn: secondaryEventBusArn }],
179-
};
180-
const createResponse = await managementClient.send(new CreateEndpointCommand(createInput));
181-
182-
expect(createResponse.$metadata.httpStatusCode).toBe(200);
183-
expect(createResponse.Name).toBe(endpointName);
184-
expect(createResponse.Arn).toBeDefined();
185-
186-
endpointArn = createResponse.Arn!;
187-
} catch (error) {
188-
console.error("Error during prerequisite resource creation:", error);
189-
await cleanupResources();
106+
})
107+
);
108+
healthCheckId = healthCheck.HealthCheck?.Id;
109+
await wait(10000);
110+
111+
const createEndpointResponse = await managementClient.send(
112+
new CreateEndpointCommand({
113+
Name: endpointName,
114+
RoutingConfig: {
115+
FailoverConfig: {
116+
Primary: { HealthCheck: `arn:aws:route53:::healthcheck/${healthCheckId}` },
117+
Secondary: { Route: secondaryRegion },
118+
},
119+
},
120+
ReplicationConfig: { State: ReplicationState.DISABLED },
121+
EventBuses: [{ EventBusArn: primaryEventBusArn }, { EventBusArn: secondaryEventBusArn }],
122+
})
123+
);
124+
endpointArn = createEndpointResponse.Arn;
125+
} else {
190126
throw error;
191-
} finally {
192-
managementClient?.destroy();
193127
}
194-
} catch (error) {
195-
console.error("Error during prerequisite resource creation:", error);
196-
await cleanupResources();
197-
throw error;
128+
} finally {
129+
managementClient.destroy();
198130
}
199131
}, LONG_TIMEOUT);
200132

201133
afterAll(async () => {
202-
vi.setConfig({ hookTimeout: LONG_TIMEOUT });
203-
await cleanupResources();
204-
// Clean up SDK clients
205-
stsClient?.destroy();
206134
primaryEbClient?.destroy();
207135
secondaryEbClient?.destroy();
208136
route53Client?.destroy();
@@ -251,42 +179,15 @@ describe("EventBridge Client with SignatureV4a", () => {
251179
it(
252180
"should send an event to an EventBridge Global Endpoint using SignatureV4a",
253181
async () => {
254-
expect(endpointArn).toBeDefined();
255-
256-
let attempts = 0;
257-
let currentState: EndpointState | string | undefined;
258-
let endpointUrl: string | undefined;
259182
const managementClient = new EventBridgeClient({ region: primaryRegion });
260-
261-
while (attempts < MAX_POLLING_ATTEMPTS) {
262-
attempts++;
263-
try {
264-
const describeResponse = await managementClient.send(new DescribeEndpointCommand({ Name: endpointName }));
265-
currentState = describeResponse.State;
266-
if (currentState === EndpointState.ACTIVE) {
267-
endpointUrl = describeResponse.EndpointUrl;
268-
break;
269-
}
270-
if (currentState === EndpointState.CREATE_FAILED || currentState === EndpointState.UPDATE_FAILED) {
271-
throw new Error(`Endpoint entered failed state: ${currentState}`);
272-
}
273-
} catch (error) {
274-
console.warn(`DescribeEndpoint failed (attempt ${attempts}):`, error);
275-
// Continue polling unless it's a definitive failure state
276-
}
277-
await wait(POLLING_DELAY);
278-
}
183+
const describeResponse = await managementClient.send(new DescribeEndpointCommand({ Name: endpointName }));
279184
managementClient.destroy();
280185

281-
if (currentState !== EndpointState.ACTIVE) {
282-
throw new Error(`Endpoint ${endpointName} did not become ACTIVE after ${attempts} attempts.`);
283-
}
284-
expect(endpointUrl).toBeDefined();
186+
expect(describeResponse.State).toBe(EndpointState.ACTIVE);
187+
expect(describeResponse.EndpointUrl).toBeDefined();
285188

286-
const endpointSubdomain = getSubdomainFromUrl(endpointUrl);
287-
if (!endpointSubdomain) {
288-
throw new Error(`Failed to extract subdomain from Endpoint URL: ${endpointUrl}`);
289-
}
189+
const endpointSubdomain = getSubdomainFromUrl(describeResponse.EndpointUrl);
190+
expect(endpointSubdomain).toBeDefined();
290191

291192
const putEventsInput: PutEventsCommandInput = {
292193
Entries: [
@@ -306,7 +207,6 @@ describe("EventBridge Client with SignatureV4a", () => {
306207
expect(putEventsResponse.FailedEntryCount).toBe(0);
307208
expect(putEventsResponse.Entries).toHaveLength(1);
308209
expect(putEventsResponse.Entries?.[0]?.EventId).toBeDefined();
309-
310210
// Note: Verifying the event *arrived* in the target bus (primary or secondary)
311211
// would require additional setup and is omitted
312212
// here to focus on the PutEvents call itself succeeding with SigV4a.

0 commit comments

Comments
 (0)