Skip to content

Commit c75dc22

Browse files
committed
Adding a first unit test for Metrics with logging
1 parent fa9617e commit c75dc22

4 files changed

Lines changed: 124 additions & 0 deletions

File tree

application/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ dependencies {
8080
implementation 'org.apache.commons:commons-text:1.15.0'
8181
implementation 'com.apptasticsoftware:rssreader:3.12.0'
8282

83+
testImplementation 'org.assertj:assertj-core:3.27.7'
8384
testImplementation 'org.mockito:mockito-core:5.23.0'
8485
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
8586
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"

application/src/main/java/org/togetherjava/tjbot/features/analytics/Metrics.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ private void processEvent(String event, Instant happenedAt) {
5353
.insert());
5454
}
5555

56+
public ExecutorService getExecutorService() {
57+
return service;
58+
}
5659
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package org.togetherjava.tjbot.features;
2+
3+
import org.junit.jupiter.api.AfterEach;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import org.togetherjava.tjbot.db.Database;
10+
import org.togetherjava.tjbot.db.generated.tables.MetricEvents;
11+
import org.togetherjava.tjbot.features.analytics.Metrics;
12+
13+
import java.time.Duration;
14+
import java.time.Instant;
15+
import java.time.temporal.ChronoUnit;
16+
import java.util.List;
17+
import java.util.concurrent.locks.LockSupport;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.within;
21+
import static org.junit.jupiter.api.Assertions.fail;
22+
23+
final class MetricsTests {
24+
private static final Logger logger = LoggerFactory.getLogger(MetricsTests.class);
25+
26+
private static final Duration WAIT_TIMEOUT = Duration.ofSeconds(3);
27+
28+
private Database database;
29+
private Metrics metrics;
30+
31+
@BeforeEach
32+
void setUp() {
33+
database = Database.createMemoryDatabase(MetricEvents.METRIC_EVENTS);
34+
metrics = new Metrics(database);
35+
}
36+
37+
@AfterEach
38+
void tearDown() {
39+
metrics.getExecutorService().shutdownNow();
40+
}
41+
42+
@Test
43+
void countInsertsSingleEvent() {
44+
45+
final String slashPing = "slash-ping";
46+
47+
metrics.count(slashPing);
48+
49+
awaitRecords(1);
50+
51+
List<String> recordedEvents = readEventsOrderedById();
52+
53+
assertThat(recordedEvents).as("Metrics should persist the counted event in insertion order")
54+
.containsExactly(slashPing);
55+
56+
assertThat(readLatestEventHappenedAt())
57+
.as("Metrics should store a recent timestamp for event '%s' (recordedEvents=%s)",
58+
slashPing, recordedEvents)
59+
.isNotNull()
60+
.isCloseTo(Instant.now(), within(5, ChronoUnit.SECONDS));
61+
}
62+
63+
private void awaitRecords(int expectedAmount) {
64+
Instant deadline = Instant.now().plus(WAIT_TIMEOUT);
65+
66+
while (Instant.now().isBefore(deadline)) {
67+
if (readRecordCount() == expectedAmount) {
68+
return;
69+
}
70+
71+
LockSupport.parkNanos(Duration.ofMillis(25).toNanos());
72+
73+
if (Thread.interrupted()) {
74+
int actualCount = readRecordCount();
75+
76+
String msg = String.format(
77+
"Interrupted while waiting for metrics writes (expectedAmount=%d, actualCount=%d, timeout=%s, events=%s)",
78+
expectedAmount, actualCount, WAIT_TIMEOUT, readEventsOrderedById());
79+
80+
logger.warn(msg);
81+
82+
fail(msg);
83+
}
84+
}
85+
86+
int actualCount = readRecordCount();
87+
88+
List<String> recordedEvents = readEventsOrderedById();
89+
90+
String timeoutMessage = String.format(
91+
"Timed out waiting for metrics writes (expectedAmount=%d, actualCount=%d, timeout=%s, events=%s)",
92+
expectedAmount, actualCount, WAIT_TIMEOUT, recordedEvents);
93+
94+
logger.warn(timeoutMessage);
95+
96+
assertThat(actualCount).as(timeoutMessage).isEqualTo(expectedAmount);
97+
}
98+
99+
private int readRecordCount() {
100+
return database.read(context -> context.fetchCount(MetricEvents.METRIC_EVENTS));
101+
}
102+
103+
private List<String> readEventsOrderedById() {
104+
return database.read(context -> context.select(MetricEvents.METRIC_EVENTS.EVENT)
105+
.from(MetricEvents.METRIC_EVENTS)
106+
.orderBy(MetricEvents.METRIC_EVENTS.ID.asc())
107+
.fetch(MetricEvents.METRIC_EVENTS.EVENT));
108+
109+
}
110+
111+
private Instant readLatestEventHappenedAt() {
112+
return database.read(context -> context.select(MetricEvents.METRIC_EVENTS.HAPPENED_AT)
113+
.from(MetricEvents.METRIC_EVENTS)
114+
.orderBy(MetricEvents.METRIC_EVENTS.ID.desc())
115+
.limit(1)
116+
.fetchOne(MetricEvents.METRIC_EVENTS.HAPPENED_AT));
117+
}
118+
}

application/src/test/resources/log4j2.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
</RollingFile>
1414
</Appenders>
1515
<Loggers>
16+
<Logger name="org.togetherjava.tjbot" level="debug"/>
17+
1618
<Root level="info">
1719
<AppenderRef ref="Console"/>
1820
<AppenderRef ref="File"/>

0 commit comments

Comments
 (0)