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