Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,17 @@ public class HelpConfig extends GuildConfigItem {
private double thankExperience = 3;

/**
* The amount that should be subtracted from every Help Account each day.
* The minimum total amount of XP that should be subtracted from every Help Account each day.
*/
private int minDailyExperienceSubtraction = 1;

/**
* The maximum total amount of XP that should be subtracted from every Help Account each day.
*/
private int maxDailyExperienceSubtraction = 50;

/**
* The percentage of XP that should be subtracted from every Help Account each day.
*/
private double dailyExperienceSubtraction = 5;

Expand Down
8 changes: 7 additions & 1 deletion src/main/java/net/discordjug/javabot/data/h2db/DbHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ private static boolean shouldInitSchema(String jdbcUrl) {
return shouldInitSchema;
}

private static void initializeSchema(HikariDataSource dataSource) throws IOException, SQLException {
/**
* Initializes the schema of the database by running all SQL statements from the schema.sql script.
* @param dataSource the {@link DataSource} to connect to the DB
* @throws IOException if an error happened while loading the schema.sql
* @throws SQLException if any SQL error happened
*/
public static void initializeSchema(DataSource dataSource) throws IOException, SQLException {
try (InputStream is = DbHelper.class.getClassLoader().getResourceAsStream("database/schema.sql")) {
if (is == null) throw new IOException("Could not load schema.sql.");
List<String> queries = Arrays.stream(new String(is.readAllBytes()).split(";"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.RequiredArgsConstructor;
import net.discordjug.javabot.data.config.BotConfig;
import net.discordjug.javabot.data.config.guild.HelpConfig;
import net.discordjug.javabot.data.h2db.DbHelper;
import net.discordjug.javabot.systems.help.dao.HelpAccountRepository;
import net.discordjug.javabot.util.ExceptionLogger;
Expand Down Expand Up @@ -31,9 +32,12 @@ public class HelpExperienceJob {
public void execute() {
asyncPool.execute(() -> {
try {
// just get the config for the first guild the bot is in, as it's not designed to work in multiple guilds anyway
HelpConfig helpConfig = botConfig.get(jda.getGuilds().get(0)).getHelpConfig();
helpAccountRepository.removeExperienceFromAllAccounts(
// just get the config for the first guild the bot is in, as it's not designed to work in multiple guilds anyway
botConfig.get(jda.getGuilds().get(0)).getHelpConfig().getDailyExperienceSubtraction(), 1, 50);
helpConfig.getDailyExperienceSubtraction(),
helpConfig.getMinDailyExperienceSubtraction(),
helpConfig.getMaxDailyExperienceSubtraction());
} catch (DataAccessException e) {
ExceptionLogger.capture(e, DbHelper.class.getSimpleName());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.discordjug.javabot.data.config.BotConfig;
import net.discordjug.javabot.systems.help.model.HelpAccount;

import org.jetbrains.annotations.NotNull;
Expand All @@ -26,7 +25,6 @@
@Repository
public class HelpAccountRepository {
private final JdbcTemplate jdbcTemplate;
private final BotConfig botConfig;

/**
* Inserts a new {@link HelpAccount}.
Expand Down Expand Up @@ -105,7 +103,7 @@ public int getTotalAccounts() throws DataAccessException {
* @throws DataAccessException If an error occurs.
*/
public void removeExperienceFromAllAccounts(double change, int min, int max) throws DataAccessException {
long rows = jdbcTemplate.execute("UPDATE help_account SET experience = GREATEST(experience - LEAST(GREATEST(experience * (? / 100), ?), ?), 0)",new CallableStatementCallback<Long>() {
long rows = jdbcTemplate.execute("UPDATE help_account SET experience = GREATEST(experience - LEAST(GREATEST((experience * ?) / 100, ?), ?), 0)",new CallableStatementCallback<Long>() {

@Override
public Long doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package net.discordjug.javabot.systems.help;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.sql.SQLException;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.JdbcTemplate;

import com.zaxxer.hikari.HikariDataSource;

import net.discordjug.javabot.data.h2db.DbHelper;
import net.discordjug.javabot.systems.help.dao.HelpAccountRepository;
import net.discordjug.javabot.systems.help.model.HelpAccount;

/**
* Tests functionality of automated experience subtraction.
*/
public class HelpExperienceSubtractionTest {

private HikariDataSource dataSource;
private HelpAccountRepository repo;

@BeforeEach
void setUp() throws IOException, SQLException {
dataSource = DataSourceBuilder.create()
.type(HikariDataSource.class)
.url("jdbc:h2:mem:test")
.username("test")
.password("")
.build();

DbHelper.initializeSchema(dataSource);

JdbcTemplate template = new JdbcTemplate(dataSource);
repo = new HelpAccountRepository(template);
}

@AfterEach
void cleanUp() {
dataSource.close();
}

/**
* If a user has less XP than the minimum experience subtraction, the user should lose all XP.
*/
@Test
void testUserHasLessThanMinimum() {
repo.insert(new HelpAccount(1, 1));
repo.removeExperienceFromAllAccounts(50, 2, 10);
assertEquals(0, repo.getByUserId(1).get().getExperience());
}

/**
* If the XP to subtract is less than the minimum, the minimum XP should be subtracted.
*/
@Test
void testSubtractMinimum() {
repo.insert(new HelpAccount(1, 6));//6XP
//would remove 50% i.e. 3XP
//the minimum is 4XP hence it should subtract 4XP
repo.removeExperienceFromAllAccounts(50, 4, 10);
assertEquals(2, repo.getByUserId(1).get().getExperience());
}

@Test
void testSubtractMaximum() {
repo.insert(new HelpAccount(1, 100));
//tries to subtract 50% which is 50XP
//but maximum is 10XP hence it should subtract 10XP
repo.removeExperienceFromAllAccounts(50, 1, 10);
assertEquals(90, repo.getByUserId(1).get().getExperience());
}

@Test
void testFraction() {
repo.insert(new HelpAccount(1, 100));
//subtract 10% i.e. 10XP
//should be inside [1,50] hence neither minimum nor maximum is active
repo.removeExperienceFromAllAccounts(10, 1, 50);
assertEquals(90, repo.getByUserId(1).get().getExperience());
//subtract 10% i.e. 9XP
//should be inside [1,50] hence neither minimum nor maximum is active
repo.removeExperienceFromAllAccounts(10, 1, 50);
assertEquals(81, repo.getByUserId(1).get().getExperience());
}

@Test
void testXPHalfLife() {
int startXP = 1_000;
int half = startXP/2;
repo.insert(new HelpAccount(1, startXP));
for (int i = 0; i < 55; i++) {
repo.removeExperienceFromAllAccounts(1.25, 0, 1_000);
double actualXP = repo.getByUserId(1).get().getExperience();
assertTrue(actualXP > half, "In iteration "+i+", XP have decayed by more than 50%, user has "+actualXP+"XP after iteration");
}
repo.removeExperienceFromAllAccounts(1.25, 0, 1_000);
double actualXP = repo.getByUserId(1).get().getExperience();
assertFalse(actualXP > half, "After all iterations, XP have not decayed by more than 50%, user has "+actualXP+"XP at the end");
}

@Test
void testMultipleUsers() {
repo.insert(new HelpAccount(79, 79));//below min
repo.insert(new HelpAccount(80, 80));//exactly min
repo.insert(new HelpAccount(100, 100));//within bounds
repo.insert(new HelpAccount(2_000, 2_000));//within bounds
repo.insert(new HelpAccount(3_999, 3_999));//close to upper bound
repo.insert(new HelpAccount(4_000, 4_000));//exactly upper bound
repo.insert(new HelpAccount(4_001, 4_001));//exceeds upper bound
repo.insert(new HelpAccount(10_000, 10_000));//significantly exceeds upper bound

repo.removeExperienceFromAllAccounts(1.25, 1, 50);

double delta = 0.0001;//required precision
assertEquals(78, repo.getByUserId(79).get().getExperience(), delta);
assertEquals(79, repo.getByUserId(80).get().getExperience(), delta);
assertEquals(98.75, repo.getByUserId(100).get().getExperience(), delta);
assertEquals(1975, repo.getByUserId(2_000).get().getExperience(), delta);
assertEquals(3949.0125, repo.getByUserId(3_999).get().getExperience(), delta);
assertEquals(3950, repo.getByUserId(4_000).get().getExperience(), delta);
assertEquals(3951, repo.getByUserId(4_001).get().getExperience(), delta);
assertEquals(9_950, repo.getByUserId(10_000).get().getExperience(), delta);
}
}