Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add unit tests for DataEventBroadcaster and DataEventListener #1

Merged
merged 5 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Test & Coverage
on:
pull_request:
branches:
- '*'

jobs:
test-and-coverage:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'

- name: Set up the Maven dependencies caching
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2

- name: Run tests
run: mvn --batch-mode --update-snapshots verify -Dgpg.skip=true

- name: Add coverage
uses: madrapps/[email protected]
with:
paths: |
${{ github.workspace }}/**/target/site/jacoco/jacoco.xml
token: ${{ secrets.GITHUB_TOKEN }}
title: '### :zap: Coverage report'
update-comment: true
min-coverage-overall: 80
min-coverage-changed-files: 60
continue-on-error: false
33 changes: 32 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

<properties>
<java.version>21</java.version>
<mockito.version>4.11.0</mockito.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -58,6 +59,18 @@
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down Expand Up @@ -122,9 +135,27 @@
<failOnError>false</failOnError>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<scm>
<connection>scm:git:https://github.com/sipios/spring-data-event.git</connection>
<developerConnection>scm:git:[email protected]:sipios/spring-data-event.git</developerConnection>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.sipios.spring.data.event.broadcaster;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.CallbackException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.kafka.core.KafkaTemplate;

public class DataEventBroadcasterTest {
private DataEventBroadcaster broadcaster;
private KafkaTemplate<String, String> kafkaTemplate;
private ObjectMapper objectMapper;

@BeforeEach
void beforeEach() {
kafkaTemplate = mock(KafkaTemplate.class);
objectMapper = new ObjectMapper();
broadcaster = new DataEventBroadcaster(kafkaTemplate, objectMapper);
}

@ParameterizedTest
@CsvSource({
"testEntity.created, testEntity.created",
"'', testentity.created"
})
void testBroadcastEntityCreated(String topicLabel, String expectedTopic) throws Exception {
TestEntity entity = new TestEntity(1, "Test Name", true);
String expectedJson = objectMapper.writeValueAsString(entity);

broadcaster.broadcastEntityCreated(entity, topicLabel);

verify(kafkaTemplate).send(eq(expectedTopic), eq(expectedJson));
}

@ParameterizedTest
@CsvSource({
"testEntity.updated, testEntity.updated",
"'', testentity.updated"
})
void testBroadcastEntityUpdated(String topicLabel, String expectedTopic) throws Exception {
TestEntity entity = new TestEntity(1, "Test Name", false);
String expectedJson = objectMapper.writeValueAsString(entity);

broadcaster.broadcastEntityUpdated(entity, topicLabel);

verify(kafkaTemplate).send(eq(expectedTopic), eq(expectedJson));
}

@ParameterizedTest
@CsvSource({
"testEntity.deleted, testEntity.deleted",
"'', testentity.deleted"
})
void testBroadcastEntityDeleted(String topicLabel, String expectedTopic) throws Exception {
TestEntity entity = new TestEntity(1, "Test Name", true);
String expectedJson = objectMapper.writeValueAsString(entity);

broadcaster.broadcastEntityDeleted(entity, topicLabel);

verify(kafkaTemplate).send(eq(expectedTopic), eq(expectedJson));
}

@Test
void testBroadcastEntityCreatedJsonProcessingException() throws Exception {
objectMapper = mock(ObjectMapper.class);
broadcaster = new DataEventBroadcaster(kafkaTemplate, objectMapper);
TestEntity entity = new TestEntity(1, "Test Name", true);

when(objectMapper.writeValueAsString(entity)).thenThrow(new JsonProcessingException("JSON processing error") {});

assertThrows(CallbackException.class, () -> broadcaster.broadcastEntityCreated(entity, "testEntity.created"));
}

@Test
void testBroadcastEntityUpdatedJsonProcessingException() throws Exception {
objectMapper = mock(ObjectMapper.class);
broadcaster = new DataEventBroadcaster(kafkaTemplate, objectMapper);
TestEntity entity = new TestEntity(1, "Test Name", false);

when(objectMapper.writeValueAsString(entity)).thenThrow(new JsonProcessingException("JSON processing error") {});

assertThrows(CallbackException.class, () -> broadcaster.broadcastEntityUpdated(entity, "testEntity.updated"));
}

@Test
void testBroadcastEntityDeletedJsonProcessingException() throws Exception {
objectMapper = mock(ObjectMapper.class);
broadcaster = new DataEventBroadcaster(kafkaTemplate, objectMapper);
TestEntity entity = new TestEntity(1, "Test Name", true);

when(objectMapper.writeValueAsString(entity)).thenThrow(new JsonProcessingException("JSON processing error") {});

assertThrows(CallbackException.class, () -> broadcaster.broadcastEntityDeleted(entity, "testEntity.deleted"));
}

@Getter
@Setter
@AllArgsConstructor
public static class TestEntity {
private int id;
private String name;
private boolean active;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package com.sipios.spring.data.event.listener;

import com.sipios.spring.data.event.annotation.DataEventEntity;
import com.sipios.spring.data.event.broadcaster.DataEventBroadcaster;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.event.spi.PostDeleteEvent;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostUpdateEvent;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class DataEventListenerTest {
@Mock
private DataEventBroadcaster dataEventBroadcaster;

@InjectMocks
private DataEventListener listener;

@Nested
class AnnotatedEntityTests {

@Test
public void testOnPostInsert() {
PostInsertEvent event = mock(PostInsertEvent.class);
TestEntity entity = new TestEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);

listener.onPostInsert(event);

verify(dataEventBroadcaster).broadcastEntityCreated(entity, "testEntity.created");
}

@Test
public void testOnPostUpdate() {
PostUpdateEvent event = mock(PostUpdateEvent.class);
TestEntity entity = new TestEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);

listener.onPostUpdate(event);

verify(dataEventBroadcaster).broadcastEntityUpdated(entity, "testEntity.updated");
}

@Test
public void testOnPostDelete() {
PostDeleteEvent event = mock(PostDeleteEvent.class);
TestEntity entity = new TestEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);

listener.onPostDelete(event);

verify(dataEventBroadcaster).broadcastEntityDeleted(entity, "testEntity.deleted");
}

@Test
public void testOnPostInsertJsonProcessingException() throws Exception {
PostInsertEvent event = mock(PostInsertEvent.class);
TestEntity entity = new TestEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);
doThrow(new RuntimeException("JSON processing error")).when(dataEventBroadcaster).broadcastEntityCreated(any(), any());

assertThrows(RuntimeException.class, () -> listener.onPostInsert(event));
}

@Test
public void testOnPostUpdateJsonProcessingException() throws Exception {
PostUpdateEvent event = mock(PostUpdateEvent.class);
TestEntity entity = new TestEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);
doThrow(new RuntimeException("JSON processing error")).when(dataEventBroadcaster).broadcastEntityUpdated(any(), any());

assertThrows(RuntimeException.class, () -> listener.onPostUpdate(event));
}

@Test
public void testOnPostDeleteJsonProcessingException() throws Exception {
PostDeleteEvent event = mock(PostDeleteEvent.class);
TestEntity entity = new TestEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);
doThrow(new RuntimeException("JSON processing error")).when(dataEventBroadcaster).broadcastEntityDeleted(any(), any());

assertThrows(RuntimeException.class, () -> listener.onPostDelete(event));
}

@DataEventEntity(creationTopic = "testEntity.created", updateTopic = "testEntity.updated", deletionTopic = "testEntity.deleted")
@Getter
@Setter
@AllArgsConstructor
private static class TestEntity {
private int id;
private String name;
private boolean active;
}
}

@Nested
class NonAnnotatedEntityTests {

@Test
public void testOnPostInsert() {
PostInsertEvent event = mock(PostInsertEvent.class);
NonAnnotatedEntity entity = new NonAnnotatedEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);

listener.onPostInsert(event);

verify(dataEventBroadcaster, never()).broadcastEntityCreated(any(), any());
}

@Test
public void testOnPostUpdate() {
PostUpdateEvent event = mock(PostUpdateEvent.class);
NonAnnotatedEntity entity = new NonAnnotatedEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);

listener.onPostUpdate(event);

verify(dataEventBroadcaster, never()).broadcastEntityUpdated(any(), any());
}

@Test
public void testOnPostDelete() {
PostDeleteEvent event = mock(PostDeleteEvent.class);
NonAnnotatedEntity entity = new NonAnnotatedEntity(1, "Test Name", true);
when(event.getEntity()).thenReturn(entity);

listener.onPostDelete(event);

verify(dataEventBroadcaster, never()).broadcastEntityDeleted(any(), any());
}

@Getter
@Setter
@AllArgsConstructor
private static class NonAnnotatedEntity {
private int id;
private String name;
private boolean active;
}
}
}
Loading