1+ import Fastify from "fastify" ;
2+ import EventBusPlugin from "./index" ;
3+ import { EventBusOptions , EventHandler } from "./interfaces" ;
4+
5+ describe ( "EventBus Plugin" , ( ) => {
6+ let fastify : ReturnType < typeof Fastify > ;
7+ let mockValidateMsg : jest . Mock ;
8+ let mockProcessError : jest . Mock ;
9+ let mockHandler : EventHandler ;
10+
11+ beforeEach ( ( ) => {
12+ fastify = Fastify ( { logger : false } ) ;
13+ mockValidateMsg = jest . fn ( ) ;
14+ mockProcessError = jest . fn ( ) . mockReturnValue ( { err : new Error ( "test" ) , status : 500 } ) ;
15+ mockHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
16+ } ) ;
17+
18+ afterEach ( async ( ) => {
19+ try {
20+ await fastify . close ( ) ;
21+ } catch ( error ) {
22+ // Ignore errors from fastify already being closed
23+ if ( ! error . message . includes ( 'already been closed' ) ) {
24+ throw error ;
25+ }
26+ }
27+ } ) ;
28+
29+ describe ( "Plugin Registration" , ( ) => {
30+ it ( "should register with in-process bus type (default)" , async ( ) => {
31+ const options : EventBusOptions = {
32+ busType : "in-process" ,
33+ handlers : [ {
34+ file : "test.ts" ,
35+ handlers : { testEvent : mockHandler }
36+ } ] ,
37+ validateMsg : mockValidateMsg ,
38+ processError : mockProcessError ,
39+ } ;
40+
41+ await fastify . register ( EventBusPlugin , options ) ;
42+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
43+ } ) ;
44+
45+ it ( "should require proper environment variables for rabbitmq" , async ( ) => {
46+ // Test that rabbitmq registration fails without required env vars
47+ const options : EventBusOptions = {
48+ busType : "rabbitmq" ,
49+ handlers : [ ] ,
50+ validateMsg : mockValidateMsg ,
51+ processError : mockProcessError ,
52+ } ;
53+
54+ await expect ( fastify . register ( EventBusPlugin , options ) ) . rejects . toThrow ( ) ;
55+ } ) ;
56+
57+ it ( "should register with gcp-pubsub bus type" , async ( ) => {
58+ process . env . EVENT_TOPIC = "test-topic" ;
59+
60+ const options : EventBusOptions = {
61+ busType : "gcp-pubsub" ,
62+ topic : "test-topic" ,
63+ handlers : [ {
64+ file : "test.ts" ,
65+ handlers : { testEvent : mockHandler }
66+ } ] ,
67+ validateMsg : mockValidateMsg ,
68+ processError : mockProcessError ,
69+ } ;
70+
71+ await fastify . register ( EventBusPlugin , options ) ;
72+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
73+
74+ delete process . env . EVENT_TOPIC ;
75+ } ) ;
76+
77+ it ( "should register with azure-servicebus bus type" , async ( ) => {
78+ process . env . EVENT_TOPIC = "test-topic" ;
79+
80+ const options : EventBusOptions = {
81+ busType : "azure-servicebus" ,
82+ namespace : "test-namespace" ,
83+ topic : "test-topic" ,
84+ handlers : [ {
85+ file : "test.ts" ,
86+ handlers : { testEvent : mockHandler }
87+ } ] ,
88+ validateMsg : mockValidateMsg ,
89+ processError : mockProcessError ,
90+ } ;
91+
92+ await fastify . register ( EventBusPlugin , options ) ;
93+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
94+
95+ delete process . env . EVENT_TOPIC ;
96+ } ) ;
97+
98+ it ( "should throw error for azure-servicebus without namespace" , async ( ) => {
99+ const options : EventBusOptions = {
100+ busType : "azure-servicebus" ,
101+ handlers : [ {
102+ file : "test.ts" ,
103+ handlers : { testEvent : mockHandler }
104+ } ] ,
105+ validateMsg : mockValidateMsg ,
106+ processError : mockProcessError ,
107+ } ;
108+
109+ await expect ( fastify . register ( EventBusPlugin , options ) ) . rejects . toThrow (
110+ "Azure ServiceBus needs the namespace specified"
111+ ) ;
112+ } ) ;
113+ } ) ;
114+
115+ describe ( "Event Publish Route" , ( ) => {
116+ beforeEach ( async ( ) => {
117+ const options : EventBusOptions = {
118+ busType : "in-process" ,
119+ handlers : [ {
120+ file : "test.ts" ,
121+ handlers : { testEvent : mockHandler }
122+ } ] ,
123+ validateMsg : mockValidateMsg ,
124+ processError : mockProcessError ,
125+ } ;
126+
127+ await fastify . register ( EventBusPlugin , options ) ;
128+ } ) ;
129+
130+ it ( "should register publish route by default" , async ( ) => {
131+ const response = await fastify . inject ( {
132+ method : "POST" ,
133+ url : "/event-bus/publish/testEvent" ,
134+ payload : { test : "data" }
135+ } ) ;
136+
137+ expect ( response . statusCode ) . toBe ( 200 ) ;
138+ expect ( response . body ) . toBe ( "OK" ) ;
139+ } ) ;
140+
141+ it ( "should handle string payload via query param" , async ( ) => {
142+ const response = await fastify . inject ( {
143+ method : "POST" ,
144+ url : "/event-bus/publish/testEvent?stringPayload=test-string" ,
145+ payload : { }
146+ } ) ;
147+
148+ expect ( response . statusCode ) . toBe ( 200 ) ;
149+ expect ( response . body ) . toBe ( "OK" ) ;
150+ } ) ;
151+
152+ it ( "should handle integer payload via query param" , async ( ) => {
153+ const response = await fastify . inject ( {
154+ method : "POST" ,
155+ url : "/event-bus/publish/testEvent?integerPayload=123" ,
156+ payload : { }
157+ } ) ;
158+
159+ expect ( response . statusCode ) . toBe ( 200 ) ;
160+ expect ( response . body ) . toBe ( "OK" ) ;
161+ } ) ;
162+
163+ it ( "should not register publish route when disabled" , async ( ) => {
164+ const fastifyDisabled = Fastify ( { logger : false } ) ;
165+
166+ const options : EventBusOptions = {
167+ busType : "in-process" ,
168+ handlers : [ {
169+ file : "test.ts" ,
170+ handlers : { testEvent : mockHandler }
171+ } ] ,
172+ validateMsg : mockValidateMsg ,
173+ processError : mockProcessError ,
174+ disableEventPublishRoute : true ,
175+ } ;
176+
177+ await fastifyDisabled . register ( EventBusPlugin , options ) ;
178+
179+ const response = await fastifyDisabled . inject ( {
180+ method : "POST" ,
181+ url : "/event-bus/publish/testEvent" ,
182+ payload : { test : "data" }
183+ } ) ;
184+
185+ expect ( response . statusCode ) . toBe ( 404 ) ;
186+ await fastifyDisabled . close ( ) ;
187+ } ) ;
188+ } ) ;
189+
190+ describe ( "EventBus Decorator" , ( ) => {
191+ beforeEach ( async ( ) => {
192+ const options : EventBusOptions = {
193+ busType : "in-process" ,
194+ handlers : [ {
195+ file : "test.ts" ,
196+ handlers : { testEvent : mockHandler }
197+ } ] ,
198+ validateMsg : mockValidateMsg ,
199+ processError : mockProcessError ,
200+ } ;
201+
202+ await fastify . register ( EventBusPlugin , options ) ;
203+ } ) ;
204+
205+ it ( "should provide EventBus on fastify instance" , ( ) => {
206+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
207+ expect ( typeof fastify . EventBus . publish ) . toBe ( "function" ) ;
208+ } ) ;
209+
210+ it ( "should allow publishing events programmatically" , ( ) => {
211+ expect ( ( ) => {
212+ fastify . EventBus . publish ( "testEvent" , { test : "data" } ) ;
213+ } ) . not . toThrow ( ) ;
214+ } ) ;
215+
216+ it ( "should allow publishing events with delay" , ( ) => {
217+ expect ( ( ) => {
218+ fastify . EventBus . publish ( "testEvent" , { test : "data" } , 1000 ) ;
219+ } ) . not . toThrow ( ) ;
220+ } ) ;
221+ } ) ;
222+
223+ describe ( "Configuration Options" , ( ) => {
224+ it ( "should handle empty handlers array" , async ( ) => {
225+ const options : EventBusOptions = {
226+ busType : "in-process" ,
227+ handlers : [ ] ,
228+ validateMsg : mockValidateMsg ,
229+ processError : mockProcessError ,
230+ } ;
231+
232+ await fastify . register ( EventBusPlugin , options ) ;
233+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
234+ } ) ;
235+
236+ it ( "should handle multiple handler files" , async ( ) => {
237+ const handler1 : EventHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
238+ const handler2 : EventHandler = jest . fn ( ) . mockResolvedValue ( undefined ) ;
239+
240+ const options : EventBusOptions = {
241+ busType : "in-process" ,
242+ handlers : [
243+ {
244+ file : "handlers1.ts" ,
245+ handlers : { event1 : handler1 }
246+ } ,
247+ {
248+ file : "handlers2.ts" ,
249+ handlers : { event2 : handler2 }
250+ }
251+ ] ,
252+ validateMsg : mockValidateMsg ,
253+ processError : mockProcessError ,
254+ } ;
255+
256+ await fastify . register ( EventBusPlugin , options ) ;
257+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
258+ } ) ;
259+
260+ it ( "should handle topic and namespace options" , async ( ) => {
261+ const options : EventBusOptions = {
262+ busType : "in-process" ,
263+ topic : "test-topic" ,
264+ namespace : "test-namespace" ,
265+ handlers : [ {
266+ file : "test.ts" ,
267+ handlers : { testEvent : mockHandler }
268+ } ] ,
269+ validateMsg : mockValidateMsg ,
270+ processError : mockProcessError ,
271+ } ;
272+
273+ await fastify . register ( EventBusPlugin , options ) ;
274+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
275+ } ) ;
276+
277+ it ( "should handle actionConcurrency option" , async ( ) => {
278+ const options : EventBusOptions = {
279+ busType : "in-process" ,
280+ handlers : [ {
281+ file : "test.ts" ,
282+ handlers : { testEvent : mockHandler }
283+ } ] ,
284+ validateMsg : mockValidateMsg ,
285+ processError : mockProcessError ,
286+ actionConcurrency : 5 ,
287+ } ;
288+
289+ await fastify . register ( EventBusPlugin , options ) ;
290+ expect ( fastify . EventBus ) . toBeDefined ( ) ;
291+ } ) ;
292+ } ) ;
293+ } ) ;
0 commit comments