Skip to content

Commit e27d2b9

Browse files
committed
Add test suite and ci pipeline
1 parent ef187a9 commit e27d2b9

44 files changed

Lines changed: 2669 additions & 20 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-lint.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: CI Lint
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- synchronize
8+
- reopened
9+
push:
10+
branches:
11+
- main
12+
13+
jobs:
14+
lint:
15+
name: Checkstyle
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v6
21+
22+
- name: Set up JDK 21
23+
uses: actions/setup-java@v5
24+
with:
25+
distribution: temurin
26+
java-version: "21"
27+
cache: maven
28+
29+
- name: Run Checkstyle
30+
run: mvn -B -ntp -DskipTests checkstyle:check
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI Tests and Coverage
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- synchronize
8+
- reopened
9+
push:
10+
branches:
11+
- main
12+
13+
jobs:
14+
tests:
15+
name: Tests and Coverage
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v6
21+
22+
- name: Set up JDK 21
23+
uses: actions/setup-java@v5
24+
with:
25+
distribution: temurin
26+
java-version: "21"
27+
cache: maven
28+
29+
- name: Run Maven Verify
30+
run: mvn -B -ntp verify
31+
32+
- name: Upload JaCoCo Report
33+
if: always()
34+
uses: actions/upload-artifact@v6
35+
with:
36+
name: jacoco-report
37+
path: target/site/jacoco

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ bus.ifPresent(redisBus -> {
4141
- [Usage guide](docs/USAGE_GUIDE.md)
4242
- [Best practices](docs/BEST_PRACTICES.md)
4343
- [Architecture and maintainability review](docs/ARCHITECTURE_REVIEW.md)
44+
- [Testing and CI](docs/TESTING_AND_CI.md)
4445
- [Examples](docs/examples)
4546

4647
## Notes

docs/TESTING_AND_CI.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Testing and CI
2+
3+
This project uses Maven for unit tests, linting, and coverage reporting.
4+
5+
## Local Commands
6+
7+
- Run unit tests:
8+
- `mvn -B -ntp test`
9+
- Run full verification including JaCoCo report:
10+
- `mvn -B -ntp verify`
11+
- Run Checkstyle:
12+
- `mvn -B -ntp -DskipTests checkstyle:check`
13+
14+
JaCoCo HTML output is generated at:
15+
- `target/site/jacoco/index.html`
16+
17+
## GitHub Actions Pipeline
18+
19+
Two workflows are configured under `.github/workflows`:
20+
21+
- `ci-lint.yml`
22+
- Triggers on pushes to `main` and pull requests.
23+
- Runs Checkstyle (`mvn -B -ntp -DskipTests checkstyle:check`).
24+
25+
- `ci-tests-and-coverage.yml`
26+
- Triggers on pushes to `main` and pull requests.
27+
- Runs `mvn -B -ntp verify`.
28+
- Uploads the JaCoCo report artifact from `target/site/jacoco`.
29+
30+
## Test Scope
31+
32+
The unit suite focuses on:
33+
- API contracts and default interface behavior.
34+
- Config parsing and defaults injection.
35+
- Registry lifecycle/reference-counting behavior.
36+
- SQL data access and schema builder behavior through mocked JDBC interfaces.
37+
- Command logic and logger adapters.
38+
39+
Some classes are integration-heavy (platform bootstrap and concrete Redis/Mongo runtime clients) and are harder to unit test without runtime dependencies. The suite still validates their guard/validation branches where possible, and all classes remain included in JaCoCo reporting.

pom.xml

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
<paper.version>1.21.11-R0.1-SNAPSHOT</paper.version>
1717
<velocity.version>3.5.0-SNAPSHOT</velocity.version>
1818
<junit.jupiter.version>5.10.2</junit.jupiter.version>
19+
<mockito.version>5.14.1</mockito.version>
1920
<maven.surefire.version>3.2.5</maven.surefire.version>
21+
<jacoco.version>0.8.12</jacoco.version>
22+
<checkstyle.version>9.3</checkstyle.version>
23+
<maven.enforcer.version>3.5.0</maven.enforcer.version>
2024
</properties>
2125

2226
<distributionManagement>
@@ -29,6 +33,30 @@
2933
<build>
3034
<finalName>${project.name}</finalName>
3135
<plugins>
36+
<plugin>
37+
<groupId>org.apache.maven.plugins</groupId>
38+
<artifactId>maven-enforcer-plugin</artifactId>
39+
<version>${maven.enforcer.version}</version>
40+
<executions>
41+
<execution>
42+
<id>enforce-build-hygiene</id>
43+
<goals>
44+
<goal>enforce</goal>
45+
</goals>
46+
<configuration>
47+
<rules>
48+
<requireMavenVersion>
49+
<version>[3.8.1,)</version>
50+
</requireMavenVersion>
51+
<requireJavaVersion>
52+
<version>[21,)</version>
53+
</requireJavaVersion>
54+
<requirePluginVersions/>
55+
</rules>
56+
</configuration>
57+
</execution>
58+
</executions>
59+
</plugin>
3260
<plugin>
3361
<groupId>org.apache.maven.plugins</groupId>
3462
<artifactId>maven-compiler-plugin</artifactId>
@@ -45,7 +73,7 @@
4573
<plugin>
4674
<groupId>org.apache.maven.plugins</groupId>
4775
<artifactId>maven-checkstyle-plugin</artifactId>
48-
<version>3.1.1</version>
76+
<version>3.3.0</version>
4977
<configuration>
5078
<configLocation>${basedir}/checkstyle.xml</configLocation>
5179
<includeTestSourceDirectory>true</includeTestSourceDirectory>
@@ -57,10 +85,30 @@
5785
<dependency>
5886
<groupId>com.puppycrawl.tools</groupId>
5987
<artifactId>checkstyle</artifactId>
60-
<version>8.38</version>
88+
<version>${checkstyle.version}</version>
6189
</dependency>
6290
</dependencies>
6391
</plugin>
92+
<plugin>
93+
<groupId>org.jacoco</groupId>
94+
<artifactId>jacoco-maven-plugin</artifactId>
95+
<version>${jacoco.version}</version>
96+
<executions>
97+
<execution>
98+
<id>jacoco-prepare-agent</id>
99+
<goals>
100+
<goal>prepare-agent</goal>
101+
</goals>
102+
</execution>
103+
<execution>
104+
<id>jacoco-report</id>
105+
<phase>verify</phase>
106+
<goals>
107+
<goal>report</goal>
108+
</goals>
109+
</execution>
110+
</executions>
111+
</plugin>
64112
<plugin>
65113
<groupId>org.apache.maven.plugins</groupId>
66114
<artifactId>maven-shade-plugin</artifactId>
@@ -81,6 +129,40 @@
81129
<filtering>true</filtering>
82130
</resource>
83131
</resources>
132+
<pluginManagement>
133+
<plugins>
134+
<plugin>
135+
<groupId>org.apache.maven.plugins</groupId>
136+
<artifactId>maven-clean-plugin</artifactId>
137+
<version>3.4.0</version>
138+
</plugin>
139+
<plugin>
140+
<groupId>org.apache.maven.plugins</groupId>
141+
<artifactId>maven-resources-plugin</artifactId>
142+
<version>3.3.1</version>
143+
</plugin>
144+
<plugin>
145+
<groupId>org.apache.maven.plugins</groupId>
146+
<artifactId>maven-jar-plugin</artifactId>
147+
<version>3.4.2</version>
148+
</plugin>
149+
<plugin>
150+
<groupId>org.apache.maven.plugins</groupId>
151+
<artifactId>maven-install-plugin</artifactId>
152+
<version>3.1.3</version>
153+
</plugin>
154+
<plugin>
155+
<groupId>org.apache.maven.plugins</groupId>
156+
<artifactId>maven-deploy-plugin</artifactId>
157+
<version>3.1.3</version>
158+
</plugin>
159+
<plugin>
160+
<groupId>org.apache.maven.plugins</groupId>
161+
<artifactId>maven-site-plugin</artifactId>
162+
<version>3.12.1</version>
163+
</plugin>
164+
</plugins>
165+
</pluginManagement>
84166
</build>
85167

86168
<repositories>
@@ -128,6 +210,12 @@
128210
<version>${junit.jupiter.version}</version>
129211
<scope>test</scope>
130212
</dependency>
213+
<dependency>
214+
<groupId>org.mockito</groupId>
215+
<artifactId>mockito-core</artifactId>
216+
<version>${mockito.version}</version>
217+
<scope>test</scope>
218+
</dependency>
131219
<!-- Hibernate Core -->
132220
<dependency>
133221
<groupId>org.hibernate.orm</groupId>

src/main/java/nl/hauntedmc/dataprovider/database/keyvalue/impl/redis/RedisDatabase.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ public void connect() {
4343
try {
4444
final String host = config.node("host").getString("localhost");
4545
final int port = config.node("port").getInt(6379);
46-
final String user = config.node("user").getString(null);
47-
final String password = config.node("password").getString(null);
46+
final String user = config.node("user").getString("");
47+
final String password = config.node("password").getString("");
4848
final int databaseIndex = config.node("database").getInt(0);
4949
final int poolSize = Math.max(1,
5050
config.node("pool_size").getInt(config.node("pool", "connections").getInt(8)));
@@ -67,8 +67,8 @@ public void connect() {
6767
poolConfig.setMaxTotal(poolSize);
6868

6969
DefaultJedisClientConfig.Builder clientConfigBuilder = DefaultJedisClientConfig.builder()
70-
.user((user == null || user.isBlank()) ? null : user)
71-
.password((password == null || password.isBlank()) ? null : password)
70+
.user(user.isBlank() ? null : user)
71+
.password(password.isBlank() ? null : password)
7272
.database(databaseIndex)
7373
.connectionTimeoutMillis(2000)
7474
.socketTimeoutMillis(2000)
@@ -101,7 +101,7 @@ public void connect() {
101101
host,
102102
port,
103103
databaseIndex,
104-
(password != null && !password.isBlank()) ? "enabled" : "disabled",
104+
!password.isBlank() ? "enabled" : "disabled",
105105
tlsEnabled ? "enabled" : "disabled",
106106
poolSize,
107107
queueCapacity

src/main/java/nl/hauntedmc/dataprovider/database/messaging/impl/redis/RedisMessagingDatabase.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public synchronized void connect() {
3939
String host = cfg.node("host").getString("localhost");
4040
int port = cfg.node("port").getInt(6379);
4141
int db = cfg.node("database").getInt(0);
42-
String user = cfg.node("user").getString(null);
42+
String user = cfg.node("user").getString("");
4343
String pass = cfg.node("password").getString("");
4444
int connectionPoolSize = Math.max(1, cfg.node("pool", "connections").getInt(4));
4545
int workerPoolSize = Math.max(1, cfg.node("pool", "threads").getInt(8));
@@ -65,8 +65,8 @@ public synchronized void connect() {
6565
poolConfig.setMaxTotal(connectionPoolSize);
6666

6767
DefaultJedisClientConfig.Builder clientConfigBuilder = DefaultJedisClientConfig.builder()
68-
.user((user == null || user.isBlank()) ? null : user)
69-
.password(pass.isEmpty() ? null : pass)
68+
.user(user.isBlank() ? null : user)
69+
.password(pass.isBlank() ? null : pass)
7070
.database(db)
7171
.ssl(tlsEnabled);
7272
if (tlsEnabled && trustAllCertificates) {
@@ -93,7 +93,7 @@ public synchronized void connect() {
9393
host,
9494
port,
9595
db,
96-
pass.isEmpty() ? "disabled" : "enabled",
96+
pass.isBlank() ? "disabled" : "enabled",
9797
tlsEnabled ? "enabled" : "disabled",
9898
maxSubscriptions,
9999
workerQueueCapacity,

src/main/java/nl/hauntedmc/dataprovider/database/relational/impl/mysql/MySQLDataAccess.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package nl.hauntedmc.dataprovider.database.relational.impl.mysql;
22

3-
import com.zaxxer.hikari.HikariDataSource;
43
import nl.hauntedmc.dataprovider.database.relational.RelationalDataAccess;
54
import nl.hauntedmc.dataprovider.database.relational.TransactionCallback;
65

6+
import javax.sql.DataSource;
77
import java.sql.*;
88
import java.util.ArrayList;
99
import java.util.LinkedHashMap;
@@ -17,10 +17,10 @@
1717
*/
1818
public class MySQLDataAccess implements RelationalDataAccess {
1919

20-
private final HikariDataSource dataSource;
20+
private final DataSource dataSource;
2121
private final ExecutorService executor;
2222

23-
public MySQLDataAccess(HikariDataSource dataSource, ExecutorService executor) {
23+
public MySQLDataAccess(DataSource dataSource, ExecutorService executor) {
2424
this.dataSource = dataSource;
2525
this.executor = executor;
2626
}

src/main/java/nl/hauntedmc/dataprovider/database/relational/impl/mysql/MySQLSchemaManager.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package nl.hauntedmc.dataprovider.database.relational.impl.mysql;
22

3-
import com.zaxxer.hikari.HikariDataSource;
43
import nl.hauntedmc.dataprovider.database.relational.schema.ColumnDefinition;
54
import nl.hauntedmc.dataprovider.database.relational.schema.SchemaManager;
65
import nl.hauntedmc.dataprovider.database.relational.schema.TableDefinition;
76

7+
import javax.sql.DataSource;
88
import java.sql.Connection;
99
import java.sql.PreparedStatement;
1010
import java.sql.ResultSet;
@@ -23,10 +23,10 @@ public class MySQLSchemaManager implements SchemaManager {
2323
private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]{0,63}");
2424
private static final Pattern SQL_TYPE_PATTERN = Pattern.compile("[A-Za-z0-9_(),\\s]+");
2525

26-
private final HikariDataSource dataSource;
26+
private final DataSource dataSource;
2727
private final ExecutorService executor;
2828

29-
public MySQLSchemaManager(HikariDataSource dataSource, ExecutorService executor) {
29+
public MySQLSchemaManager(DataSource dataSource, ExecutorService executor) {
3030
this.dataSource = dataSource;
3131
this.executor = executor;
3232
}

src/main/java/nl/hauntedmc/dataprovider/internal/DataProviderHandler.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,24 @@ public DataProviderHandler(
4040
Objects.requireNonNull(resourceClassLoader, "Resource class loader cannot be null.");
4141
Objects.requireNonNull(configHandler, "Config handler cannot be null.");
4242
this.logger = Objects.requireNonNull(logger, "Logger cannot be null.");
43+
this.callerContextResolver = Objects.requireNonNull(callerContextResolver, "Caller context resolver cannot be null.");
4344
this.ownClassLoader = resourceClassLoader;
4445

4546
DatabaseConfigMap configMap = new DatabaseConfigMap(dataPath, this.logger, resourceClassLoader);
4647
DatabaseFactory factory = new DatabaseFactory(configMap, this.logger);
4748
this.registry = new DataProviderRegistry(factory, configHandler, this.logger);
49+
}
50+
51+
DataProviderHandler(
52+
DataProviderRegistry registry,
53+
CallerContextResolver callerContextResolver,
54+
ILoggerAdapter logger,
55+
ClassLoader ownClassLoader
56+
) {
57+
this.logger = Objects.requireNonNull(logger, "Logger cannot be null.");
58+
this.registry = Objects.requireNonNull(registry, "Registry cannot be null.");
4859
this.callerContextResolver = Objects.requireNonNull(callerContextResolver, "Caller context resolver cannot be null.");
60+
this.ownClassLoader = Objects.requireNonNull(ownClassLoader, "Own class loader cannot be null.");
4961
}
5062

5163
/**
@@ -112,7 +124,7 @@ public Map<DatabaseConnectionKey, Integer> getActiveDatabaseReferenceCounts() {
112124

113125
private CallerContext resolveCallerContext() {
114126
CallerContext caller = callerContextResolver.resolveCaller();
115-
if (caller == null || caller.pluginId() == null || caller.pluginId().isBlank()) {
127+
if (caller == null) {
116128
logger.error("Could not resolve caller plugin context for API operation.");
117129
throw new SecurityException("Could not resolve caller plugin context.");
118130
}

0 commit comments

Comments
 (0)