diff --git a/src/mod/eventsystem/event_system_test.go b/src/mod/eventsystem/event_system_test.go index f4751a8..8ae3798 100644 --- a/src/mod/eventsystem/event_system_test.go +++ b/src/mod/eventsystem/event_system_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/google/uuid" + "imuslab.com/zoraxy/mod/info/logger" "imuslab.com/zoraxy/mod/plugins/zoraxy_plugin/events" ) @@ -116,3 +117,209 @@ func TestEventDeSerialization(t *testing.T) { }) } } + +type TestListener struct { + id ListenerID + receivedEvents chan events.Event +} + +func (tl *TestListener) GetID() ListenerID { + return tl.id +} + +func (tl *TestListener) Notify(event events.Event) error { + tl.receivedEvents <- event + return nil +} + +func TestEventEmission(t *testing.T) { + logger, err := logger.NewFmtLogger() + if err != nil { + t.Fatalf("Failed to create logger: %v", err) + } + em := eventManager{ + subscriptions: make(map[events.EventName][]ListenerID), + subscribers: make(map[ListenerID]Listener), + logger: logger, + } + + // Create a test listener + listenerID := ListenerID("testListener") + testListener := &TestListener{ + id: listenerID, + receivedEvents: make(chan events.Event, 10), + } + + // Register the listener for BlacklistedIPBlocked events + if err := em.RegisterSubscriberToEvent(testListener, events.EventBlacklistedIPBlocked); err != nil { + t.Fatalf("Failed to register subscriber: %v", err) + } + + // Emit a BlacklistedIPBlocked event + testEvent := &events.BlacklistedIPBlockedEvent{ + IP: "192.168.1.1", + Comment: "Malicious activity detected", + RequestedURL: "http://mysite.com/admin", + Hostname: "mysite.com", + UserAgent: "BadBot/1.0", + Method: "GET", + } + em.Emit(testEvent) + + // Verify that the listener received the event + select { + case receivedEvent := <-testListener.receivedEvents: + if receivedEvent.Name != events.EventBlacklistedIPBlocked { + t.Fatalf("Unexpected event received by listener.\nGot: %+v\nWant: %+v", receivedEvent, testEvent) + } + case <-time.After(1 * time.Second): + t.Fatal("Timeout waiting for event to be received by listener") + } + // Unregister the listener + if err := em.UnregisterSubscriber(listenerID); err != nil { + t.Fatalf("Failed to unregister subscriber: %v", err) + } +} + +func TestEventEmissionToSpecificListener(t *testing.T) { + logger, err := logger.NewFmtLogger() + if err != nil { + t.Fatalf("Failed to create logger: %v", err) + } + em := eventManager{ + subscriptions: make(map[events.EventName][]ListenerID), + subscribers: make(map[ListenerID]Listener), + logger: logger, + } + + // Create a few test listeners + // listener1 and listener2 are plugins that will send events to each other + // moderator is a plugin that is subscribed to EventCustom, so it will receive all custom events + // otherListener is neither subscribed to EventCustom nor a recipient of any custom event, so it should not receive any events + listener1ID := ListenerID("pluginA") + listener1 := &TestListener{ + id: listener1ID, + receivedEvents: make(chan events.Event, 10), + } + em.RegisterSubscriberToEvent(listener1, events.EventAccessRuleCreated) // We need to register to some event to be a valid subscriber + + listener2ID := ListenerID("pluginB") + listener2 := &TestListener{ + id: listener2ID, + receivedEvents: make(chan events.Event, 10), + } + em.RegisterSubscriberToEvent(listener2, events.EventAccessRuleCreated) // We need to register to some event to be a valid subscriber + + moderatorID := ListenerID("moderator") + moderator := &TestListener{ + id: moderatorID, + receivedEvents: make(chan events.Event, 10), + } + em.RegisterSubscriberToEvent(moderator, events.EventCustom) + + otherListenerID := ListenerID("pluginD") + otherListener := &TestListener{ + id: otherListenerID, + receivedEvents: make(chan events.Event, 10), + } + em.RegisterSubscriberToEvent(otherListener, events.EventAccessRuleCreated) // We need to register to some event to be a valid subscriber + + // Send a custom event from listener1 to listener2 + customEvent := &events.CustomEvent{ + SourcePlugin: "pluginA", + Recipients: []string{"pluginB"}, + Payload: map[string]interface{}{ + "message": "Hello from pluginA", + }, + } + em.EmitToSubscribersAnd([]ListenerID{listener2ID}, customEvent) + + // Verify that listener2 received the event + select { + case receivedEvent := <-listener2.receivedEvents: + if receivedEvent.Name != events.EventCustom { + t.Fatalf("Unexpected event received by listener2.\nGot: %+v\nWant: %+v", receivedEvent, customEvent) + } + case <-time.After(1 * time.Second): + t.Fatal("Timeout waiting for event to be received by listener2") + } + // Verify that listener1 did not receive the event + select { + case <-listener1.receivedEvents: + t.Fatal("Listener1 should not have received any events") + case <-time.After(500 * time.Millisecond): + // No event received, as expected + } + + // send a custom event from listener2 to listener1 + customEvent2 := &events.CustomEvent{ + SourcePlugin: "pluginB", + Recipients: []string{"pluginA"}, + Payload: map[string]interface{}{ + "message": "Hello from pluginB", + }, + } + em.EmitToSubscribersAnd([]ListenerID{listener1ID}, customEvent2) + + // Verify that listener1 received the event + select { + case receivedEvent := <-listener1.receivedEvents: + if receivedEvent.Name != events.EventCustom { + t.Fatalf("Unexpected event received by listener1.\nGot: %+v\nWant: %+v", receivedEvent, customEvent2) + } + case <-time.After(1 * time.Second): + t.Fatal("Timeout waiting for event to be received by listener1") + } + // Verify that listener2 did not receive any new event + select { + case <-listener2.receivedEvents: + t.Fatal("Listener2 should not have received any new events") + case <-time.After(500 * time.Millisecond): + // No event received, as expected + } + + // ensure the moderator received both events + expectedMessagesSeen := map[string]bool{ + "Hello from pluginA": false, + "Hello from pluginB": false, + } + for i := 0; i < 2; i++ { + select { + case receivedEvent := <-moderator.receivedEvents: + if receivedEvent.Name != events.EventCustom { + t.Fatalf("Unexpected event received by moderator.\nGot: %+v\nWant: %+v", receivedEvent, customEvent) + } + data, ok := receivedEvent.Data.(*events.CustomEvent) + if !ok { + t.Fatalf("Unexpected data type in event received by moderator.\nGot: %T\nWant: *events.CustomEvent", receivedEvent.Data) + } + message, ok := data.Payload["message"].(string) + if !ok { + t.Fatalf("Unexpected payload format in event received by moderator.\nGot: %+v\nWant: map with 'message' key", data.Payload) + } + if alreadySeen, exists := expectedMessagesSeen[message]; exists { + if alreadySeen { + t.Fatalf("Duplicate message in event received by moderator: %s", message) + } + expectedMessagesSeen[message] = true + } else { + t.Fatalf("Unexpected message in event received by moderator: %s", message) + } + case <-time.After(1 * time.Second): + t.Fatal("Timeout waiting for event to be received by moderator") + } + } + for msg, seen := range expectedMessagesSeen { + if !seen { + t.Fatalf("Moderator did not receive expected message: %s", msg) + } + } + + // Ensure that the events were not seen by the other listener + select { + case <-otherListener.receivedEvents: + t.Fatal("otherListener should not have received any events") + case <-time.After(500 * time.Millisecond): + // No event received, as expected + } +}