@@ -5,8 +5,6 @@ import { Sha256 } from "@aws-crypto/sha256-js";
55import {
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" ;
2017import { defaultProvider } from "@aws-sdk/credential-provider-node" ;
2118import { SignatureV4MultiRegion } from "@aws-sdk/signature-v4-multi-region" ;
2219import { HttpRequest } from "@smithy/protocol-http" ;
2320import { afterAll , beforeAll , describe , expect , it , vi } from "vitest" ;
2421
2522const 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
2927const wait = ( ms : number ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
3028
3129const 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
4843describe ( "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