@@ -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,26 @@ 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" ;
23- import { afterAll , beforeAll , describe , expect , it , vi } from "vitest" ;
20+ import { beforeAll , describe , expect , it , vi } from "vitest" ;
2421
2522const LONG_TIMEOUT = 5 * 60 * 1000 ;
2623const POLLING_DELAY = 5000 ;
2724const MAX_POLLING_ATTEMPTS = 40 ;
2825
26+ // long-lived resources
27+ const RESOURCE_PREFIX = "jsv3-e2e-global" ;
28+
2929const wait = ( ms : number ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
3030
3131const getSubdomainFromUrl = ( url : string | undefined ) : string | undefined => {
3232 if ( ! url ) return undefined ;
3333 try {
3434 const parsedUrl = new URL ( url ) ;
35- // Hostname will be like abcde.xyz.endpoint.events.amazonaws.com
3635 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'
3936 if ( hostnameParts . length >= 5 && hostnameParts [ hostnameParts . length - 3 ] === "events" ) {
4037 return `${ hostnameParts [ 0 ] } .${ hostnameParts [ 1 ] } ` ;
4138 }
@@ -46,71 +43,25 @@ const getSubdomainFromUrl = (url: string | undefined): string | undefined => {
4643} ;
4744
4845describe ( "EventBridge Client with SignatureV4a" , ( ) => {
49- let stsClient : STSClient ;
5046 let primaryEbClient : EventBridgeClient ;
5147 let secondaryEbClient : EventBridgeClient ;
5248 let route53Client : Route53Client ;
5349 let signer : SignatureV4MultiRegion ;
5450 let globalEbClient : EventBridgeClient ;
55-
56- let accountId : string ;
57- let timestamp : number ;
5851 let primaryRegion : string ;
5952 let secondaryRegion : string ;
6053 let eventBusName : string ;
54+ let endpointName : string ;
6155 let primaryEventBusArn : string | undefined ;
6256 let secondaryEventBusArn : string | undefined ;
6357 let healthCheckId : string | undefined ;
64- let endpointName : string ;
65- let endpointArn : string | undefined ;
66-
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- } ;
10658
10759 beforeAll ( async ( ) => {
10860 vi . setConfig ( { hookTimeout : LONG_TIMEOUT } ) ;
10961
11062 primaryRegion = "us-west-2" ;
11163 secondaryRegion = "us-east-1" ;
11264
113- stsClient = new STSClient ( { region : primaryRegion } ) ;
11465 primaryEbClient = new EventBridgeClient ( { region : primaryRegion } ) ;
11566 secondaryEbClient = new EventBridgeClient ( { region : secondaryRegion } ) ;
11667 route53Client = new Route53Client ( { region : "us-west-2" } ) ;
@@ -127,87 +78,69 @@ describe("EventBridge Client with SignatureV4a", () => {
12778 signer,
12879 } ) ;
12980
130- // Account ID & Timestamp for Unique Names
13181 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 ) ;
82+ eventBusName = `${ RESOURCE_PREFIX } -bus` ;
83+ endpointName = `${ RESOURCE_PREFIX } -endpoint` ;
16584
166- let managementClient : EventBridgeClient | undefined ;
85+ const managementClient = new EventBridgeClient ( { region : primaryRegion } ) ;
16786 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 } ,
175- } ,
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 ( ) ;
190- throw error ;
87+ await managementClient . send ( new DescribeEndpointCommand ( { Name : endpointName } ) ) ;
88+ } catch ( error : any ) {
89+ if ( error . name === "ResourceNotFoundException" ) {
90+ // Create resources if they don't exist
91+ const primaryBus = await primaryEbClient . send ( new CreateEventBusCommand ( { Name : eventBusName } ) ) ;
92+ primaryEventBusArn = primaryBus . EventBusArn ;
93+
94+ const secondaryBus = await secondaryEbClient . send ( new CreateEventBusCommand ( { Name : eventBusName } ) ) ;
95+ secondaryEventBusArn = secondaryBus . EventBusArn ;
96+
97+ const healthCheck = await route53Client . send (
98+ new CreateHealthCheckCommand ( {
99+ CallerReference : `${ RESOURCE_PREFIX } -${ Date . now ( ) } ` ,
100+ HealthCheckConfig : {
101+ Type : "HTTP" ,
102+ FullyQualifiedDomainName : "example.com" ,
103+ Port : 80 ,
104+ ResourcePath : "/" ,
105+ RequestInterval : 30 ,
106+ FailureThreshold : 3 ,
107+ } ,
108+ } )
109+ ) ;
110+ healthCheckId = healthCheck . HealthCheck ?. Id ;
111+ await wait ( 10000 ) ;
112+
113+ const createEndpointResponse = await managementClient . send (
114+ new CreateEndpointCommand ( {
115+ Name : endpointName ,
116+ RoutingConfig : {
117+ FailoverConfig : {
118+ Primary : { HealthCheck : `arn:aws:route53:::healthcheck/${ healthCheckId } ` } ,
119+ Secondary : { Route : secondaryRegion } ,
120+ } ,
121+ } ,
122+ ReplicationConfig : { State : ReplicationState . DISABLED } ,
123+ EventBuses : [ { EventBusArn : primaryEventBusArn } , { EventBusArn : secondaryEventBusArn } ] ,
124+ } )
125+ ) ;
126+ } else {
127+ throw error ;
128+ }
191129 } finally {
192- managementClient ? .destroy ( ) ;
130+ managementClient . destroy ( ) ;
193131 }
194132 } catch ( error ) {
195- console . error ( "Error during prerequisite resource creation:" , error ) ;
196- await cleanupResources ( ) ;
133+ console . error ( "Error during setup:" , error ) ;
197134 throw error ;
198135 }
199136 } , LONG_TIMEOUT ) ;
200137
201- afterAll ( async ( ) => {
202- vi . setConfig ( { hookTimeout : LONG_TIMEOUT } ) ;
203- await cleanupResources ( ) ;
204- // Clean up SDK clients
205- stsClient ?. destroy ( ) ;
138+ beforeAll ( async ( ) => {
206139 primaryEbClient ?. destroy ( ) ;
207140 secondaryEbClient ?. destroy ( ) ;
208141 route53Client ?. destroy ( ) ;
209142 globalEbClient ?. destroy ( ) ;
210- } , LONG_TIMEOUT ) ;
143+ } ) ;
211144
212145 it ( "should add SigV4a headers when signing an EventBridge request (mocked)" , async ( ) => {
213146 expect ( signer ) . toBeDefined ( ) ;
@@ -251,8 +184,6 @@ describe("EventBridge Client with SignatureV4a", () => {
251184 it (
252185 "should send an event to an EventBridge Global Endpoint using SignatureV4a" ,
253186 async ( ) => {
254- expect ( endpointArn ) . toBeDefined ( ) ;
255-
256187 let attempts = 0 ;
257188 let currentState : EndpointState | string | undefined ;
258189 let endpointUrl : string | undefined ;
@@ -272,7 +203,6 @@ describe("EventBridge Client with SignatureV4a", () => {
272203 }
273204 } catch ( error ) {
274205 console . warn ( `DescribeEndpoint failed (attempt ${ attempts } ):` , error ) ;
275- // Continue polling unless it's a definitive failure state
276206 }
277207 await wait ( POLLING_DELAY ) ;
278208 }
@@ -306,10 +236,6 @@ describe("EventBridge Client with SignatureV4a", () => {
306236 expect ( putEventsResponse . FailedEntryCount ) . toBe ( 0 ) ;
307237 expect ( putEventsResponse . Entries ) . toHaveLength ( 1 ) ;
308238 expect ( putEventsResponse . Entries ?. [ 0 ] ?. EventId ) . toBeDefined ( ) ;
309-
310- // Note: Verifying the event *arrived* in the target bus (primary or secondary)
311- // would require additional setup and is omitted
312- // here to focus on the PutEvents call itself succeeding with SigV4a.
313239 } ,
314240 LONG_TIMEOUT
315241 ) ;
0 commit comments