Skip to content

Commit 199067f

Browse files
committed
feat: Add test cases
Resolves #2671
1 parent 3e1e516 commit 199067f

File tree

7 files changed

+423
-0
lines changed

7 files changed

+423
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.iluwatar.transactionaloutbox;
2+
3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
8+
import static org.mockito.Mockito.mock;
9+
import org.mockito.junit.jupiter.MockitoExtension;
10+
11+
import jakarta.persistence.EntityManager;
12+
import jakarta.persistence.EntityManagerFactory;
13+
14+
@ExtendWith(MockitoExtension.class)
15+
class AppTest {
16+
17+
@Test
18+
void testMainExecution() {
19+
assertDoesNotThrow(() -> App.main(new String[] {}));
20+
}
21+
22+
@Test
23+
void testSimulateCustomerCreation() {
24+
var entityManagerFactory = mock(EntityManagerFactory.class);
25+
var entityManager = mock(EntityManager.class);
26+
var customerService = new CustomerService(entityManager);
27+
var messageBroker = new MessageBroker();
28+
var eventPoller = new EventPoller(entityManager, messageBroker);
29+
30+
assertNotNull(entityManagerFactory);
31+
assertNotNull(entityManager);
32+
assertNotNull(customerService);
33+
assertNotNull(messageBroker);
34+
assertNotNull(eventPoller);
35+
}
36+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.iluwatar.transactionaloutbox;
2+
3+
import static org.junit.jupiter.api.Assertions.assertThrows;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.Mockito.doThrow;
6+
import static org.mockito.Mockito.never;
7+
import static org.mockito.Mockito.times;
8+
import static org.mockito.Mockito.verify;
9+
import static org.mockito.Mockito.when;
10+
11+
import jakarta.persistence.EntityManager;
12+
import jakarta.persistence.EntityTransaction;
13+
import org.junit.jupiter.api.BeforeEach;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import org.mockito.Mock;
17+
import org.mockito.junit.jupiter.MockitoExtension;
18+
19+
/** Tests for {@link CustomerService}. */
20+
@ExtendWith(MockitoExtension.class)
21+
class CustomerServiceTests {
22+
23+
@Mock private EntityManager entityManager;
24+
25+
@Mock private EntityTransaction transaction;
26+
27+
private CustomerService customerService;
28+
29+
@BeforeEach
30+
void setup() {
31+
when(entityManager.getTransaction()).thenReturn(transaction);
32+
customerService = new CustomerService(entityManager);
33+
}
34+
35+
@Test
36+
void shouldCreateCustomerAndOutboxEventInSameTransaction() throws Exception {
37+
var username = "testUser";
38+
39+
customerService.createCustomer(username);
40+
41+
verify(transaction).begin();
42+
verify(entityManager, times(2)).persist(any());
43+
verify(transaction).commit();
44+
verify(transaction, never()).rollback();
45+
}
46+
47+
@Test
48+
void shouldRollbackTransactionOnFailure() {
49+
50+
var username = "testUser";
51+
doThrow(new RuntimeException("Test failure")).when(entityManager).persist(any(Customer.class));
52+
53+
assertThrows(Exception.class, () -> customerService.createCustomer(username));
54+
verify(transaction).begin();
55+
verify(transaction).rollback();
56+
verify(transaction, never()).commit();
57+
}
58+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package com.iluwatar.transactionaloutbox;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
import static org.mockito.ArgumentMatchers.any;
7+
import static org.mockito.ArgumentMatchers.anyString;
8+
import static org.mockito.ArgumentMatchers.eq;
9+
import static org.mockito.Mockito.doThrow;
10+
import static org.mockito.Mockito.never;
11+
import static org.mockito.Mockito.times;
12+
import static org.mockito.Mockito.verify;
13+
import static org.mockito.Mockito.when;
14+
15+
import jakarta.persistence.EntityManager;
16+
import jakarta.persistence.EntityTransaction;
17+
import jakarta.persistence.LockModeType;
18+
import jakarta.persistence.TypedQuery;
19+
import java.util.Collections;
20+
import java.util.concurrent.TimeUnit;
21+
import org.junit.jupiter.api.BeforeEach;
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.extension.ExtendWith;
24+
import org.mockito.Mock;
25+
import org.mockito.junit.jupiter.MockitoExtension;
26+
27+
/** Tests for {@link EventPoller}. */
28+
@ExtendWith(MockitoExtension.class)
29+
class EventPollerTests {
30+
31+
private static final String TEST_EVENT = "TEST_EVENT";
32+
private static final String TEST_PAYLOAD = "payload";
33+
34+
@Mock private EntityManager entityManager;
35+
@Mock private EntityTransaction transaction;
36+
@Mock private MessageBroker messageBroker;
37+
@Mock private TypedQuery<OutboxEvent> query;
38+
private EventPoller eventPoller;
39+
40+
@BeforeEach
41+
void setup() {
42+
when(entityManager.getTransaction()).thenReturn(transaction);
43+
when(entityManager.createQuery(anyString(), eq(OutboxEvent.class))).thenReturn(query);
44+
when(query.setMaxResults(any(Integer.class))).thenReturn(query);
45+
when(query.setLockMode(any(LockModeType.class))).thenReturn(query);
46+
eventPoller = new EventPoller(entityManager, messageBroker);
47+
}
48+
49+
@Test
50+
void shouldProcessEventsSuccessfully() {
51+
var event = new OutboxEvent("EVENT_1", "payload1");
52+
when(query.getResultList()).thenReturn(Collections.singletonList(event));
53+
54+
eventPoller.processOutboxEvents();
55+
56+
verify(messageBroker).sendMessage(event);
57+
verify(transaction).begin();
58+
verify(transaction).commit();
59+
assertEquals(1, eventPoller.getProcessedEventsCount());
60+
}
61+
62+
@Test
63+
void shouldHandleFailureAndRetry() {
64+
var event = new OutboxEvent(TEST_EVENT, TEST_PAYLOAD);
65+
when(query.getResultList()).thenReturn(Collections.singletonList(event));
66+
doThrow(new RuntimeException("First attempt"))
67+
.doNothing()
68+
.when(messageBroker)
69+
.sendMessage(any(OutboxEvent.class));
70+
71+
eventPoller.processOutboxEvents();
72+
73+
verify(messageBroker, times(2)).sendMessage(event);
74+
assertEquals(1, eventPoller.getProcessedEventsCount());
75+
assertEquals(0, eventPoller.getFailedEventsCount());
76+
}
77+
78+
@Test
79+
void shouldHandleInterruptedThread() {
80+
var event = new OutboxEvent(TEST_EVENT, TEST_PAYLOAD);
81+
when(query.getResultList()).thenReturn(Collections.singletonList(event));
82+
doThrow(new RuntimeException("Processing fails"))
83+
.when(messageBroker)
84+
.sendMessage(any(OutboxEvent.class));
85+
Thread.currentThread().interrupt();
86+
87+
eventPoller.processOutboxEvents();
88+
89+
verify(transaction).begin();
90+
verify(transaction).commit();
91+
verify(messageBroker).sendMessage(event);
92+
assertEquals(0, eventPoller.getProcessedEventsCount());
93+
assertEquals(
94+
0, eventPoller.getFailedEventsCount(), "Interrupted events are not counted as failures");
95+
assertTrue(Thread.interrupted(), "Interrupt flag should be preserved");
96+
}
97+
98+
@Test
99+
void shouldHandleEmptyEventList() {
100+
when(query.getResultList()).thenReturn(Collections.emptyList());
101+
102+
eventPoller.processOutboxEvents();
103+
104+
verify(transaction).begin();
105+
verify(transaction).commit();
106+
assertEquals(0, eventPoller.getProcessedEventsCount());
107+
assertEquals(0, eventPoller.getFailedEventsCount());
108+
}
109+
110+
@Test
111+
void shouldHandleMaxRetryAttempts() {
112+
var event = new OutboxEvent(TEST_EVENT, TEST_PAYLOAD);
113+
when(query.getResultList()).thenReturn(Collections.singletonList(event));
114+
doThrow(new RuntimeException("Failed processing"))
115+
.when(messageBroker)
116+
.sendMessage(any(OutboxEvent.class));
117+
118+
eventPoller.processOutboxEvents();
119+
120+
verify(messageBroker, times(3)).sendMessage(event);
121+
assertEquals(0, eventPoller.getProcessedEventsCount());
122+
assertEquals(1, eventPoller.getFailedEventsCount());
123+
}
124+
125+
@Test
126+
void shouldProcessMultipleEvents() {
127+
var event1 = new OutboxEvent("EVENT_1", "payload1");
128+
var event2 = new OutboxEvent("EVENT_2", "payload2");
129+
when(query.getResultList()).thenReturn(java.util.Arrays.asList(event1, event2));
130+
131+
eventPoller.processOutboxEvents();
132+
133+
verify(messageBroker).sendMessage(event1);
134+
verify(messageBroker).sendMessage(event2);
135+
verify(transaction).begin();
136+
verify(transaction).commit();
137+
assertEquals(2, eventPoller.getProcessedEventsCount());
138+
assertEquals(0, eventPoller.getFailedEventsCount());
139+
}
140+
141+
@Test
142+
void shouldRollbackTransactionWhenRepositoryFails() {
143+
var dbException = new RuntimeException("Database connection failed");
144+
when(query.getResultList()).thenThrow(dbException);
145+
146+
eventPoller.processOutboxEvents();
147+
148+
verify(transaction).begin();
149+
verify(transaction).rollback();
150+
verify(transaction, never()).commit();
151+
152+
assertEquals(1, eventPoller.getFailedEventsCount());
153+
assertEquals(0, eventPoller.getProcessedEventsCount());
154+
}
155+
156+
@Test
157+
void shouldStartAndStopCorrectly() throws Exception {
158+
eventPoller.start();
159+
160+
assertFalse(eventPoller.getScheduler().isShutdown());
161+
assertFalse(eventPoller.getScheduler().isTerminated());
162+
163+
eventPoller.stop();
164+
165+
assertTrue(eventPoller.getScheduler().isShutdown());
166+
assertTrue(eventPoller.getScheduler().awaitTermination(1, TimeUnit.SECONDS));
167+
assertTrue(eventPoller.getScheduler().isTerminated());
168+
}
169+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.iluwatar.transactionaloutbox;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
class MessageBrokerTests {
9+
private final MessageBroker messageBroker = new MessageBroker();
10+
11+
@Test
12+
void shouldSendMessageSuccessfully() {
13+
var event = new OutboxEvent("TEST_EVENT", "test_payload");
14+
messageBroker.sendMessage(event);
15+
assertFalse(Thread.interrupted(), "Thread should not be interrupted");
16+
}
17+
18+
@Test
19+
void shouldHandleInterruptedException() {
20+
var event = new OutboxEvent("TEST_EVENT", "test_payload");
21+
Thread.currentThread().interrupt();
22+
23+
messageBroker.sendMessage(event);
24+
25+
assertTrue(Thread.interrupted(), "Thread interrupt flag should be preserved");
26+
}
27+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.iluwatar.transactionaloutbox;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNotNull;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
/** Tests for {@link OutboxEvent}. */
11+
class OutboxEventTests {
12+
13+
@Test
14+
void newOutboxEventShouldBeUnprocessed() {
15+
var eventType = "CUSTOMER_CREATED";
16+
var payload = "{\"customerId\":1}";
17+
18+
var event = new OutboxEvent(eventType, payload);
19+
20+
assertNotNull(event);
21+
assertEquals(eventType, event.getEventType());
22+
assertEquals(payload, event.getPayload());
23+
assertFalse(event.isProcessed());
24+
assertNotNull(event.toString(), "toString should include createdAt value");
25+
}
26+
27+
@Test
28+
void processedEventShouldBeMarkedAsProcessed() {
29+
var event = new OutboxEvent("TEST_EVENT", "payload");
30+
event.setId(1);
31+
event.setProcessed(true);
32+
33+
assertTrue(event.isProcessed());
34+
assertEquals(Integer.valueOf(1), event.getId());
35+
}
36+
37+
@Test
38+
void eventsShouldMaintainSequentialOrder() {
39+
var event1 = new OutboxEvent("EVENT_1", "payload1");
40+
var event2 = new OutboxEvent("EVENT_2", "payload2");
41+
42+
event1.setSequenceNumber(1L);
43+
event2.setSequenceNumber(2L);
44+
45+
assertEquals(Long.valueOf(1L), event1.getSequenceNumber());
46+
assertEquals(Long.valueOf(2L), event2.getSequenceNumber());
47+
assertTrue(event1.getSequenceNumber() < event2.getSequenceNumber());
48+
}
49+
50+
@Test
51+
void shouldFormatToStringWithAllFields() {
52+
var event = new OutboxEvent("TEST_EVENT", "payload");
53+
event.setId(123);
54+
event.setSequenceNumber(456L);
55+
event.setProcessed(true);
56+
57+
var toString = event.toString();
58+
59+
assertTrue(toString.contains("id=123"), "toString should contain id");
60+
assertTrue(toString.contains("eventType='TEST_EVENT'"), "toString should contain eventType");
61+
assertTrue(toString.contains("payload='payload'"), "toString should contain payload");
62+
assertTrue(toString.contains("processed=true"), "toString should contain processed status");
63+
assertTrue(toString.contains("createdAt="), "toString should contain createdAt timestamp");
64+
}
65+
66+
@Test
67+
void defaultConstructorShouldInitializeBasicFields() {
68+
var event = new OutboxEvent();
69+
70+
assertNotNull(event.toString(), "toString should include createdAt value");
71+
assertFalse(event.isProcessed(), "Should not be processed by default");
72+
}
73+
}

0 commit comments

Comments
 (0)