From df434ce7c07764f97fbaabc6a68a86b6fc5f3d36 Mon Sep 17 00:00:00 2001 From: Stephan Schnabel Date: Sat, 4 Mar 2023 21:49:10 +0100 Subject: [PATCH] Add tests using Mockito. --- pom.xml | 11 ++ .../kokuwa/keycloak/metrics/KeycloakIT.java | 5 + .../metrics/MicrometerEventListenerTest.java | 173 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java diff --git a/pom.xml b/pom.xml index 43ea01f..f6a68b9 100644 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,7 @@ 21.0.1 + 5.1.1 1.17.6 @@ -116,6 +117,11 @@ + + org.mockito + mockito-junit-jupiter + ${version.org.mockito} + org.testcontainers testcontainers-bom @@ -162,6 +168,11 @@ + + org.mockito + mockito-junit-jupiter + test + org.testcontainers junit-jupiter diff --git a/src/test/java/io/kokuwa/keycloak/metrics/KeycloakIT.java b/src/test/java/io/kokuwa/keycloak/metrics/KeycloakIT.java index c3115fe..67d545c 100644 --- a/src/test/java/io/kokuwa/keycloak/metrics/KeycloakIT.java +++ b/src/test/java/io/kokuwa/keycloak/metrics/KeycloakIT.java @@ -16,6 +16,11 @@ import io.kokuwa.keycloak.metrics.junit.KeycloakClient; import io.kokuwa.keycloak.metrics.junit.KeycloakExtension; import io.kokuwa.keycloak.metrics.prometheus.Prometheus; +/** + * Integration tests with Keycloak. + * + * @author Stephan Schnabel + */ @ExtendWith(KeycloakExtension.class) public class KeycloakIT { diff --git a/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java b/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java new file mode 100644 index 0000000..29151d2 --- /dev/null +++ b/src/test/java/io/kokuwa/keycloak/metrics/MicrometerEventListenerTest.java @@ -0,0 +1,173 @@ +package io.kokuwa.keycloak.metrics; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.keycloak.events.Event; +import org.keycloak.events.EventType; +import org.keycloak.events.admin.AdminEvent; +import org.keycloak.events.admin.OperationType; +import org.keycloak.events.admin.ResourceType; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; + +/** + * Test for {@link MicrometerEventListener} with Mockito. + * + * @author Stephan Schnabel + */ +@ExtendWith(MockitoExtension.class) +public class MicrometerEventListenerTest { + + @InjectMocks + MicrometerEventListener listener; + @Mock + MeterRegistry registry; + @Mock + Counter counter; + @Captor + ArgumentCaptor metricCaptor; + @Captor + ArgumentCaptor tagsCaptor; + + @BeforeEach + void setup() { + when(registry.counter(metricCaptor.capture(), tagsCaptor.capture())).thenReturn(counter); + } + + @DisplayName("onEvent(Event)") + @Nested + class onEvent { + + @DisplayName("without error") + @Test + void withoutError() { + + var realmId = UUID.randomUUID().toString(); + var clientId = UUID.randomUUID().toString(); + var type = EventType.LOGIN; + + listener.onEvent(toEvent(realmId, clientId, type, null)); + assertEvent(realmId, clientId, type.toString(), ""); + } + + @DisplayName("with error") + @Test + void withError() { + + var realmId = UUID.randomUUID().toString(); + var clientId = UUID.randomUUID().toString(); + var type = EventType.LOGIN_ERROR; + var error = UUID.randomUUID().toString(); + + listener.onEvent(toEvent(realmId, clientId, type, error)); + assertEvent(realmId, clientId, type.toString(), error); + } + + @DisplayName("all fields empty") + @Test + void fieldsEmpty() { + listener.onEvent(toEvent(null, null, null, null)); + assertEvent("", "", "", ""); + } + + private Event toEvent(String realmId, String clientId, EventType type, String error) { + var event = new Event(); + event.setRealmId(realmId); + event.setClientId(clientId); + event.setType(type); + event.setError(error); + return event; + } + + private void assertEvent(String realm, String client, String type, String error) { + assertCounter("keycloak_event_user", Map.of( + "realm", realm, + "client", client, + "type", type, + "error", error)); + } + } + + @DisplayName("onEvent(AdminEvent,boolean)") + @Nested + class onAdminEvent { + + @DisplayName("without error") + @Test + void withoutError() { + + var realmId = UUID.randomUUID().toString(); + var resource = ResourceType.USER; + var operation = OperationType.CREATE; + + listener.onEvent(toAdminEvent(realmId, resource, operation, null), false); + assertAdminEvent(realmId, resource.toString(), operation.toString(), ""); + } + + @DisplayName("with error") + @Test + void withError() { + + var realmId = UUID.randomUUID().toString(); + var resource = ResourceType.USER; + var operation = OperationType.CREATE; + var error = UUID.randomUUID().toString(); + + listener.onEvent(toAdminEvent(realmId, resource, operation, error), false); + assertAdminEvent(realmId, resource.toString(), operation.toString(), error); + } + + @DisplayName("all fields empty") + @Test + void fieldsEmpty() { + listener.onEvent(toAdminEvent(null, null, null, null), false); + assertAdminEvent("", "", "", ""); + } + + private AdminEvent toAdminEvent(String realmId, ResourceType resource, OperationType operation, String error) { + var event = new AdminEvent(); + event.setRealmId(realmId); + event.setResourceType(resource); + event.setOperationType(operation); + event.setError(error); + return event; + } + + private void assertAdminEvent(String realm, String resource, String operation, String error) { + assertCounter("keycloak_event_admin", Map.of( + "realm", realm, + "resource", resource, + "operation", operation, + "error", error)); + } + } + + private void assertCounter(String metric, Map tags) { + verify(registry).counter(anyString(), any(String[].class)); + verify(counter).increment(); + assertEquals(metric, metricCaptor.getValue(), "metric"); + assertEquals(tags, IntStream + .range(0, tagsCaptor.getValue().length / 2).mapToObj(i -> i * 2) + .collect(Collectors.toMap(i -> tagsCaptor.getValue()[i], i -> tagsCaptor.getValue()[i + 1])), "tags"); + } +}