From 5eb8c3da7dbf99604662178a4d05abd9c7b0c9f1 Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Wed, 31 Aug 2022 17:35:47 +0200
Subject: [PATCH 1/9] Start using assertj-db
---
gradle/libs.versions.toml | 7 +-
miner/build.gradle.kts | 2 +-
.../miner/database/SQLiteDatabaseTest.java | 78 +++++++++++++++++++
viewer/build.gradle.kts | 2 +-
4 files changed, 85 insertions(+), 4 deletions(-)
create mode 100644 miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6aed66a6..c554fa55 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -10,7 +10,8 @@ lang3-version = "3.12.0"
jetbrains-annotations-version = "23.0.0"
websocket-version = "1.5.3"
junit-version = "5.9.0"
-assertj-version = "3.23.1"
+assertj-core-version = "3.23.1"
+assertj-db-version = "2.0.2"
mockito-version = "4.7.0"
awaitility-version = "4.2.0"
json-unit-version = "2.35.0"
@@ -54,7 +55,8 @@ flyway-mysql = { group = "org.flywaydb", name = "flyway-mysql", version.ref = "f
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit-version" }
junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit-version" }
junitEngine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit-version" }
-assertj = { group = "org.assertj", name = "assertj-core", version.ref = "assertj-version" }
+assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "assertj-core-version" }
+assertj-db = { group = "org.assertj", name = "assertj-db", version.ref = "assertj-db-version" }
mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito-version" }
mockito-inline = { group = "org.mockito", name = "mockito-inline", version.ref = "mockito-version" }
mockito-junit = { group = "org.mockito", name = "mockito-junit-jupiter", version.ref = "mockito-version" }
@@ -71,6 +73,7 @@ flyway = ["flyway-core", "flyway-mysql"]
junit = ["junit-api", "junit-params"]
mockito = ["mockito-core", "mockito-inline", "mockito-junit"]
jsonUnit = ["json-unit", "json-unit-assertj"]
+assertj = ["assertj-core", "assertj-db"]
[plugins]
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow-version" }
diff --git a/miner/build.gradle.kts b/miner/build.gradle.kts
index 2c802c7d..c7320ae1 100644
--- a/miner/build.gradle.kts
+++ b/miner/build.gradle.kts
@@ -29,7 +29,7 @@ dependencies {
testImplementation(libs.bundles.junit)
testRuntimeOnly(libs.junitEngine)
- testImplementation(libs.assertj)
+ testImplementation(libs.bundles.assertj)
testImplementation(libs.bundles.mockito)
testImplementation(libs.awaitility)
testImplementation(libs.unirestMocks)
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
new file mode 100644
index 00000000..ff260f25
--- /dev/null
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -0,0 +1,78 @@
+package fr.raksrinana.channelpointsminer.miner.database;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.assertj.db.type.Table;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import java.nio.file.Path;
+import java.sql.SQLException;
+import static org.assertj.db.api.Assertions.assertThat;
+import static org.assertj.db.output.Outputs.output;
+
+class SQLiteDatabaseTest{
+ private static final String CHANNEL_ID = "channel-id";
+ private static final String CHANNEL_USERNAME = "channel-username";
+
+ @TempDir
+ private Path tempPath;
+
+ private SQLiteDatabase tested;
+ private HikariDataSource dataSource;
+
+ private Table tableBalance;
+ private Table tableChannel;
+ private Table tablePrediction;
+ private Table tablePredictionUser;
+ private Table tableUserPrediction;
+
+ @BeforeEach
+ void setUp() throws SQLException{
+ var poolConfiguration = new HikariConfig();
+ poolConfiguration.setJdbcUrl("jdbc:sqlite:" + tempPath.resolve(System.currentTimeMillis() + "_test.db").toAbsolutePath());
+ poolConfiguration.setDriverClassName("org.sqlite.JDBC");
+ poolConfiguration.setMaximumPoolSize(1);
+
+ dataSource = new HikariDataSource(poolConfiguration);
+ tested = new SQLiteDatabase(dataSource);
+
+ tested.initDatabase();
+
+ tableBalance = new Table(dataSource, "Balance");
+ tableChannel = new Table(dataSource, "Channel");
+ tablePrediction = new Table(dataSource, "Prediction");
+ tablePredictionUser = new Table(dataSource, "PredictionUser");
+ tableUserPrediction = new Table(dataSource, "UserPrediction");
+ }
+
+ @AfterEach
+ void tearDown(){
+ tested.close();
+ }
+
+ @Test
+ void tablesAreCreated(){
+ assertThat(tableBalance).exists();
+ assertThat(tableChannel).exists();
+ assertThat(tablePrediction).exists();
+ assertThat(tablePredictionUser).exists();
+ assertThat(tableUserPrediction).exists();
+ }
+
+ @Test
+ void newChannelIsInsertedIfNew() throws SQLException{
+ assertThat(tableChannel).isEmpty();
+
+ tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+
+ output(tableChannel).toConsole();
+ assertThat(tableChannel)
+ .hasNumberOfRows(1) // TODO Hmmm why isn't it passing, if looking manually there is indeed 1 record :/ Because of Hikari that has a pool?
+ .row()
+ .column("ID").value().isEqualTo(CHANNEL_ID)
+ .column("Username").value().isEqualTo(CHANNEL_USERNAME)
+ .column("LastStatusChange").value().isNotNull();
+ }
+}
\ No newline at end of file
diff --git a/viewer/build.gradle.kts b/viewer/build.gradle.kts
index faffac17..e21d9e43 100644
--- a/viewer/build.gradle.kts
+++ b/viewer/build.gradle.kts
@@ -19,7 +19,7 @@ dependencies {
testImplementation(libs.bundles.junit)
testRuntimeOnly(libs.junitEngine)
- testImplementation(libs.assertj)
+ testImplementation(libs.bundles.assertj)
testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-inline")
testImplementation("org.mockito:mockito-junit-jupiter")
From bc399f7984e33e55ced9840d32d4bba2e24131e1 Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sat, 3 Sep 2022 15:47:08 +0200
Subject: [PATCH 2/9] Add more DB tests
---
.../miner/database/BaseDatabase.java | 19 ++---
.../miner/database/SQLiteDatabase.java | 8 +-
.../db/migrations/sqlite/V1_0_0__base.sql | 10 +--
.../miner/database/SQLiteDatabaseTest.java | 79 +++++++++++++------
.../tests/WebsocketMockServerExtension.java | 2 +-
miner/src/test/resources/log4j2-test.xml | 10 +--
6 files changed, 76 insertions(+), 52 deletions(-)
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
index 59b5a583..028045fc 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
@@ -14,8 +14,8 @@
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
+import java.sql.Timestamp;
import java.time.Instant;
-import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.LinkedList;
@@ -23,7 +23,6 @@
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
-import static java.time.ZoneOffset.UTC;
@RequiredArgsConstructor
@Log4j2
@@ -54,9 +53,7 @@ public void updateChannelStatusTime(@NotNull String channelId, @NotNull Instant
WHERE `ID` = ?;"""
)){
- var timestamp = LocalDateTime.now(UTC);
-
- statement.setObject(1, timestamp);
+ statement.setTimestamp(1, Timestamp.from(instant));
statement.setString(2, channelId);
statement.executeUpdate();
@@ -72,7 +69,7 @@ public void addBalance(@NotNull String channelId, int balance, @Nullable String
)){
statement.setString(1, channelId);
- statement.setObject(2, LocalDateTime.ofInstant(instant, UTC));
+ statement.setTimestamp(2, Timestamp.from(instant));
statement.setInt(3, balance);
statement.setString(4, reason);
@@ -90,7 +87,7 @@ public void addPrediction(@NotNull String channelId, @NotNull String eventId, @N
statement.setString(1, channelId);
statement.setString(2, eventId);
- statement.setObject(3, LocalDateTime.ofInstant(instant, UTC));
+ statement.setTimestamp(3, Timestamp.from(instant));
statement.setString(4, type);
statement.setString(5, description);
@@ -182,8 +179,8 @@ public void cancelPrediction(@NotNull Event event) throws SQLException{
addCanceledPredictionStmt.setString(1, event.getId());
addCanceledPredictionStmt.setString(2, event.getChannelId());
addCanceledPredictionStmt.setString(3, event.getTitle());
- addCanceledPredictionStmt.setObject(4, event.getCreatedAt().withZoneSameInstant(UTC).toLocalDateTime());
- addCanceledPredictionStmt.setObject(5, LocalDateTime.ofInstant(ended, UTC));
+ addCanceledPredictionStmt.setTimestamp(4, Timestamp.from(event.getCreatedAt().toInstant()));
+ addCanceledPredictionStmt.setTimestamp(5, Timestamp.from(ended));
addCanceledPredictionStmt.executeUpdate();
//Remove made predictions
@@ -240,8 +237,8 @@ public void resolvePrediction(@NotNull Event event, @NotNull String outcome, @No
addResolvedPredictionStmt.setString(1, event.getId());
addResolvedPredictionStmt.setString(2, event.getChannelId());
addResolvedPredictionStmt.setString(3, event.getTitle());
- addResolvedPredictionStmt.setObject(4, event.getCreatedAt().withZoneSameInstant(UTC).toLocalDateTime());
- addResolvedPredictionStmt.setObject(5, LocalDateTime.ofInstant(ended, UTC));
+ addResolvedPredictionStmt.setTimestamp(4, Timestamp.from(event.getCreatedAt().toInstant()));
+ addResolvedPredictionStmt.setTimestamp(5, Timestamp.from(ended));
addResolvedPredictionStmt.setString(6, outcome);
addResolvedPredictionStmt.setString(7, badge);
addResolvedPredictionStmt.setDouble(8, returnRatioForWin);
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
index a65d186f..697befe4 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
@@ -1,12 +1,12 @@
package fr.raksrinana.channelpointsminer.miner.database;
import com.zaxxer.hikari.HikariDataSource;
+import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
import org.jetbrains.annotations.NotNull;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
-import java.time.LocalDateTime;
-import static java.time.ZoneOffset.UTC;
+import java.sql.Timestamp;
public class SQLiteDatabase extends BaseDatabase{
public SQLiteDatabase(HikariDataSource dataSource){
@@ -26,11 +26,9 @@ public void createChannel(@NotNull String channelId, @NotNull String username) t
VALUES(?, ?, ?);"""
)){
- var timestamp = LocalDateTime.now(UTC);
-
statement.setString(1, channelId);
statement.setString(2, username);
- statement.setObject(3, timestamp);
+ statement.setTimestamp(3, Timestamp.from(TimeFactory.now()));
statement.executeUpdate();
}
diff --git a/miner/src/main/resources/db/migrations/sqlite/V1_0_0__base.sql b/miner/src/main/resources/db/migrations/sqlite/V1_0_0__base.sql
index e250838f..5bc1781f 100644
--- a/miner/src/main/resources/db/migrations/sqlite/V1_0_0__base.sql
+++ b/miner/src/main/resources/db/migrations/sqlite/V1_0_0__base.sql
@@ -2,14 +2,14 @@ CREATE TABLE IF NOT EXISTS `Channel`
(
`ID` VARCHAR(32) NOT NULL PRIMARY KEY,
`Username` VARCHAR(128) NOT NULL,
- `LastStatusChange` DATETIME NOT NULL
+ `LastStatusChange` TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS `Balance`
(
`ID` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`ChannelID` VARCHAR(32) NOT NULL REFERENCES `Channel` (`ID`),
- `BalanceDate` DATETIME(3) NOT NULL,
+ `BalanceDate` TIMESTAMP NOT NULL,
`Balance` INTEGER NOT NULL,
`Reason` VARCHAR(16) NULL
);
@@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS `Prediction`
`ID` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`ChannelID` VARCHAR(32) NOT NULL REFERENCES `Channel` (`ID`),
`EventID` VARCHAR(36) NOT NULL,
- `EventDate` DATETIME NOT NULL,
+ `EventDate` TIMESTAMP NOT NULL,
`Type` VARCHAR(16) NULL,
`Description` VARCHAR(255) NULL
);
@@ -35,8 +35,8 @@ CREATE TABLE IF NOT EXISTS `ResolvedPrediction`
`EventID` VARCHAR(36) NOT NULL PRIMARY KEY,
`ChannelID` VARCHAR(32) NOT NULL REFERENCES `Channel` (`ID`),
`Title` VARCHAR(64) NOT NULL,
- `EventCreated` DATETIME NOT NULL,
- `EventEnded` DATETIME NULL,
+ `EventCreated` TIMESTAMP NOT NULL,
+ `EventEnded` TIMESTAMP NULL,
`Canceled` BOOLEAN NOT NULL,
`Outcome` VARCHAR(32) NULL,
`Badge` VARCHAR(32) NULL,
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index ff260f25..bfd39423 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -2,6 +2,7 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
+import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
import org.assertj.db.type.Table;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -9,8 +10,14 @@
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import java.sql.SQLException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.util.function.Supplier;
import static org.assertj.db.api.Assertions.assertThat;
-import static org.assertj.db.output.Outputs.output;
+import static org.mockito.Mockito.mockStatic;
class SQLiteDatabaseTest{
private static final String CHANNEL_ID = "channel-id";
@@ -22,11 +29,11 @@ class SQLiteDatabaseTest{
private SQLiteDatabase tested;
private HikariDataSource dataSource;
- private Table tableBalance;
- private Table tableChannel;
- private Table tablePrediction;
- private Table tablePredictionUser;
- private Table tableUserPrediction;
+ private final Supplier
tableBalance = () -> new Table(dataSource, "Balance");
+ private final Supplier tableChannel = () -> new Table(dataSource, "Channel");
+ private final Supplier tablePrediction = () -> new Table(dataSource, "Prediction");
+ private final Supplier tablePredictionUser = () -> new Table(dataSource, "PredictionUser");
+ private final Supplier tableUserPrediction = () -> new Table(dataSource, "UserPrediction");
@BeforeEach
void setUp() throws SQLException{
@@ -39,12 +46,6 @@ void setUp() throws SQLException{
tested = new SQLiteDatabase(dataSource);
tested.initDatabase();
-
- tableBalance = new Table(dataSource, "Balance");
- tableChannel = new Table(dataSource, "Channel");
- tablePrediction = new Table(dataSource, "Prediction");
- tablePredictionUser = new Table(dataSource, "PredictionUser");
- tableUserPrediction = new Table(dataSource, "UserPrediction");
}
@AfterEach
@@ -54,25 +55,59 @@ void tearDown(){
@Test
void tablesAreCreated(){
- assertThat(tableBalance).exists();
- assertThat(tableChannel).exists();
- assertThat(tablePrediction).exists();
- assertThat(tablePredictionUser).exists();
- assertThat(tableUserPrediction).exists();
+ assertThat(tableBalance.get()).exists();
+ assertThat(tableChannel.get()).exists();
+ assertThat(tablePrediction.get()).exists();
+ assertThat(tablePredictionUser.get()).exists();
+ assertThat(tableUserPrediction.get()).exists();
}
@Test
void newChannelIsInsertedIfNew() throws SQLException{
- assertThat(tableChannel).isEmpty();
-
tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
- output(tableChannel).toConsole();
- assertThat(tableChannel)
- .hasNumberOfRows(1) // TODO Hmmm why isn't it passing, if looking manually there is indeed 1 record :/ Because of Hikari that has a pool?
+ assertThat(tableChannel.get())
+ .hasNumberOfRows(1)
.row()
.column("ID").value().isEqualTo(CHANNEL_ID)
.column("Username").value().isEqualTo(CHANNEL_USERNAME)
.column("LastStatusChange").value().isNotNull();
}
+
+ @Test
+ void oldChannelIsNotInsertedIfNew() throws SQLException{
+ try(var factory = mockStatic(TimeFactory.class)){
+ var beforeChange = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
+ factory.when(TimeFactory::now).thenReturn(beforeChange);
+
+ tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+ assertThat(tableChannel.get()).hasNumberOfRows(1);
+
+ factory.when(TimeFactory::now).thenReturn(beforeChange.plus(1, ChronoUnit.HOURS));
+
+ tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+
+ assertThat(tableChannel.get()).hasNumberOfRows(1)
+ .row()
+ .column("LastStatusChange").value().isEqualTo(LocalDateTime.ofInstant(beforeChange, ZoneId.systemDefault()));
+ }
+ }
+
+ @Test
+ void updateChannelStatusTime() throws SQLException{
+ try(var factory = mockStatic(TimeFactory.class)){
+ var beforeChange = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
+ factory.when(TimeFactory::now).thenReturn(beforeChange);
+
+ tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+ assertThat(tableChannel.get()).hasNumberOfRows(1);
+
+ var newTime = beforeChange.plus(1, ChronoUnit.HOURS);
+ tested.updateChannelStatusTime(CHANNEL_ID, newTime);
+
+ assertThat(tableChannel.get()).hasNumberOfRows(1)
+ .row()
+ .column("LastStatusChange").value().isEqualTo(LocalDateTime.ofInstant(newTime, ZoneId.systemDefault()));
+ }
+ }
}
\ No newline at end of file
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/tests/WebsocketMockServerExtension.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/tests/WebsocketMockServerExtension.java
index c47d1667..92db897d 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/tests/WebsocketMockServerExtension.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/tests/WebsocketMockServerExtension.java
@@ -15,7 +15,7 @@ public class WebsocketMockServerExtension implements Extension, BeforeAllCallbac
private final WebsocketMockServer server;
public WebsocketMockServerExtension(){
- server = new WebsocketMockServer(ThreadLocalRandom.current().nextInt(10000, 30000));
+ this(ThreadLocalRandom.current().nextInt(10000, 30000));
}
public WebsocketMockServerExtension(int port){
diff --git a/miner/src/test/resources/log4j2-test.xml b/miner/src/test/resources/log4j2-test.xml
index ca2eea96..c2188970 100644
--- a/miner/src/test/resources/log4j2-test.xml
+++ b/miner/src/test/resources/log4j2-test.xml
@@ -6,16 +6,10 @@
-
+
-
-
-
-
-
-
-
+
From 5cac835e058c4e93b456bfa5198d4875e52a3c49 Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sat, 3 Sep 2022 15:50:14 +0200
Subject: [PATCH 3/9] PR template
---
.github/PULL_REQUEST_TEMPLATE.md | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index b8f7c1c5..c6783fcf 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -3,13 +3,11 @@
### Checklist
- [ ] Tests have been added in relevant areas
-- [ ] Corresponding changes made to the documentation (README.adoc)
+- [ ] Corresponding changes made to the documentation (README.adoc)
### Type of change
-- [ ] Bug fix
-- [ ] New feature
-- [ ] Breaking change
+
## Description
From d6bc3918bf240adf9f73f10c687e7aafcfef946c Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sun, 4 Sep 2022 09:03:29 +0200
Subject: [PATCH 4/9] Add balance test
---
.../miner/database/SQLiteDatabaseTest.java | 86 +++++++++++++++----
1 file changed, 71 insertions(+), 15 deletions(-)
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index bfd39423..473b4e42 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -3,6 +3,7 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
+import org.assertj.db.type.Changes;
import org.assertj.db.type.Table;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -23,13 +24,21 @@ class SQLiteDatabaseTest{
private static final String CHANNEL_ID = "channel-id";
private static final String CHANNEL_USERNAME = "channel-username";
+ private static final String ID_COL = "ID";
+ private static final String USERNAME_COL = "Username";
+ private static final String LAST_STATUS_CHANGE_COL = "LastStatusChange";
+ private static final String CHANNEL_ID_COL = "ChannelID";
+ private static final String BALANCE_DATE_COL = "BalanceDate";
+ private static final String BALANCE_COL = "Balance";
+ private static final String REASON_COL = "Reason";
+
@TempDir
private Path tempPath;
private SQLiteDatabase tested;
private HikariDataSource dataSource;
- private final Supplier tableBalance = () -> new Table(dataSource, "Balance");
+ private final Supplier tableBalance = () -> new Table(dataSource, BALANCE_COL);
private final Supplier tableChannel = () -> new Table(dataSource, "Channel");
private final Supplier tablePrediction = () -> new Table(dataSource, "Prediction");
private final Supplier tablePredictionUser = () -> new Table(dataSource, "PredictionUser");
@@ -64,50 +73,97 @@ void tablesAreCreated(){
@Test
void newChannelIsInsertedIfNew() throws SQLException{
+ var table = tableChannel.get();
+ var changes = new Changes(table);
+
+ changes.setStartPointNow();
tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+ changes.setEndPointNow();
- assertThat(tableChannel.get())
- .hasNumberOfRows(1)
- .row()
- .column("ID").value().isEqualTo(CHANNEL_ID)
- .column("Username").value().isEqualTo(CHANNEL_USERNAME)
- .column("LastStatusChange").value().isNotNull();
+ assertThat(changes).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(USERNAME_COL).valueAtEndPoint().isEqualTo(CHANNEL_USERNAME)
+ .column(LAST_STATUS_CHANGE_COL).valueAtEndPoint().isNotNull();
}
@Test
void oldChannelIsNotInsertedIfNew() throws SQLException{
try(var factory = mockStatic(TimeFactory.class)){
+ var table = tableChannel.get();
+ var changes = new Changes(table);
+
var beforeChange = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
factory.when(TimeFactory::now).thenReturn(beforeChange);
tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
- assertThat(tableChannel.get()).hasNumberOfRows(1);
factory.when(TimeFactory::now).thenReturn(beforeChange.plus(1, ChronoUnit.HOURS));
+ changes.setStartPointNow();
tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+ changes.setEndPointNow();
- assertThat(tableChannel.get()).hasNumberOfRows(1)
- .row()
- .column("LastStatusChange").value().isEqualTo(LocalDateTime.ofInstant(beforeChange, ZoneId.systemDefault()));
+ assertThat(changes).hasNumberOfChanges(0);
}
}
@Test
void updateChannelStatusTime() throws SQLException{
try(var factory = mockStatic(TimeFactory.class)){
+ var table = tableChannel.get();
+ var changes = new Changes(table);
+
var beforeChange = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
factory.when(TimeFactory::now).thenReturn(beforeChange);
tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
- assertThat(tableChannel.get()).hasNumberOfRows(1);
var newTime = beforeChange.plus(1, ChronoUnit.HOURS);
+
+ changes.setStartPointNow();
tested.updateChannelStatusTime(CHANNEL_ID, newTime);
+ changes.setEndPointNow();
- assertThat(tableChannel.get()).hasNumberOfRows(1)
- .row()
- .column("LastStatusChange").value().isEqualTo(LocalDateTime.ofInstant(newTime, ZoneId.systemDefault()));
+ assertThat(changes).hasNumberOfChanges(1)
+ .changeOfModification()
+ .column(LAST_STATUS_CHANGE_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(newTime));
}
}
+
+ @Test
+ void addBalance() throws SQLException{
+ var table = tableBalance.get();
+ var change = new Changes(table);
+
+ var firstInstant = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
+ change.setStartPointNow();
+ tested.addBalance(CHANNEL_ID, 25, "Test1", firstInstant);
+ change.setEndPointNow();
+
+ assertThat(change).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(ID_COL).valueAtEndPoint().isNotNull()
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(BALANCE_DATE_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(firstInstant))
+ .column(BALANCE_COL).valueAtEndPoint().isEqualTo(25)
+ .column(REASON_COL).valueAtEndPoint().isEqualTo("Test1");
+
+ var secondInstant = firstInstant.plusSeconds(30);
+ change.setStartPointNow();
+ tested.addBalance(CHANNEL_ID, 50, "Test2", secondInstant);
+ change.setEndPointNow();
+
+ assertThat(change).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(ID_COL).valueAtEndPoint().isNotNull()
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(BALANCE_DATE_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(secondInstant))
+ .column(BALANCE_COL).valueAtEndPoint().isEqualTo(50)
+ .column(REASON_COL).valueAtEndPoint().isEqualTo("Test2");
+ }
+
+ private LocalDateTime getExpectedTimestamp(Instant instant){
+ return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
+ }
}
\ No newline at end of file
From 9d38fe1a8347f9c4b05e4275e47945ec8ff0bf2b Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sun, 4 Sep 2022 09:34:37 +0200
Subject: [PATCH 5/9] Add prediction test
---
.../miner/database/IDatabase.java | 6 +--
.../miner/database/SQLiteDatabaseTest.java | 38 +++++++++++++++++++
2 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
index 48f52d59..1c82cfa3 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
@@ -12,6 +12,9 @@
public interface IDatabase extends AutoCloseable{
void initDatabase() throws SQLException;
+ @Override
+ void close();
+
void createChannel(@NotNull String channelId, @NotNull String username) throws SQLException;
void updateChannelStatusTime(@NotNull String channelId, @NotNull Instant instant) throws SQLException;
@@ -33,9 +36,6 @@ public interface IDatabase extends AutoCloseable{
@NotNull
Collection getOutcomeStatisticsForChannel(@NotNull String channelId, int minBetsPlacedByUser) throws SQLException;
- @Override
- void close();
-
@NotNull
Optional getStreamerIdFromName(@NotNull String channelName) throws SQLException;
}
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index 473b4e42..153c1bab 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -31,6 +31,10 @@ class SQLiteDatabaseTest{
private static final String BALANCE_DATE_COL = "BalanceDate";
private static final String BALANCE_COL = "Balance";
private static final String REASON_COL = "Reason";
+ private static final String EVENT_ID_COL = "EventID";
+ private static final String EVENT_DATE_COL = "EventDate";
+ private static final String TYPE_COL = "Type";
+ private static final String DESCRIPTION_COL = "Description";
@TempDir
private Path tempPath;
@@ -163,6 +167,40 @@ void addBalance() throws SQLException{
.column(REASON_COL).valueAtEndPoint().isEqualTo("Test2");
}
+ @Test
+ void addPrediction() throws SQLException{
+ var table = tablePrediction.get();
+ var change = new Changes(table);
+
+ var firstInstant = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
+ change.setStartPointNow();
+ tested.addPrediction(CHANNEL_ID, "Event1", "Type1", "Description1", firstInstant);
+ change.setEndPointNow();
+
+ assertThat(change).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(ID_COL).valueAtEndPoint().isNotNull()
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(EVENT_ID_COL).valueAtEndPoint().isEqualTo("Event1")
+ .column(EVENT_DATE_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(firstInstant))
+ .column(TYPE_COL).valueAtEndPoint().isEqualTo("Type1")
+ .column(DESCRIPTION_COL).valueAtEndPoint().isEqualTo("Description1");
+
+ var secondInstant = firstInstant.plusSeconds(30);
+ change.setStartPointNow();
+ tested.addPrediction(CHANNEL_ID, "Event2", "Type2", "Description2", secondInstant);
+ change.setEndPointNow();
+
+ assertThat(change).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(ID_COL).valueAtEndPoint().isNotNull()
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(EVENT_ID_COL).valueAtEndPoint().isEqualTo("Event2")
+ .column(EVENT_DATE_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(secondInstant))
+ .column(TYPE_COL).valueAtEndPoint().isEqualTo("Type2")
+ .column(DESCRIPTION_COL).valueAtEndPoint().isEqualTo("Description2");
+ }
+
private LocalDateTime getExpectedTimestamp(Instant instant){
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
From 56fe2658fda64ee47d2ac19da47d91b5c3b366f3 Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sun, 4 Sep 2022 09:46:30 +0200
Subject: [PATCH 6/9] Add user prediction test
---
.../miner/database/SQLiteDatabaseTest.java | 56 +++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index 153c1bab..6c0e4997 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -23,6 +23,7 @@
class SQLiteDatabaseTest{
private static final String CHANNEL_ID = "channel-id";
private static final String CHANNEL_USERNAME = "channel-username";
+ private static final String USER_USERNAME = "user1";
private static final String ID_COL = "ID";
private static final String USERNAME_COL = "Username";
@@ -35,6 +36,12 @@ class SQLiteDatabaseTest{
private static final String EVENT_DATE_COL = "EventDate";
private static final String TYPE_COL = "Type";
private static final String DESCRIPTION_COL = "Description";
+ private static final String PREDICTION_CNT_COL = "PredictionCnt";
+ private static final String WIN_CNT_COL = "WinCnt";
+ private static final String WIN_RATE_COL = "WinRate";
+ private static final String RETURN_ON_INVESTMENT_COL = "ReturnOnInvestment";
+ private static final String USER_ID_COL = "UserID";
+ private static final String BADGE_COL = "Badge";
@TempDir
private Path tempPath;
@@ -201,6 +208,55 @@ void addPrediction() throws SQLException{
.column(DESCRIPTION_COL).valueAtEndPoint().isEqualTo("Description2");
}
+ @Test
+ void addPredictionFromNewUser() throws SQLException{
+ var tableUserPrediction = this.tableUserPrediction.get();
+ var tablePredictionUser = this.tablePredictionUser.get();
+ var changeUserPrediction = new Changes(tableUserPrediction);
+ var changePredictionUser = new Changes(tablePredictionUser);
+
+ changeUserPrediction.setStartPointNow();
+ changePredictionUser.setStartPointNow();
+ tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ changeUserPrediction.setEndPointNow();
+ changePredictionUser.setEndPointNow();
+
+ assertThat(changePredictionUser).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(ID_COL).valueAtEndPoint().isNotNull()
+ .column(USERNAME_COL).valueAtEndPoint().isEqualTo(USER_USERNAME)
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(PREDICTION_CNT_COL).valueAtEndPoint().isEqualTo(0)
+ .column(WIN_CNT_COL).valueAtEndPoint().isEqualTo(0)
+ .column(WIN_RATE_COL).valueAtEndPoint().isEqualTo(0D)
+ .column(RETURN_ON_INVESTMENT_COL).valueAtEndPoint().isEqualTo(0D);
+
+ assertThat(changeUserPrediction).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(USER_ID_COL).valueAtEndPoint().isNotNull()
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(BADGE_COL).valueAtEndPoint().isEqualTo("B1");
+ }
+
+ @Test
+ void addPredictionFromExistingUser() throws SQLException{
+ var tableUserPrediction = this.tableUserPrediction.get();
+ var tablePredictionUser = this.tablePredictionUser.get();
+ var changeUserPrediction = new Changes(tableUserPrediction);
+ var changePredictionUser = new Changes(tablePredictionUser);
+
+ tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+
+ changeUserPrediction.setStartPointNow();
+ changePredictionUser.setStartPointNow();
+ tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B2"); //This should be impossible, we can't change badge
+ changeUserPrediction.setEndPointNow();
+ changePredictionUser.setEndPointNow();
+
+ assertThat(changePredictionUser).hasNumberOfChanges(0);
+ assertThat(changeUserPrediction).hasNumberOfChanges(0);
+ }
+
private LocalDateTime getExpectedTimestamp(Instant instant){
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
From 83b6fc8a6e60493a9949f97be5dcddd48ac44beb Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sun, 4 Sep 2022 10:23:27 +0200
Subject: [PATCH 7/9] Add cancel prediction test
---
.../miner/database/BaseDatabase.java | 130 +++++-------
.../miner/database/IDatabase.java | 2 +-
.../miner/database/MariaDBDatabase.java | 17 +-
.../miner/database/NoOpDatabase.java | 3 +-
.../miner/database/SQLiteDatabase.java | 19 +-
.../miner/database/SQLiteDatabaseTest.java | 197 +++++++++++++-----
6 files changed, 222 insertions(+), 146 deletions(-)
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
index 028045fc..54615b6b 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
@@ -60,6 +60,13 @@ public void updateChannelStatusTime(@NotNull String channelId, @NotNull Instant
}
}
+ @Override
+ public int addUserPrediction(@NotNull String username, @NotNull String channelId, @NotNull String badge) throws SQLException{
+ var userId = getOrCreatePredictionUserId(username, channelId);
+ addUserPrediction(channelId, userId, badge);
+ return userId;
+ }
+
@Override
public void addBalance(@NotNull String channelId, int balance, @Nullable String reason, @NotNull Instant instant) throws SQLException{
try(var conn = getConnection();
@@ -95,31 +102,14 @@ public void addPrediction(@NotNull String channelId, @NotNull String eventId, @N
}
}
- @Override
- public void addUserPrediction(@NotNull String username, @NotNull String channelId, @NotNull String badge) throws SQLException{
-
- try(var conn = getConnection();
- var predictionStatement = getPredictionStmt(conn)){
- conn.setAutoCommit(false);
-
- var userId = getOrCreatePredictionUserId(conn, username, channelId);
-
- predictionStatement.setString(1, channelId);
- predictionStatement.setInt(2, userId);
- predictionStatement.setString(3, badge);
-
- predictionStatement.executeUpdate();
- conn.commit();
- }
- }
-
- protected int getOrCreatePredictionUserId(@NotNull Connection conn, @NotNull String username, @NotNull String channelId) throws SQLException{
+ protected int getOrCreatePredictionUserId(@NotNull String username, @NotNull String channelId) throws SQLException{
username = username.toLowerCase(Locale.ROOT);
var lock = getOrCreatePredictionUserIdLocks[hashToIndex(username.hashCode(), getOrCreatePredictionUserIdLocks.length)];
lock.lock();
- try(var selectUserStatement = conn.prepareStatement("""
- SELECT `ID` FROM `PredictionUser` WHERE `Username`=? AND `ChannelID`=?""")){
+ try(var conn = getConnection();
+ var selectUserStatement = conn.prepareStatement("""
+ SELECT `ID` FROM `PredictionUser` WHERE `Username`=? AND `ChannelID`=?""")){
selectUserStatement.setString(1, username);
selectUserStatement.setString(2, channelId);
@@ -153,6 +143,8 @@ protected int getOrCreatePredictionUserId(@NotNull Connection conn, @NotNull Str
}
}
+ protected abstract void addUserPrediction(@NotNull String channelId, int userId, @NotNull String badge) throws SQLException;
+
private int hashToIndex(int hash, int length){
if(hash == Integer.MIN_VALUE){
return 0;
@@ -160,9 +152,6 @@ private int hashToIndex(int hash, int length){
return Math.abs(hash) % length;
}
- @NotNull
- protected abstract PreparedStatement getPredictionStmt(@NotNull Connection conn) throws SQLException;
-
@Override
public void cancelPrediction(@NotNull Event event) throws SQLException{
var ended = Optional.ofNullable(event.getEndedAt()).map(ZonedDateTime::toInstant).orElseGet(TimeFactory::now);
@@ -170,30 +159,17 @@ public void cancelPrediction(@NotNull Event event) throws SQLException{
try(var conn = getConnection();
var addCanceledPredictionStmt = conn.prepareStatement("""
INSERT INTO `ResolvedPrediction`(`EventID`,`ChannelID`, `Title`,`EventCreated`,`EventEnded`,`Canceled`)
- VALUES (?,?,?,?,?,true)""");
- var removePredictionsStmt = getDeleteUserPredictionsForChannelStmt(conn)
+ VALUES (?,?,?,?,?,true)""")
){
- conn.setAutoCommit(false);
- try{
- //Add canceled prediction
- addCanceledPredictionStmt.setString(1, event.getId());
- addCanceledPredictionStmt.setString(2, event.getChannelId());
- addCanceledPredictionStmt.setString(3, event.getTitle());
- addCanceledPredictionStmt.setTimestamp(4, Timestamp.from(event.getCreatedAt().toInstant()));
- addCanceledPredictionStmt.setTimestamp(5, Timestamp.from(ended));
- addCanceledPredictionStmt.executeUpdate();
-
- //Remove made predictions
- removePredictionsStmt.setString(1, event.getChannelId());
- removePredictionsStmt.executeUpdate();
-
- conn.commit();
- }
- catch(SQLException e){
- conn.rollback();
- throw e;
- }
+ addCanceledPredictionStmt.setString(1, event.getId());
+ addCanceledPredictionStmt.setString(2, event.getChannelId());
+ addCanceledPredictionStmt.setString(3, event.getTitle());
+ addCanceledPredictionStmt.setTimestamp(4, Timestamp.from(event.getCreatedAt().toInstant()));
+ addCanceledPredictionStmt.setTimestamp(5, Timestamp.from(ended));
+ addCanceledPredictionStmt.executeUpdate();
}
+
+ deleteUserPredictionsForChannel(event.getChannelId());
}
@Override
@@ -264,21 +240,11 @@ public void resolvePrediction(@NotNull Event event, @NotNull String outcome, @No
public void deleteUserPredictions() throws SQLException{
log.debug("Removing all user predictions.");
try(var conn = getConnection();
- var statement = conn.prepareStatement("""
- DELETE FROM `UserPrediction`"""
- )){
+ var statement = conn.prepareStatement("DELETE FROM `UserPrediction`")){
statement.executeUpdate();
}
}
- @NotNull
- private PreparedStatement getDeleteUserPredictionsForChannelStmt(@NotNull Connection conn) throws SQLException{
- return conn.prepareStatement("""
- DELETE FROM `UserPrediction`
- WHERE `ChannelID`=?"""
- );
- }
-
@Override
public void deleteUserPredictionsForChannel(@NotNull String channelId) throws SQLException{
log.debug("Removing user predictions for channelId '{}'.", channelId);
@@ -289,6 +255,28 @@ public void deleteUserPredictionsForChannel(@NotNull String channelId) throws SQ
}
}
+ @Override
+ @NotNull
+ public Optional getStreamerIdFromName(@NotNull String channelName) throws SQLException{
+ log.debug("Getting streamerId from channel {}", channelName);
+ try(var conn = getConnection();
+ var statement = conn.prepareStatement("""
+ SELECT `ID`
+ FROM `Channel`
+ WHERE `Username`=?"""
+ )){
+ statement.setString(1, channelName);
+
+ try(var result = statement.executeQuery()){
+ if(result.next()){
+ return Optional.ofNullable(result.getString("ID"));
+ }
+ }
+
+ return Optional.empty();
+ }
+ }
+
@Override
@NotNull
public Collection getOutcomeStatisticsForChannel(@NotNull String channelId, int minBetsPlacedByUser) throws SQLException{
@@ -327,30 +315,16 @@ public void close(){
dataSource.close();
}
- @Override
@NotNull
- public Optional getStreamerIdFromName(@NotNull String channelName) throws SQLException{
- log.debug("Getting streamerId from channel {}", channelName);
- try(var conn = getConnection();
- var statement = conn.prepareStatement("""
- SELECT `ID`
- FROM `Channel`
- WHERE `Username`=?"""
- )){
- statement.setString(1, channelName);
-
- try(var result = statement.executeQuery()){
- if(result.next()){
- return Optional.ofNullable(result.getString("ID"));
- }
- }
-
- return Optional.empty();
- }
+ protected Connection getConnection() throws SQLException{
+ return dataSource.getConnection();
}
@NotNull
- protected Connection getConnection() throws SQLException{
- return dataSource.getConnection();
+ private PreparedStatement getDeleteUserPredictionsForChannelStmt(@NotNull Connection conn) throws SQLException{
+ return conn.prepareStatement("""
+ DELETE FROM `UserPrediction`
+ WHERE `ChannelID`=?"""
+ );
}
}
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
index 1c82cfa3..643fb933 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
@@ -23,7 +23,7 @@ public interface IDatabase extends AutoCloseable{
void addPrediction(@NotNull String channelId, @NotNull String eventId, @NotNull String type, @NotNull String description, @NotNull Instant instant) throws SQLException;
- void addUserPrediction(@NotNull String username, @NotNull String streamerId, @NotNull String badge) throws SQLException;
+ int addUserPrediction(@NotNull String username, @NotNull String streamerId, @NotNull String badge) throws SQLException;
void cancelPrediction(@NotNull Event event) throws SQLException;
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java
index 6603ed79..22f5ab68 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java
@@ -31,12 +31,19 @@ public void createChannel(@NotNull String channelId, @NotNull String username) t
}
}
- @NotNull
@Override
- protected PreparedStatement getPredictionStmt(@NotNull Connection conn) throws SQLException{
- return conn.prepareStatement("""
- INSERT IGNORE INTO `UserPrediction`(`ChannelID`, `UserID`, `Badge`) VALUES (?,?,?)"""
- );
+ protected void addUserPrediction(@NotNull String channelId, int userId, @NotNull String badge) throws SQLException{
+ try(var conn = getConnection();
+ var predictionStatement = conn.prepareStatement("""
+ INSERT IGNORE INTO `UserPrediction`(`ChannelID`, `UserID`, `Badge`) VALUES (?,?,?)"""
+ )){
+
+ predictionStatement.setString(1, channelId);
+ predictionStatement.setInt(2, userId);
+ predictionStatement.setString(3, badge);
+
+ predictionStatement.executeUpdate();
+ }
}
@NotNull
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java
index 30c799fb..e04538c8 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java
@@ -32,7 +32,8 @@ public void addPrediction(@NotNull String channelId, @NotNull String eventId, @N
}
@Override
- public void addUserPrediction(@NotNull String username, @NotNull String streamerName, @NotNull String badge){
+ public int addUserPrediction(@NotNull String username, @NotNull String streamerName, @NotNull String badge){
+ return -1;
}
@Override
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
index 697befe4..8ae47fd7 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
@@ -34,13 +34,20 @@ public void createChannel(@NotNull String channelId, @NotNull String username) t
}
}
- @NotNull
@Override
- protected PreparedStatement getPredictionStmt(@NotNull Connection conn) throws SQLException{
- return conn.prepareStatement("""
- INSERT OR IGNORE INTO `UserPrediction`(`ChannelID`, `UserID`, `Badge`)
- VALUES(?,?,?)"""
- );
+ protected void addUserPrediction(@NotNull String channelId, int userId, @NotNull String badge) throws SQLException{
+ try(var conn = getConnection();
+ var predictionStatement = conn.prepareStatement("""
+ INSERT OR IGNORE INTO `UserPrediction`(`ChannelID`, `UserID`, `Badge`)
+ VALUES(?,?,?)"""
+ )){
+
+ predictionStatement.setString(1, channelId);
+ predictionStatement.setInt(2, userId);
+ predictionStatement.setString(3, badge);
+
+ predictionStatement.executeUpdate();
+ }
}
@NotNull
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index 6c0e4997..0f737a60 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -2,28 +2,40 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
+import fr.raksrinana.channelpointsminer.miner.api.ws.data.message.subtype.Event;
import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
import org.assertj.db.type.Changes;
import org.assertj.db.type.Table;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.function.Supplier;
import static org.assertj.db.api.Assertions.assertThat;
+import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+@ExtendWith(MockitoExtension.class)
class SQLiteDatabaseTest{
private static final String CHANNEL_ID = "channel-id";
private static final String CHANNEL_USERNAME = "channel-username";
private static final String USER_USERNAME = "user1";
+ private static final String EVENT_ID = "event-id";
+ private static final String EVENT_TITLE = "event-title";
+ private static final ZonedDateTime EVENT_CREATED_AT = ZonedDateTime.of(2022, 2, 15, 12, 0, 0, 0, ZoneId.systemDefault());
+ private static final ZonedDateTime EVENT_ENDED_AT = EVENT_CREATED_AT.plusDays(1);
private static final String ID_COL = "ID";
private static final String USERNAME_COL = "Username";
@@ -42,18 +54,26 @@ class SQLiteDatabaseTest{
private static final String RETURN_ON_INVESTMENT_COL = "ReturnOnInvestment";
private static final String USER_ID_COL = "UserID";
private static final String BADGE_COL = "Badge";
+ private static final String TITLE_COL = "Title";
+ private static final String EVENT_CREATED_COL = "EventCreated";
+ private static final String EVENT_ENDED_COL = "EventEnded";
+ private static final String CANCELED_COL = "Canceled";
+ private static final String OUTCOME_COL = "Outcome";
+ private static final String RETURN_RATIO_FOR_WIN_COL = "ReturnRatioForWIn";
@TempDir
private Path tempPath;
+ private final Supplier changesBalance = () -> new Changes(new Table(dataSource, "Balance"));
private SQLiteDatabase tested;
private HikariDataSource dataSource;
-
- private final Supplier tableBalance = () -> new Table(dataSource, BALANCE_COL);
- private final Supplier tableChannel = () -> new Table(dataSource, "Channel");
- private final Supplier tablePrediction = () -> new Table(dataSource, "Prediction");
- private final Supplier tablePredictionUser = () -> new Table(dataSource, "PredictionUser");
- private final Supplier tableUserPrediction = () -> new Table(dataSource, "UserPrediction");
+ private final Supplier changesChannel = () -> new Changes(new Table(dataSource, "Channel"));
+ private final Supplier changesPrediction = () -> new Changes(new Table(dataSource, "Prediction"));
+ private final Supplier changesPredictionUser = () -> new Changes(new Table(dataSource, "PredictionUser"));
+ private final Supplier changesUserPrediction = () -> new Changes(new Table(dataSource, "UserPrediction"));
+ private final Supplier changesResolvedPrediction = () -> new Changes(new Table(dataSource, "ResolvedPrediction"));
+ @Mock
+ private Event event;
@BeforeEach
void setUp() throws SQLException{
@@ -66,6 +86,12 @@ void setUp() throws SQLException{
tested = new SQLiteDatabase(dataSource);
tested.initDatabase();
+
+ lenient().when(event.getId()).thenReturn(EVENT_ID);
+ lenient().when(event.getChannelId()).thenReturn(CHANNEL_ID);
+ lenient().when(event.getTitle()).thenReturn(EVENT_TITLE);
+ lenient().when(event.getCreatedAt()).thenReturn(EVENT_CREATED_AT);
+ lenient().when(event.getEndedAt()).thenReturn(EVENT_ENDED_AT);
}
@AfterEach
@@ -75,17 +101,16 @@ void tearDown(){
@Test
void tablesAreCreated(){
- assertThat(tableBalance.get()).exists();
- assertThat(tableChannel.get()).exists();
- assertThat(tablePrediction.get()).exists();
- assertThat(tablePredictionUser.get()).exists();
- assertThat(tableUserPrediction.get()).exists();
+ assertThat(new Table(dataSource, "Balance")).exists();
+ assertThat(new Table(dataSource, "Channel")).exists();
+ assertThat(new Table(dataSource, "Prediction")).exists();
+ assertThat(new Table(dataSource, "PredictionUser")).exists();
+ assertThat(new Table(dataSource, "UserPrediction")).exists();
}
@Test
void newChannelIsInsertedIfNew() throws SQLException{
- var table = tableChannel.get();
- var changes = new Changes(table);
+ var changes = changesChannel.get();
changes.setStartPointNow();
tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
@@ -101,8 +126,7 @@ void newChannelIsInsertedIfNew() throws SQLException{
@Test
void oldChannelIsNotInsertedIfNew() throws SQLException{
try(var factory = mockStatic(TimeFactory.class)){
- var table = tableChannel.get();
- var changes = new Changes(table);
+ var changes = changesChannel.get();
var beforeChange = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
factory.when(TimeFactory::now).thenReturn(beforeChange);
@@ -122,8 +146,7 @@ void oldChannelIsNotInsertedIfNew() throws SQLException{
@Test
void updateChannelStatusTime() throws SQLException{
try(var factory = mockStatic(TimeFactory.class)){
- var table = tableChannel.get();
- var changes = new Changes(table);
+ var changes = changesChannel.get();
var beforeChange = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
factory.when(TimeFactory::now).thenReturn(beforeChange);
@@ -144,15 +167,14 @@ void updateChannelStatusTime() throws SQLException{
@Test
void addBalance() throws SQLException{
- var table = tableBalance.get();
- var change = new Changes(table);
+ var changes = changesBalance.get();
var firstInstant = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
- change.setStartPointNow();
+ changes.setStartPointNow();
tested.addBalance(CHANNEL_ID, 25, "Test1", firstInstant);
- change.setEndPointNow();
+ changes.setEndPointNow();
- assertThat(change).hasNumberOfChanges(1)
+ assertThat(changes).hasNumberOfChanges(1)
.changeOfCreation()
.column(ID_COL).valueAtEndPoint().isNotNull()
.column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
@@ -161,11 +183,11 @@ void addBalance() throws SQLException{
.column(REASON_COL).valueAtEndPoint().isEqualTo("Test1");
var secondInstant = firstInstant.plusSeconds(30);
- change.setStartPointNow();
+ changes.setStartPointNow();
tested.addBalance(CHANNEL_ID, 50, "Test2", secondInstant);
- change.setEndPointNow();
+ changes.setEndPointNow();
- assertThat(change).hasNumberOfChanges(1)
+ assertThat(changes).hasNumberOfChanges(1)
.changeOfCreation()
.column(ID_COL).valueAtEndPoint().isNotNull()
.column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
@@ -176,15 +198,14 @@ void addBalance() throws SQLException{
@Test
void addPrediction() throws SQLException{
- var table = tablePrediction.get();
- var change = new Changes(table);
+ var changes = changesPrediction.get();
var firstInstant = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
- change.setStartPointNow();
+ changes.setStartPointNow();
tested.addPrediction(CHANNEL_ID, "Event1", "Type1", "Description1", firstInstant);
- change.setEndPointNow();
+ changes.setEndPointNow();
- assertThat(change).hasNumberOfChanges(1)
+ assertThat(changes).hasNumberOfChanges(1)
.changeOfCreation()
.column(ID_COL).valueAtEndPoint().isNotNull()
.column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
@@ -194,11 +215,11 @@ void addPrediction() throws SQLException{
.column(DESCRIPTION_COL).valueAtEndPoint().isEqualTo("Description1");
var secondInstant = firstInstant.plusSeconds(30);
- change.setStartPointNow();
+ changes.setStartPointNow();
tested.addPrediction(CHANNEL_ID, "Event2", "Type2", "Description2", secondInstant);
- change.setEndPointNow();
+ changes.setEndPointNow();
- assertThat(change).hasNumberOfChanges(1)
+ assertThat(changes).hasNumberOfChanges(1)
.changeOfCreation()
.column(ID_COL).valueAtEndPoint().isNotNull()
.column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
@@ -210,20 +231,18 @@ void addPrediction() throws SQLException{
@Test
void addPredictionFromNewUser() throws SQLException{
- var tableUserPrediction = this.tableUserPrediction.get();
- var tablePredictionUser = this.tablePredictionUser.get();
- var changeUserPrediction = new Changes(tableUserPrediction);
- var changePredictionUser = new Changes(tablePredictionUser);
+ var changesUserPrediction = this.changesUserPrediction.get();
+ var changesPredictionUser = this.changesPredictionUser.get();
- changeUserPrediction.setStartPointNow();
- changePredictionUser.setStartPointNow();
- tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
- changeUserPrediction.setEndPointNow();
- changePredictionUser.setEndPointNow();
+ changesUserPrediction.setStartPointNow();
+ changesPredictionUser.setStartPointNow();
+ var userId = tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ changesUserPrediction.setEndPointNow();
+ changesPredictionUser.setEndPointNow();
- assertThat(changePredictionUser).hasNumberOfChanges(1)
+ assertThat(changesPredictionUser).hasNumberOfChanges(1)
.changeOfCreation()
- .column(ID_COL).valueAtEndPoint().isNotNull()
+ .column(ID_COL).valueAtEndPoint().isEqualTo(userId)
.column(USERNAME_COL).valueAtEndPoint().isEqualTo(USER_USERNAME)
.column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
.column(PREDICTION_CNT_COL).valueAtEndPoint().isEqualTo(0)
@@ -231,7 +250,7 @@ void addPredictionFromNewUser() throws SQLException{
.column(WIN_RATE_COL).valueAtEndPoint().isEqualTo(0D)
.column(RETURN_ON_INVESTMENT_COL).valueAtEndPoint().isEqualTo(0D);
- assertThat(changeUserPrediction).hasNumberOfChanges(1)
+ assertThat(changesUserPrediction).hasNumberOfChanges(1)
.changeOfCreation()
.column(USER_ID_COL).valueAtEndPoint().isNotNull()
.column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
@@ -240,21 +259,89 @@ void addPredictionFromNewUser() throws SQLException{
@Test
void addPredictionFromExistingUser() throws SQLException{
- var tableUserPrediction = this.tableUserPrediction.get();
- var tablePredictionUser = this.tablePredictionUser.get();
- var changeUserPrediction = new Changes(tableUserPrediction);
- var changePredictionUser = new Changes(tablePredictionUser);
+ var changesUserPrediction = this.changesUserPrediction.get();
+ var changesPredictionUser = this.changesPredictionUser.get();
tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
- changeUserPrediction.setStartPointNow();
- changePredictionUser.setStartPointNow();
+ changesUserPrediction.setStartPointNow();
+ changesPredictionUser.setStartPointNow();
tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B2"); //This should be impossible, we can't change badge
- changeUserPrediction.setEndPointNow();
- changePredictionUser.setEndPointNow();
+ changesUserPrediction.setEndPointNow();
+ changesPredictionUser.setEndPointNow();
+
+ assertThat(changesPredictionUser).hasNumberOfChanges(0);
+ assertThat(changesUserPrediction).hasNumberOfChanges(0);
+ }
+
+ @Test
+ void cancelPrediction() throws SQLException{
+ var changes = changesResolvedPrediction.get();
+
+ changes.setStartPointNow();
+ tested.cancelPrediction(event);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(EVENT_ID_COL).valueAtEndPoint().isEqualTo(EVENT_ID)
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(TITLE_COL).valueAtEndPoint().isEqualTo(EVENT_TITLE)
+ .column(EVENT_CREATED_COL).valueAtEndPoint().isEqualTo(EVENT_CREATED_AT.toLocalDateTime())
+ .column(EVENT_ENDED_COL).valueAtEndPoint().isEqualTo(EVENT_ENDED_AT.toLocalDateTime())
+ .column(CANCELED_COL).valueAtEndPoint().isEqualTo(1)
+ .column(OUTCOME_COL).valueAtEndPoint().isNull()
+ .column(BADGE_COL).valueAtEndPoint().isNull()
+ .column(RETURN_RATIO_FOR_WIN_COL).valueAtEndPoint().isNull();
+ }
+
+ @Test
+ void cancelPredictionWithNoEndDate() throws SQLException{
+ try(var factory = mockStatic(TimeFactory.class)){
+ var endInstant = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
+ factory.when(TimeFactory::now).thenReturn(endInstant);
+
+ var changes = changesResolvedPrediction.get();
+
+ when(event.getEndedAt()).thenReturn(null);
+
+ changes.setStartPointNow();
+ tested.cancelPrediction(event);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(EVENT_ID_COL).valueAtEndPoint().isEqualTo(EVENT_ID)
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(TITLE_COL).valueAtEndPoint().isEqualTo(EVENT_TITLE)
+ .column(EVENT_CREATED_COL).valueAtEndPoint().isEqualTo(EVENT_CREATED_AT.toLocalDateTime())
+ .column(EVENT_ENDED_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(endInstant))
+ .column(CANCELED_COL).valueAtEndPoint().isEqualTo(1)
+ .column(OUTCOME_COL).valueAtEndPoint().isNull()
+ .column(BADGE_COL).valueAtEndPoint().isNull()
+ .column(RETURN_RATIO_FOR_WIN_COL).valueAtEndPoint().isNull();
+ }
+ }
+
+ @Test
+ void cancelPredictionClearsUserPredictions() throws SQLException{
+ var changes = changesUserPrediction.get();
+
+ var userId1 = tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ var userId2 = tested.addUserPrediction("user-2", CHANNEL_ID, "B2");
+ tested.addUserPrediction(USER_USERNAME, "other-channel", "B1");
+
+ changes.setStartPointNow();
+ tested.cancelPrediction(event);
+ changes.setEndPointNow();
- assertThat(changePredictionUser).hasNumberOfChanges(0);
- assertThat(changeUserPrediction).hasNumberOfChanges(0);
+ assertThat(changes).hasNumberOfChanges(2)
+ .changeOfDeletion()
+ .column(USER_ID_COL).valueAtStartPoint().isEqualTo(userId1)
+ .column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID)
+ .changeOfDeletion()
+ .column(USER_ID_COL).valueAtStartPoint().isEqualTo(userId2)
+ .column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID);
}
private LocalDateTime getExpectedTimestamp(Instant instant){
From 885c2e90967f079e4ca198160554f21ca16b53b6 Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sun, 4 Sep 2022 10:43:35 +0200
Subject: [PATCH 8/9] Add resolve prediction test
---
.../miner/database/BaseDatabase.java | 67 +++-------
.../miner/database/MariaDBDatabase.java | 47 +++++--
.../miner/database/SQLiteDatabase.java | 51 +++++--
.../miner/database/SQLiteDatabaseTest.java | 124 ++++++++++++++++--
4 files changed, 203 insertions(+), 86 deletions(-)
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
index 54615b6b..af3a43b2 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
@@ -176,65 +176,28 @@ public void cancelPrediction(@NotNull Event event) throws SQLException{
public void resolvePrediction(@NotNull Event event, @NotNull String outcome, @NotNull String badge, double returnRatioForWin) throws SQLException{
var ended = Optional.ofNullable(event.getEndedAt()).map(ZonedDateTime::toInstant).orElseGet(TimeFactory::now);
+ resolveUserPredictions(returnRatioForWin, event.getChannelId(), badge);
+
try(var conn = getConnection();
- var getOpenPredictionStmt = conn.prepareStatement("""
- SELECT * FROM `UserPrediction` WHERE `ChannelID`=?""");
- var updatePredictionUserStmt = getUpdatePredictionUserStmt(conn);
var addResolvedPredictionStmt = conn.prepareStatement("""
INSERT INTO `ResolvedPrediction`(`EventID`,`ChannelID`, `Title`,`EventCreated`,`EventEnded`,`Canceled`,`Outcome`,`Badge`,`ReturnRatioForWin`)
- VALUES (?,?,?,?,?,false,?,?,?)""");
- var removePredictionsStmt = getDeleteUserPredictionsForChannelStmt(conn)
+ VALUES (?,?,?,?,?,false,?,?,?)""")
){
- conn.setAutoCommit(false);
-
- try{
- //Get user predictions, determine win/lose and update
- double returnOnInvestment = returnRatioForWin - 1;
- getOpenPredictionStmt.setString(1, event.getChannelId());
- try(var result = getOpenPredictionStmt.executeQuery()){
- while(result.next()){
- var userPrediction = Converters.convertUserPrediction(result);
- if(badge.equals(userPrediction.getBadge())){
- updatePredictionUserStmt.setInt(1, 1);
- updatePredictionUserStmt.setDouble(2, returnOnInvestment);
- }
- else{
- updatePredictionUserStmt.setInt(1, 0);
- updatePredictionUserStmt.setDouble(2, -1);
- }
- updatePredictionUserStmt.setInt(3, userPrediction.getUserId());
- updatePredictionUserStmt.setString(4, userPrediction.getChannelId());
- updatePredictionUserStmt.addBatch();
- }
- updatePredictionUserStmt.executeBatch();
- }
-
- //Add the resolved prediction
- addResolvedPredictionStmt.setString(1, event.getId());
- addResolvedPredictionStmt.setString(2, event.getChannelId());
- addResolvedPredictionStmt.setString(3, event.getTitle());
- addResolvedPredictionStmt.setTimestamp(4, Timestamp.from(event.getCreatedAt().toInstant()));
- addResolvedPredictionStmt.setTimestamp(5, Timestamp.from(ended));
- addResolvedPredictionStmt.setString(6, outcome);
- addResolvedPredictionStmt.setString(7, badge);
- addResolvedPredictionStmt.setDouble(8, returnRatioForWin);
- addResolvedPredictionStmt.executeUpdate();
-
- //Remove Predictions
- removePredictionsStmt.setString(1, event.getChannelId());
- removePredictionsStmt.executeUpdate();
-
- conn.commit();
- }
- catch(SQLException e){
- conn.rollback();
- throw e;
- }
+ addResolvedPredictionStmt.setString(1, event.getId());
+ addResolvedPredictionStmt.setString(2, event.getChannelId());
+ addResolvedPredictionStmt.setString(3, event.getTitle());
+ addResolvedPredictionStmt.setTimestamp(4, Timestamp.from(event.getCreatedAt().toInstant()));
+ addResolvedPredictionStmt.setTimestamp(5, Timestamp.from(ended));
+ addResolvedPredictionStmt.setString(6, outcome);
+ addResolvedPredictionStmt.setString(7, badge);
+ addResolvedPredictionStmt.setDouble(8, returnRatioForWin);
+ addResolvedPredictionStmt.executeUpdate();
}
+
+ deleteUserPredictionsForChannel(event.getChannelId());
}
- @NotNull
- protected abstract PreparedStatement getUpdatePredictionUserStmt(@NotNull Connection conn) throws SQLException;
+ protected abstract void resolveUserPredictions(double returnRatioForWin, @NotNull String channelId, @NotNull String badge) throws SQLException;
@Override
public void deleteUserPredictions() throws SQLException{
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java
index 22f5ab68..cc755374 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/MariaDBDatabase.java
@@ -1,9 +1,8 @@
package fr.raksrinana.channelpointsminer.miner.database;
import com.zaxxer.hikari.HikariDataSource;
+import fr.raksrinana.channelpointsminer.miner.database.converter.Converters;
import org.jetbrains.annotations.NotNull;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.SQLException;
public class MariaDBDatabase extends BaseDatabase{
@@ -46,16 +45,40 @@ protected void addUserPrediction(@NotNull String channelId, int userId, @NotNull
}
}
- @NotNull
@Override
- protected PreparedStatement getUpdatePredictionUserStmt(@NotNull Connection conn) throws SQLException{
- return conn.prepareStatement("""
- UPDATE `PredictionUser`
- SET
- `PredictionCnt`=`PredictionCnt`+1,
- `WinCnt`=`WinCnt`+?,
- `WinRate`=`WinCnt`/`PredictionCnt`,
- `ReturnOnInvestment`=`ReturnOnInvestment`+?
- WHERE `ID`=? AND `ChannelID`=?""");
+ protected void resolveUserPredictions(double returnRatioForWin, @NotNull String channelId, @NotNull String badge) throws SQLException{
+ try(var conn = getConnection();
+ var getOpenPredictionStmt = conn.prepareStatement("""
+ SELECT * FROM `UserPrediction` WHERE `ChannelID`=?""");
+ var updatePredictionUserStmt = conn.prepareStatement("""
+ UPDATE `PredictionUser`
+ SET
+ `PredictionCnt`=`PredictionCnt`+1,
+ `WinCnt`=`WinCnt`+?,
+ `WinRate`=`WinCnt`/`PredictionCnt`,
+ `ReturnOnInvestment`=`ReturnOnInvestment`+?
+ WHERE `ID`=? AND `ChannelID`=?""")
+ ){
+ double returnOnInvestment = returnRatioForWin - 1;
+
+ getOpenPredictionStmt.setString(1, channelId);
+ try(var result = getOpenPredictionStmt.executeQuery()){
+ while(result.next()){
+ var userPrediction = Converters.convertUserPrediction(result);
+ if(badge.equals(userPrediction.getBadge())){
+ updatePredictionUserStmt.setInt(1, 1);
+ updatePredictionUserStmt.setDouble(2, returnOnInvestment);
+ }
+ else{
+ updatePredictionUserStmt.setInt(1, 0);
+ updatePredictionUserStmt.setDouble(2, -1);
+ }
+ updatePredictionUserStmt.setInt(3, userPrediction.getUserId());
+ updatePredictionUserStmt.setString(4, userPrediction.getChannelId());
+ updatePredictionUserStmt.addBatch();
+ }
+ updatePredictionUserStmt.executeBatch();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
index 8ae47fd7..99777ad6 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabase.java
@@ -1,10 +1,9 @@
package fr.raksrinana.channelpointsminer.miner.database;
import com.zaxxer.hikari.HikariDataSource;
+import fr.raksrinana.channelpointsminer.miner.database.converter.Converters;
import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
import org.jetbrains.annotations.NotNull;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
@@ -50,18 +49,42 @@ protected void addUserPrediction(@NotNull String channelId, int userId, @NotNull
}
}
- @NotNull
@Override
- protected PreparedStatement getUpdatePredictionUserStmt(@NotNull Connection conn) throws SQLException{
- return conn.prepareStatement("""
- WITH wi AS (SELECT ? AS n)
- UPDATE `PredictionUser`
- SET
- `PredictionCnt`=`PredictionCnt`+1,
- `WinCnt`=`WinCnt`+wi.n,
- `WinRate`=CAST((`WinCnt`+wi.n) AS REAL)/(`PredictionCnt`+1),
- `ReturnOnInvestment`=`ReturnOnInvestment`+?
- FROM wi
- WHERE `ID`=? AND `ChannelID`=?""");
+ protected void resolveUserPredictions(double returnRatioForWin, @NotNull String channelId, @NotNull String badge) throws SQLException{
+ try(var conn = getConnection();
+ var getOpenPredictionStmt = conn.prepareStatement("""
+ SELECT * FROM `UserPrediction` WHERE `ChannelID`=?""");
+ var updatePredictionUserStmt = conn.prepareStatement("""
+ WITH wi AS (SELECT ? AS n)
+ UPDATE `PredictionUser`
+ SET
+ `PredictionCnt`=`PredictionCnt`+1,
+ `WinCnt`=`WinCnt`+wi.n,
+ `WinRate`=CAST((`WinCnt`+wi.n) AS REAL)/(`PredictionCnt`+1),
+ `ReturnOnInvestment`=`ReturnOnInvestment`+?
+ FROM wi
+ WHERE `ID`=? AND `ChannelID`=?""")
+ ){
+ double returnOnInvestment = returnRatioForWin - 1;
+
+ getOpenPredictionStmt.setString(1, channelId);
+ try(var result = getOpenPredictionStmt.executeQuery()){
+ while(result.next()){
+ var userPrediction = Converters.convertUserPrediction(result);
+ if(badge.equals(userPrediction.getBadge())){
+ updatePredictionUserStmt.setInt(1, 1);
+ updatePredictionUserStmt.setDouble(2, returnOnInvestment);
+ }
+ else{
+ updatePredictionUserStmt.setInt(1, 0);
+ updatePredictionUserStmt.setDouble(2, -1);
+ }
+ updatePredictionUserStmt.setInt(3, userPrediction.getUserId());
+ updatePredictionUserStmt.setString(4, userPrediction.getChannelId());
+ updatePredictionUserStmt.addBatch();
+ }
+ updatePredictionUserStmt.executeBatch();
+ }
+ }
}
}
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index 0f737a60..5ad7f0fc 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -63,17 +63,18 @@ class SQLiteDatabaseTest{
@TempDir
private Path tempPath;
- private final Supplier changesBalance = () -> new Changes(new Table(dataSource, "Balance"));
+ @Mock
+ private Event event;
private SQLiteDatabase tested;
private HikariDataSource dataSource;
- private final Supplier changesChannel = () -> new Changes(new Table(dataSource, "Channel"));
- private final Supplier changesPrediction = () -> new Changes(new Table(dataSource, "Prediction"));
- private final Supplier changesPredictionUser = () -> new Changes(new Table(dataSource, "PredictionUser"));
- private final Supplier changesUserPrediction = () -> new Changes(new Table(dataSource, "UserPrediction"));
- private final Supplier changesResolvedPrediction = () -> new Changes(new Table(dataSource, "ResolvedPrediction"));
- @Mock
- private Event event;
+
+ private Supplier changesBalance;
+ private Supplier changesChannel;
+ private Supplier changesPrediction;
+ private Supplier changesPredictionUser;
+ private Supplier changesUserPrediction;
+ private Supplier changesResolvedPrediction;
@BeforeEach
void setUp() throws SQLException{
@@ -87,6 +88,13 @@ void setUp() throws SQLException{
tested.initDatabase();
+ changesBalance = () -> new Changes(new Table(dataSource, "Balance"));
+ changesChannel = () -> new Changes(new Table(dataSource, "Channel"));
+ changesPrediction = () -> new Changes(new Table(dataSource, "Prediction"));
+ changesPredictionUser = () -> new Changes(new Table(dataSource, "PredictionUser"));
+ changesUserPrediction = () -> new Changes(new Table(dataSource, "UserPrediction"));
+ changesResolvedPrediction = () -> new Changes(new Table(dataSource, "ResolvedPrediction"));
+
lenient().when(event.getId()).thenReturn(EVENT_ID);
lenient().when(event.getChannelId()).thenReturn(CHANNEL_ID);
lenient().when(event.getTitle()).thenReturn(EVENT_TITLE);
@@ -344,6 +352,106 @@ void cancelPredictionClearsUserPredictions() throws SQLException{
.column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID);
}
+ @Test
+ void resolvePrediction() throws SQLException{
+ var changes = changesResolvedPrediction.get();
+
+ changes.setStartPointNow();
+ tested.resolvePrediction(event, "Outcome1", "B1", 1.5D);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(EVENT_ID_COL).valueAtEndPoint().isEqualTo(EVENT_ID)
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(TITLE_COL).valueAtEndPoint().isEqualTo(EVENT_TITLE)
+ .column(EVENT_CREATED_COL).valueAtEndPoint().isEqualTo(EVENT_CREATED_AT.toLocalDateTime())
+ .column(EVENT_ENDED_COL).valueAtEndPoint().isEqualTo(EVENT_ENDED_AT.toLocalDateTime())
+ .column(CANCELED_COL).valueAtEndPoint().isEqualTo(0)
+ .column(OUTCOME_COL).valueAtEndPoint().isEqualTo("Outcome1")
+ .column(BADGE_COL).valueAtEndPoint().isEqualTo("B1")
+ .column(RETURN_RATIO_FOR_WIN_COL).valueAtEndPoint().isEqualTo(1.5D);
+ }
+
+ @Test
+ void resolvePredictionWithNoEndDate() throws SQLException{
+ try(var factory = mockStatic(TimeFactory.class)){
+ var endInstant = Instant.now().with(ChronoField.NANO_OF_SECOND, 0);
+ factory.when(TimeFactory::now).thenReturn(endInstant);
+
+ var changes = changesResolvedPrediction.get();
+
+ when(event.getEndedAt()).thenReturn(null);
+
+ changes.setStartPointNow();
+ tested.resolvePrediction(event, "Outcome1", "B1", 1.5D);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(1)
+ .changeOfCreation()
+ .column(EVENT_ID_COL).valueAtEndPoint().isEqualTo(EVENT_ID)
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(TITLE_COL).valueAtEndPoint().isEqualTo(EVENT_TITLE)
+ .column(EVENT_CREATED_COL).valueAtEndPoint().isEqualTo(EVENT_CREATED_AT.toLocalDateTime())
+ .column(EVENT_ENDED_COL).valueAtEndPoint().isEqualTo(getExpectedTimestamp(endInstant))
+ .column(CANCELED_COL).valueAtEndPoint().isEqualTo(0)
+ .column(OUTCOME_COL).valueAtEndPoint().isEqualTo("Outcome1")
+ .column(BADGE_COL).valueAtEndPoint().isEqualTo("B1")
+ .column(RETURN_RATIO_FOR_WIN_COL).valueAtEndPoint().isEqualTo(1.5D);
+ }
+ }
+
+ @Test
+ void resolvePredictionClearsUserPredictions() throws SQLException{
+ var changes = changesUserPrediction.get();
+
+ var userId1 = tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ var userId2 = tested.addUserPrediction("user-2", CHANNEL_ID, "B2");
+ tested.addUserPrediction(USER_USERNAME, "other-channel", "B1");
+
+ changes.setStartPointNow();
+ tested.resolvePrediction(event, "Outcome1", "B1", 1.5D);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(2)
+ .changeOfDeletion()
+ .column(USER_ID_COL).valueAtStartPoint().isEqualTo(userId1)
+ .column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID)
+ .changeOfDeletion()
+ .column(USER_ID_COL).valueAtStartPoint().isEqualTo(userId2)
+ .column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID);
+ }
+
+ @Test
+ void resolvePredictionUpdatesPredictionUsers() throws SQLException{
+ var changes = changesPredictionUser.get();
+
+ var userId1 = tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ var userId2 = tested.addUserPrediction("user-2", CHANNEL_ID, "B2");
+
+ changes.setStartPointNow();
+ tested.resolvePrediction(event, "Outcome1", "B1", 1.5D);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(2)
+ .changeOfModification()
+ .column(ID_COL).valueAtEndPoint().isEqualTo(userId1)
+ .column(USERNAME_COL).valueAtEndPoint().isEqualTo(USER_USERNAME)
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(PREDICTION_CNT_COL).valueAtEndPoint().isEqualTo(1)
+ .column(WIN_CNT_COL).valueAtEndPoint().isEqualTo(1)
+ .column(WIN_RATE_COL).valueAtEndPoint().isEqualTo(1D)
+ .column(RETURN_ON_INVESTMENT_COL).valueAtEndPoint().isEqualTo(0.5D)
+ .changeOfModification()
+ .column(ID_COL).valueAtEndPoint().isEqualTo(userId2)
+ .column(USERNAME_COL).valueAtEndPoint().isEqualTo("user-2")
+ .column(CHANNEL_ID_COL).valueAtEndPoint().isEqualTo(CHANNEL_ID)
+ .column(PREDICTION_CNT_COL).valueAtEndPoint().isEqualTo(1)
+ .column(WIN_CNT_COL).valueAtEndPoint().isEqualTo(0)
+ .column(WIN_RATE_COL).valueAtEndPoint().isEqualTo(0D)
+ .column(RETURN_ON_INVESTMENT_COL).valueAtEndPoint().isEqualTo(-1D);
+ }
+
private LocalDateTime getExpectedTimestamp(Instant instant){
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
From 8770fa1e5130074e3e0ecbdd9fe6b5bfddec17c2 Mon Sep 17 00:00:00 2001
From: Thomas Couchoud <1688389+RakSrinaNa@users.noreply.github.com>
Date: Sun, 4 Sep 2022 11:09:54 +0200
Subject: [PATCH 9/9] More tests
---
.../miner/database/BaseDatabase.java | 4 +-
.../miner/database/IDatabase.java | 8 +-
.../miner/database/NoOpDatabase.java | 9 +--
.../model/prediction/OutcomeStatistic.java | 27 +++----
.../miner/factory/MinerFactory.java | 2 +-
.../miner/database/SQLiteDatabaseTest.java | 81 +++++++++++++++++++
.../miner/factory/MinerFactoryTest.java | 2 +-
7 files changed, 104 insertions(+), 29 deletions(-)
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
index af3a43b2..16a843a7 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/BaseDatabase.java
@@ -200,7 +200,7 @@ public void resolvePrediction(@NotNull Event event, @NotNull String outcome, @No
protected abstract void resolveUserPredictions(double returnRatioForWin, @NotNull String channelId, @NotNull String badge) throws SQLException;
@Override
- public void deleteUserPredictions() throws SQLException{
+ public void deleteAllUserPredictions() throws SQLException{
log.debug("Removing all user predictions.");
try(var conn = getConnection();
var statement = conn.prepareStatement("DELETE FROM `UserPrediction`")){
@@ -256,7 +256,7 @@ public Collection getOutcomeStatisticsForChannel(@NotNull Stri
INNER JOIN `PredictionUser` AS pu
ON up.`UserID`=pu.`ID` AND up.`ChannelID` = pu.`ChannelID`
WHERE up.`ChannelID`=?
- AND `PredictionCnt`>?
+ AND `PredictionCnt`>=?
GROUP BY `Badge`"""
)){
statement.setString(1, channelId);
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
index 643fb933..4bb01fb3 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/IDatabase.java
@@ -19,6 +19,9 @@ public interface IDatabase extends AutoCloseable{
void updateChannelStatusTime(@NotNull String channelId, @NotNull Instant instant) throws SQLException;
+ @NotNull
+ Optional getStreamerIdFromName(@NotNull String channelName) throws SQLException;
+
void addBalance(@NotNull String channelId, int balance, @Nullable String reason, @NotNull Instant instant) throws SQLException;
void addPrediction(@NotNull String channelId, @NotNull String eventId, @NotNull String type, @NotNull String description, @NotNull Instant instant) throws SQLException;
@@ -29,13 +32,10 @@ public interface IDatabase extends AutoCloseable{
void resolvePrediction(@NotNull Event event, @NotNull String outcome, @NotNull String badge, double returnOnInvestment) throws SQLException;
- void deleteUserPredictions() throws SQLException;
+ void deleteAllUserPredictions() throws SQLException;
void deleteUserPredictionsForChannel(@NotNull String channelId) throws SQLException;
@NotNull
Collection getOutcomeStatisticsForChannel(@NotNull String channelId, int minBetsPlacedByUser) throws SQLException;
-
- @NotNull
- Optional getStreamerIdFromName(@NotNull String channelName) throws SQLException;
}
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java
index e04538c8..91358d15 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/NoOpDatabase.java
@@ -4,7 +4,6 @@
import fr.raksrinana.channelpointsminer.miner.database.model.prediction.OutcomeStatistic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.sql.SQLException;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
@@ -45,7 +44,9 @@ public void resolvePrediction(@NotNull Event event, @NotNull String outcome, @No
}
@Override
- public void deleteUserPredictions(){
+ @NotNull
+ public Optional getStreamerIdFromName(@NotNull String channelName){
+ return Optional.empty();
}
@Override
@@ -63,8 +64,6 @@ public void close(){
}
@Override
- @NotNull
- public Optional getStreamerIdFromName(@NotNull String channelName) throws SQLException{
- return Optional.empty();
+ public void deleteAllUserPredictions(){
}
}
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/model/prediction/OutcomeStatistic.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/model/prediction/OutcomeStatistic.java
index d400722a..e6852fc6 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/model/prediction/OutcomeStatistic.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/database/model/prediction/OutcomeStatistic.java
@@ -1,24 +1,19 @@
package fr.raksrinana.channelpointsminer.miner.database.model.prediction;
-import lombok.AllArgsConstructor;
import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
+import lombok.Data;
import org.jetbrains.annotations.NotNull;
-@Getter
-@NoArgsConstructor
-@AllArgsConstructor
+@Data
@Builder
-@ToString
public class OutcomeStatistic{
-
- @NotNull
- private String badge;
- private int userCnt;
- private double averageWinRate;
- private double averageUserBetsPlaced;
- private double averageUserWins;
- private double averageReturnOnInvestment;
+
+ @NotNull
+ private final String badge;
+ private final int userCnt;
+ private final double averageWinRate;
+ private final double averageUserBetsPlaced;
+ private final double averageUserWins;
+ private final double averageReturnOnInvestment;
}
+
diff --git a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactory.java b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactory.java
index 21b5becc..1ee13efa 100644
--- a/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactory.java
+++ b/miner/src/main/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactory.java
@@ -46,7 +46,7 @@ public static Miner create(@NotNull AccountConfiguration config){
throw new IllegalStateException("Analytics is enabled but no database is defined");
}
- database.deleteUserPredictions();
+ database.deleteAllUserPredictions();
miner.addEventHandler(DatabaseFactory.createDatabaseHandler(database));
}
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
index 5ad7f0fc..e54e8b9f 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/database/SQLiteDatabaseTest.java
@@ -3,7 +3,9 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import fr.raksrinana.channelpointsminer.miner.api.ws.data.message.subtype.Event;
+import fr.raksrinana.channelpointsminer.miner.database.model.prediction.OutcomeStatistic;
import fr.raksrinana.channelpointsminer.miner.factory.TimeFactory;
+import org.assertj.core.api.Assertions;
import org.assertj.db.type.Changes;
import org.assertj.db.type.Table;
import org.mockito.Mock;
@@ -173,6 +175,17 @@ void updateChannelStatusTime() throws SQLException{
}
}
+ @Test
+ void getStreamerIdFromName() throws SQLException{
+ tested.createChannel("other-id", "other-name");
+
+ Assertions.assertThat(tested.getStreamerIdFromName(CHANNEL_USERNAME)).isEmpty();
+
+ tested.createChannel(CHANNEL_ID, CHANNEL_USERNAME);
+
+ Assertions.assertThat(tested.getStreamerIdFromName(CHANNEL_USERNAME)).isPresent().get().isEqualTo(CHANNEL_ID);
+ }
+
@Test
void addBalance() throws SQLException{
var changes = changesBalance.get();
@@ -452,6 +465,74 @@ void resolvePredictionUpdatesPredictionUsers() throws SQLException{
.column(RETURN_ON_INVESTMENT_COL).valueAtEndPoint().isEqualTo(-1D);
}
+ @Test
+ void getOutcomeStatisticsForChannel() throws SQLException{
+ tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ tested.addUserPrediction("user-2", CHANNEL_ID, "B2");
+
+ tested.resolvePrediction(event, "Outcome1", "B1", 1.5D);
+
+ tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B3");
+ tested.addUserPrediction("user-2", CHANNEL_ID, "B4");
+ tested.addUserPrediction("user-3", CHANNEL_ID, "B5");
+
+ Assertions.assertThat(tested.getOutcomeStatisticsForChannel(CHANNEL_ID, 1))
+ .containsExactlyInAnyOrder(
+ OutcomeStatistic.builder()
+ .badge("B3")
+ .userCnt(1)
+ .averageWinRate(1D)
+ .averageUserBetsPlaced(1D)
+ .averageUserWins(1D)
+ .averageReturnOnInvestment(0.5)
+ .build(),
+ OutcomeStatistic.builder()
+ .badge("B4")
+ .userCnt(1)
+ .averageWinRate(0D)
+ .averageUserBetsPlaced(1D)
+ .averageUserWins(0D)
+ .averageReturnOnInvestment(-1D)
+ .build()
+ );
+ }
+
+ @Test
+ void deleteAllUserPredictions() throws SQLException{
+ var changes = changesUserPrediction.get();
+
+ tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ tested.addUserPrediction(USER_USERNAME, "channel-2", "B2");
+ tested.addUserPrediction("user-2", CHANNEL_ID, "B2");
+
+ changes.setStartPointNow();
+ tested.deleteAllUserPredictions();
+ changes.setEndPointNow();
+
+ assertThat(changes).ofDeletion().hasNumberOfChanges(3);
+ }
+
+ @Test
+ void deleteUserPredictionsForChannel() throws SQLException{
+ var changes = changesUserPrediction.get();
+
+ var userId1 = tested.addUserPrediction(USER_USERNAME, CHANNEL_ID, "B1");
+ tested.addUserPrediction(USER_USERNAME, "channel-2", "B2");
+ var userId3 = tested.addUserPrediction("user-2", CHANNEL_ID, "B2");
+
+ changes.setStartPointNow();
+ tested.deleteUserPredictionsForChannel(CHANNEL_ID);
+ changes.setEndPointNow();
+
+ assertThat(changes).hasNumberOfChanges(2)
+ .changeOfDeletion()
+ .column(USER_ID_COL).valueAtStartPoint().isEqualTo(userId1)
+ .column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID)
+ .changeOfDeletion()
+ .column(USER_ID_COL).valueAtStartPoint().isEqualTo(userId3)
+ .column(CHANNEL_ID_COL).valueAtStartPoint().isEqualTo(CHANNEL_ID);
+ }
+
private LocalDateTime getExpectedTimestamp(Instant instant){
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
diff --git a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactoryTest.java b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactoryTest.java
index d82dadd4..9e2ed200 100644
--- a/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactoryTest.java
+++ b/miner/src/test/java/fr/raksrinana/channelpointsminer/miner/factory/MinerFactoryTest.java
@@ -150,7 +150,7 @@ void nominalWithAnalytics() throws SQLException{
.hasAtLeastOneElementOfType(LoggerEventListener.class)
.hasAtLeastOneElementOfType(DatabaseEventHandler.class);
- verify(database).deleteUserPredictions();
+ verify(database).deleteAllUserPredictions();
miner.close();
}