[AB-197] splitting utility maven module into separate maven modules

aligning some package names
removing some unnecessary computed values from liquibase
This commit is contained in:
Sheldan
2021-03-12 17:29:49 +01:00
parent e2da800d84
commit 2ed456c164
835 changed files with 12790 additions and 3310 deletions

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,68 @@
package dev.sheldan.abstracto.suggestion.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class Accept extends AbstractConditionableCommand {
@Autowired
private SuggestionService suggestionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long suggestionId = (Long) parameters.get(0);
String text = parameters.size() == 2 ? (String) parameters.get(1) : "";
log.trace("Using default reason for accept: {}.", parameters.size() != 2);
SuggestionLog suggestionModel = (SuggestionLog) ContextConverter.fromCommandContext(commandContext, SuggestionLog.class);
return suggestionService.acceptSuggestion(suggestionId, text, suggestionModel)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> suggestionIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("suggestionId").validators(suggestionIdValidator).type(Long.class).templated(true).build());
parameters.add(Parameter.builder().name("text").type(String.class).optional(true).remainder(true).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
return CommandConfiguration.builder()
.name("accept")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return SuggestionFeatureDefinition.SUGGEST;
}
}

View File

@@ -0,0 +1,68 @@
package dev.sheldan.abstracto.suggestion.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class Reject extends AbstractConditionableCommand {
@Autowired
private SuggestionService suggestionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long suggestionId = (Long) parameters.get(0);
String text = parameters.size() == 2 ? (String) parameters.get(1) : "";
log.trace("Using default reason for accept: {}.", parameters.size() != 2);
SuggestionLog suggestionModel = (SuggestionLog) ContextConverter.fromCommandContext(commandContext, SuggestionLog.class);
return suggestionService.rejectSuggestion(suggestionId, text, suggestionModel)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> suggestionIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("suggestionId").validators(suggestionIdValidator).type(Long.class).templated(true).build());
parameters.add(Parameter.builder().name("text").type(String.class).optional(true).remainder(true).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
return CommandConfiguration.builder()
.name("reject")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return SuggestionFeatureDefinition.SUGGEST;
}
}

View File

@@ -0,0 +1,59 @@
package dev.sheldan.abstracto.suggestion.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class Suggest extends AbstractConditionableCommand {
@Autowired
private SuggestionService suggestionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String text = (String) parameters.get(0);
SuggestionLog suggestLogModel = (SuggestionLog) ContextConverter.fromCommandContext(commandContext, SuggestionLog.class);
suggestLogModel.setSuggester(commandContext.getAuthor());
return suggestionService.createSuggestionMessage(commandContext.getAuthor(), text, suggestLogModel)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("text").type(String.class).templated(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("suggest")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return SuggestionFeatureDefinition.SUGGEST;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.suggestion.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:suggestion-config.properties")
public class SuggestionConfig {
}

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.suggestion.repository;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SuggestionRepository extends JpaRepository<Suggestion, ServerSpecificId> {
}

View File

@@ -0,0 +1,163 @@
package dev.sheldan.abstracto.suggestion.service;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.utils.MessageUtils;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.suggestion.config.SuggestionPostTarget;
import dev.sheldan.abstracto.suggestion.exception.SuggestionNotFoundException;
import dev.sheldan.abstracto.suggestion.exception.SuggestionUpdateException;
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class SuggestionServiceBean implements SuggestionService {
public static final String SUGGESTION_LOG_TEMPLATE = "suggest_log";
public static final String SUGGESTION_YES_EMOTE = "suggestionYes";
public static final String SUGGESTION_NO_EMOTE = "suggestionNo";
public static final String SUGGESTION_COUNTER_KEY = "suggestion";
@Autowired
private SuggestionManagementService suggestionManagementService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Autowired
private MemberService memberService;
@Autowired
private ReactionService reactionService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SuggestionServiceBean self;
@Autowired
private CounterService counterService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public CompletableFuture<Void> createSuggestionMessage(Member member, String text, SuggestionLog suggestionLog) {
AServer server = serverManagementService.loadServer(member.getGuild());
AUserInAServer suggester = userInServerManagementService.loadOrCreateUser(member);
Long newSuggestionId = counterService.getNextCounterValue(server, SUGGESTION_COUNTER_KEY);
suggestionLog.setSuggestionId(newSuggestionId);
suggestionLog.setState(SuggestionState.NEW);
suggestionLog.setSuggesterUser(suggester);
suggestionLog.setText(text);
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_LOG_TEMPLATE, suggestionLog);
long guildId = member.getGuild().getIdLong();
log.info("Creating suggestion with id {} in server {} from member {}.", newSuggestionId, member.getGuild().getId(), member.getId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, guildId);
return FutureUtils.toSingleFutureGeneric(completableFutures).thenCompose(aVoid -> {
Message message = completableFutures.get(0).join();
log.trace("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
CompletableFuture<Void> firstReaction = reactionService.addReactionToMessageAsync(SUGGESTION_YES_EMOTE, guildId, message);
CompletableFuture<Void> secondReaction = reactionService.addReactionToMessageAsync(SUGGESTION_NO_EMOTE, guildId, message);
return CompletableFuture.allOf(firstReaction, secondReaction).thenAccept(aVoid1 -> {
log.trace("Reaction added to message {} for suggestion {}.", message.getId(), newSuggestionId);
self.persistSuggestionInDatabase(member, text, message, newSuggestionId);
});
});
}
@Transactional
public void persistSuggestionInDatabase(Member member, String text, Message message, Long suggestionId) {
log.info("Persisting suggestion {} for server {} in database.", suggestionId, member.getGuild().getId());
suggestionManagementService.createSuggestion(member, text, message, suggestionId);
}
@Override
public CompletableFuture<Void> acceptSuggestion(Long suggestionId, String text, SuggestionLog suggestionLog) {
Suggestion suggestion = suggestionManagementService.getSuggestion(suggestionId, suggestionLog.getGuild().getIdLong()).orElseThrow(() -> new SuggestionNotFoundException(suggestionId));
suggestionManagementService.setSuggestionState(suggestion, SuggestionState.ACCEPTED);
log.info("Accepting suggestion {} in server {}.", suggestionId, suggestion.getServer().getId());
return updateSuggestion(text, suggestionLog, suggestion);
}
private CompletableFuture<Void> updateSuggestion(String text, SuggestionLog suggestionLog, Suggestion suggestion) {
suggestionLog.setSuggesterUser(suggestion.getSuggester());
Long channelId = suggestion.getChannel().getId();
Long originalMessageId = suggestion.getMessageId();
Long serverId = suggestion.getServer().getId();
log.info("Updated posted suggestion {} in server {}.", suggestion.getSuggestionId().getId(), suggestion.getServer().getId());
suggestionLog.setOriginalChannelId(channelId);
suggestionLog.setOriginalMessageId(originalMessageId);
suggestionLog.setOriginalMessageUrl(MessageUtils.buildMessageUrl(serverId, channelId, originalMessageId));
AUserInAServer suggester = suggestion.getSuggester();
TextChannel textChannelById = channelService.getTextChannelFromServer(serverId, channelId);
CompletableFuture<Member> memberById = memberService.getMemberInServerAsync(serverId, suggester.getUserReference().getId());
suggestionLog.setState(suggestion.getState());
suggestionLog.setSuggestionId(suggestion.getSuggestionId().getId());
CompletableFuture<Void> finalFuture = new CompletableFuture<>();
memberById.whenComplete((member, throwable) -> {
if(throwable == null) {
suggestionLog.setSuggester(member);
}
channelService.retrieveMessageInChannel(textChannelById, originalMessageId).thenCompose(message ->
self.updateSuggestionMessageText(text, suggestionLog, message)
).thenAccept(aVoid -> finalFuture.complete(null));
});
return finalFuture;
}
@Transactional
public CompletableFuture<Void> updateSuggestionMessageText(String text, SuggestionLog suggestionLog, Message message) {
Optional<MessageEmbed> embedOptional = message.getEmbeds().stream().filter(embed -> embed.getDescription() != null).findFirst();
if(embedOptional.isPresent()) {
log.info("Updating the text of the suggestion {} in server {}.", suggestionLog.getSuggestionId(), message.getGuild().getId());
MessageEmbed suggestionEmbed = embedOptional.get();
suggestionLog.setReason(text);
suggestionLog.setText(suggestionEmbed.getDescription());
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_LOG_TEMPLATE, suggestionLog);
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, suggestionLog.getGuild().getIdLong());
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]));
} else {
log.warn("The message to update the suggestion for, did not contain an embed to update. Suggestions require an embed with a description as a container. MessageURL: {}", message.getJumpUrl());
throw new SuggestionUpdateException();
}
}
@Override
public CompletableFuture<Void> rejectSuggestion(Long suggestionId, String text, SuggestionLog suggestionLog) {
Suggestion suggestion = suggestionManagementService.getSuggestion(suggestionId, suggestionLog.getGuild().getIdLong()).orElseThrow(() -> new SuggestionNotFoundException(suggestionId));
suggestionManagementService.setSuggestionState(suggestion, SuggestionState.REJECTED);
log.info("Rejecting suggestion {} in server {}.", suggestionId, suggestion.getServer().getId());
return updateSuggestion(text, suggestionLog, suggestion);
}
}

View File

@@ -0,0 +1,75 @@
package dev.sheldan.abstracto.suggestion.service.management;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
import dev.sheldan.abstracto.suggestion.repository.SuggestionRepository;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Optional;
@Component
@Slf4j
public class SuggestionManagementServiceBean implements SuggestionManagementService {
@Autowired
private SuggestionRepository suggestionRepository;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public Suggestion createSuggestion(Member suggester, String text, Message message, Long suggestionId) {
AUserInAServer user = userInServerManagementService.loadOrCreateUser(suggester);
return this.createSuggestion(user, text, message, suggestionId);
}
@Override
public Suggestion createSuggestion(AUserInAServer suggester, String text, Message message, Long suggestionId) {
long channelId = message.getChannel().getIdLong();
AChannel channel = channelManagementService.loadChannel(channelId);
Suggestion suggestion = Suggestion
.builder()
.state(SuggestionState.NEW)
.suggester(suggester)
.suggestionId(new ServerSpecificId(suggester.getServerReference().getId(), suggestionId))
.server(suggester.getServerReference())
.suggestionDate(Instant.now())
.channel(channel)
.messageId(message.getIdLong())
.build();
log.info("Persisting suggestion {} at message {} in channel {} on server {} from user {}.",
suggestionId, message.getId(), channelId, message.getGuild().getId(), suggester.getUserReference().getId());
suggestionRepository.save(suggestion);
return suggestion;
}
@Override
public Optional<Suggestion> getSuggestion(Long suggestionId, Long serverId) {
return suggestionRepository.findById(new ServerSpecificId(serverId, suggestionId));
}
@Override
public void setSuggestionState(Suggestion suggestion, SuggestionState newState) {
suggestion.setState(newState);
log.info("Setting suggestion {} in server {} to state {}.", suggestion.getSuggestionId().getId(), suggestion.getSuggestionId().getServerId(), newState);
suggestionRepository.save(suggestion);
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
<include file="suggestion-tables/tables.xml" relativeToChangelogFile="true"/>
<include file="suggestion-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,30 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="suggestionFeature" value="(SELECT id FROM feature WHERE key = 'suggestion')"/>
<changeSet author="Sheldan" id="suggestion-commands">
<insert tableName="command">
<column name="name" value="accept"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${suggestionFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="reject"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${suggestionFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="suggest"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${suggestionFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,12 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="default_emote.xml" relativeToChangelogFile="true"/>
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,19 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="suggestion_default_emote-insert">
<insert tableName="default_emote">
<column name="emote_key" value="suggestionYes"/>
<column name="name" value="⬆"/>
</insert>
<insert tableName="default_emote">
<column name="emote_key" value="suggestionNo"/>
<column name="name" value="⬇️"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="suggestion_feature-insertion">
<insert tableName="feature">
<column name="key" value="suggestion"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,53 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="suggestion-table">
<createTable tableName="suggestion">
<column name="id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="message_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="state" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="suggestion_date" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="channel_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="suggester_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="server_id, id" tableName="suggestion" constraintName="pk_suggestion" validate="true"/>
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="suggestion" constraintName="fk_suggestion_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
<addForeignKeyConstraint baseColumnNames="suggester_user_in_server_id" baseTableName="suggestion" constraintName="fk_suggestion_suggester" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="suggestion" constraintName="fk_suggestion_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS suggestion_update_trigger ON suggestion;
CREATE TRIGGER suggestion_update_trigger BEFORE UPDATE ON suggestion FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS suggestion_insert_trigger ON suggestion;
CREATE TRIGGER suggestion_insert_trigger BEFORE INSERT ON suggestion FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
<sql>
ALTER TABLE suggestion ADD CONSTRAINT check_suggestion_state CHECK (state IN ('NEW','ACCEPTED', 'REJECTED'));
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="suggestion.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog-3.8.xsd" >
<include file="1.0-suggestion/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,5 @@
abstracto.featureFlags.suggestion.featureName=suggestion
abstracto.featureFlags.suggestion.enabled=false
abstracto.postTargets.suggestions.name=suggestions

View File

@@ -0,0 +1,47 @@
package dev.sheldan.abstracto.suggestion.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class AcceptTest {
@InjectMocks
private Accept testUnit;
@Mock
private SuggestionService suggestionService;
@Test
public void testExecuteCommand() throws ExecutionException, InterruptedException {
String text = "text";
Long suggestionId = 5L;
CommandContext context = CommandTestUtilities.getWithParameters(Arrays.asList(suggestionId, text));
when(suggestionService.acceptSuggestion(eq(suggestionId), eq(text), any(SuggestionLog.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(suggestionService, times(1)).acceptSuggestion(eq(suggestionId), eq(text), any(SuggestionLog.class));
CommandTestUtilities.checkSuccessfulCompletion(result.get());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,47 @@
package dev.sheldan.abstracto.suggestion.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RejectTest {
@InjectMocks
private Reject testUnit;
@Mock
private SuggestionService suggestionService;
@Test
public void testExecuteCommand() throws ExecutionException, InterruptedException {
String text = "text";
Long suggestionId = 5L;
CommandContext context = CommandTestUtilities.getWithParameters(Arrays.asList(suggestionId, text));
when(suggestionService.rejectSuggestion(eq(suggestionId), eq(text), any(SuggestionLog.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(suggestionService, times(1)).rejectSuggestion(eq(suggestionId), eq(text), any(SuggestionLog.class));
CommandTestUtilities.checkSuccessfulCompletion(result.get());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,45 @@
package dev.sheldan.abstracto.suggestion.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class SuggestTest {
@InjectMocks
private Suggest testUnit;
@Mock
private SuggestionService suggestionService;
@Test
public void testExecuteCommand() throws ExecutionException, InterruptedException {
String text = "text";
CommandContext context = CommandTestUtilities.getWithParameters(Arrays.asList(text));
when(suggestionService.createSuggestionMessage(eq(context.getAuthor()), eq(text), any(SuggestionLog.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(suggestionService, times(1)).createSuggestionMessage(eq(context.getAuthor()), eq(text), any(SuggestionLog.class));
CommandTestUtilities.checkSuccessfulCompletion(result.get());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,272 @@
package dev.sheldan.abstracto.suggestion.service;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.suggestion.config.SuggestionPostTarget;
import dev.sheldan.abstracto.suggestion.exception.SuggestionNotFoundException;
import dev.sheldan.abstracto.suggestion.exception.SuggestionUpdateException;
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
import net.dv8tion.jda.api.entities.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class SuggestionServiceBeanTest {
public static final String CLOSING_TEXT = "accepted";
@InjectMocks
private SuggestionServiceBean testUnit;
@Mock
private SuggestionManagementService suggestionManagementService;
@Mock
private PostTargetService postTargetService;
@Mock
private TemplateService templateService;
@Mock
private ChannelService channelService;
@Mock
private MemberService memberService;
@Mock
private ReactionService reactionService;
@Mock
private SuggestionServiceBean self;
@Mock
private Member suggestionCreator;
@Mock
private Guild guild;
@Mock
private TextChannel textChannel;
@Mock
private CounterService counterService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private ServerManagementService serverManagementService;
@Mock
private AServer server;
@Mock
private AChannel channel;
@Mock
private AUserInAServer suggester;
@Mock
private AUser suggesterUser;
@Mock
private Member suggesterMember;
private static final Long SUGGESTER_ID = 8L;
private static final Long SERVER_ID = 3L;
private static final Long CHANNEL_ID = 7L;
private static final Long SUGGESTION_ID = 5L;
@Test
public void testCreateSuggestionMessage() {
String suggestionText = "text";
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(suggestionCreator.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
when(guild.getIdLong()).thenReturn(SERVER_ID);
when(serverManagementService.loadServer(suggestionCreator.getGuild())).thenReturn(server);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(SuggestionServiceBean.SUGGESTION_LOG_TEMPLATE), any(SuggestionLog.class))).thenReturn(messageToSend);
Message suggestionMessage = Mockito.mock(Message.class);
when(counterService.getNextCounterValue(server, SuggestionServiceBean.SUGGESTION_COUNTER_KEY)).thenReturn(SUGGESTION_ID);
AUserInAServer aUserInAServer = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(suggestionCreator)).thenReturn(aUserInAServer);
List<CompletableFuture<Message>> postingFutures = Arrays.asList(CompletableFuture.completedFuture(suggestionMessage));
when(postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, SERVER_ID)).thenReturn(postingFutures);
testUnit.createSuggestionMessage(suggestionCreator, suggestionText, log);
verify(reactionService, times(1)).addReactionToMessageAsync(SuggestionServiceBean.SUGGESTION_YES_EMOTE, SERVER_ID, suggestionMessage);
verify(reactionService, times(1)).addReactionToMessageAsync(SuggestionServiceBean.SUGGESTION_NO_EMOTE, SERVER_ID, suggestionMessage);
}
@Test
public void testCreateSuggestion() {
when(suggesterMember.getGuild()).thenReturn(guild);
when(guild.getId()).thenReturn("5");
String text = "text";
Message message = Mockito.mock(Message.class);
testUnit.persistSuggestionInDatabase(suggesterMember, text, message, SUGGESTION_ID);
verify(suggestionManagementService, times(1)).createSuggestion(suggesterMember, text, message, SUGGESTION_ID);
}
@Test
public void testAcceptExistingSuggestion() {
executeAcceptWithMember(suggesterMember);
}
@Test(expected = SuggestionNotFoundException.class)
public void testAcceptNotExistingSuggestion() {
when(suggestionManagementService.getSuggestion(SUGGESTION_ID, SERVER_ID)).thenReturn(Optional.empty());
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, log);
}
@Test
public void testAcceptSuggestionWithMemberLeavingGuild() {
executeAcceptWithMember(null);
}
@Test(expected = ChannelNotInGuildException.class)
public void testAcceptSuggestionInNoTextChannel() {
setupForNoTextChannel();
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, log);
}
private void setupForNoTextChannel() {
Long messageId = 7L;
Suggestion suggestionToAccept = Mockito.mock(Suggestion.class);
when(suggestionToAccept.getChannel()).thenReturn(channel);
when(suggestionToAccept.getServer()).thenReturn(server);
when(suggestionToAccept.getSuggester()).thenReturn(suggester);
ServerSpecificId suggestionId = Mockito.mock(ServerSpecificId.class);
when(suggestionId.getId()).thenReturn(SUGGESTION_ID);
when(suggestionToAccept.getSuggestionId()).thenReturn(suggestionId);
when(suggestionToAccept.getMessageId()).thenReturn(messageId);
when(server.getId()).thenReturn(SERVER_ID);
when(channel.getId()).thenReturn(CHANNEL_ID);
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenThrow(new ChannelNotInGuildException(CHANNEL_ID));
when(suggestionManagementService.getSuggestion(SUGGESTION_ID, SERVER_ID)).thenReturn(Optional.of(suggestionToAccept));
}
@Test(expected = SuggestionUpdateException.class)
public void testUpdateSuggestionTextWithoutEmbed() {
SuggestionLog log = Mockito.mock(SuggestionLog.class);
Message suggestionMessage = Mockito.mock(Message.class);
testUnit.updateSuggestionMessageText(CLOSING_TEXT, log, suggestionMessage);
}
@Test
public void testUpdateSuggestionMessageWithEmbed() {
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
MessageEmbed embed = Mockito.mock(MessageEmbed.class);
when(embed.getDescription()).thenReturn("description");
Message suggestionMessage = Mockito.mock(Message.class);
when(suggestionMessage.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
when(suggestionMessage.getEmbeds()).thenReturn(Arrays.asList(embed));
MessageToSend updatedMessage = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(SuggestionServiceBean.SUGGESTION_LOG_TEMPLATE), any(SuggestionLog.class))).thenReturn(updatedMessage);
testUnit.updateSuggestionMessageText(CLOSING_TEXT, log, suggestionMessage);
verify(postTargetService, times(1)).sendEmbedInPostTarget(updatedMessage, SuggestionPostTarget.SUGGESTION, SERVER_ID);
}
@Test
public void testRejectExistingSuggestion() {
executeRejectWithMember(suggesterMember);
}
@Test(expected = SuggestionNotFoundException.class)
public void testRejectNotExistingSuggestion() {
when(suggestionManagementService.getSuggestion(SUGGESTION_ID, SERVER_ID)).thenReturn(Optional.empty());
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, log);
}
@Test
public void testRejectSuggestionWithMemberLeavingGuild() {
executeRejectWithMember(null);
}
@Test(expected = ChannelNotInGuildException.class)
public void testRejectSuggestionInNoTextChannel() {
setupForNoTextChannel();
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, log);
}
private void executeAcceptWithMember(Member actualMember) {
Long messageId = 7L;
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
Suggestion suggestionToAccept = setupClosing(messageId);
Message suggestionMessage = Mockito.mock(Message.class);
when(channelService.retrieveMessageInChannel(textChannel, messageId)).thenReturn(CompletableFuture.completedFuture(suggestionMessage));
when(memberService.getMemberInServerAsync(SERVER_ID, SUGGESTER_ID)).thenReturn(CompletableFuture.completedFuture(actualMember));
testUnit.acceptSuggestion(SUGGESTION_ID, CLOSING_TEXT, log);
verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.ACCEPTED);
}
private void executeRejectWithMember(Member actualMember) {
Long messageId = 7L;
SuggestionLog log = Mockito.mock(SuggestionLog.class);
when(log.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(SERVER_ID);
Suggestion suggestionToAccept = setupClosing(messageId);
Message suggestionMessage = Mockito.mock(Message.class);
when(channelService.retrieveMessageInChannel(textChannel, messageId)).thenReturn(CompletableFuture.completedFuture(suggestionMessage));
when(memberService.getMemberInServerAsync(SERVER_ID, SUGGESTER_ID)).thenReturn(CompletableFuture.completedFuture(actualMember));
testUnit.rejectSuggestion(SUGGESTION_ID, CLOSING_TEXT, log);
verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.REJECTED);
}
private Suggestion setupClosing(Long messageId) {
Suggestion suggestionToAccept = Mockito.mock(Suggestion.class);
when(suggestionToAccept.getChannel()).thenReturn(channel);
when(suggestionToAccept.getServer()).thenReturn(server);
when(suggestionToAccept.getSuggester()).thenReturn(suggester);
ServerSpecificId suggestionId = Mockito.mock(ServerSpecificId.class);
when(suggestionId.getId()).thenReturn(SUGGESTION_ID);
when(suggestionToAccept.getSuggestionId()).thenReturn(suggestionId);
when(suggestionToAccept.getMessageId()).thenReturn(messageId);
when(server.getId()).thenReturn(SERVER_ID);
when(channel.getId()).thenReturn(CHANNEL_ID);
when(suggester.getUserReference()).thenReturn(suggesterUser);
when(suggesterUser.getId()).thenReturn(SUGGESTER_ID);
when(suggestionManagementService.getSuggestion(SUGGESTION_ID, SERVER_ID)).thenReturn(Optional.of(suggestionToAccept));
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
return suggestionToAccept;
}
}

View File

@@ -0,0 +1,136 @@
package dev.sheldan.abstracto.suggestion.service.management;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
import dev.sheldan.abstracto.suggestion.repository.SuggestionRepository;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class SuggestionManagementServiceBeanTest {
@InjectMocks
private SuggestionManagementServiceBean testUnit;
@Mock
private SuggestionRepository suggestionRepository;
@Mock
private ChannelManagementService channelManagementService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private ServerManagementService serverManagementService;
@Mock
private AServer server;
@Mock
private AUserInAServer aUserInAServer;
@Mock
private AUser aUser;
public static final long CHANNEL_ID = 6L;
public static final long SERVER_ID = 6L;
public static final long SUGGESTION_ID = 6L;
@Test
public void testCreateSuggestionViaUser() {
String text = "text";
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(aUser);
Guild guild = Mockito.mock(Guild.class);
Message message = Mockito.mock(Message.class);
MessageChannel messageChannel = Mockito.mock(MessageChannel.class);
when(messageChannel.getIdLong()).thenReturn(CHANNEL_ID);
when(message.getChannel()).thenReturn(messageChannel);
when(message.getGuild()).thenReturn(guild);
when(guild.getId()).thenReturn("8");
long suggestionId = 1L;
Suggestion createdSuggestion = testUnit.createSuggestion(aUserInAServer, text, message, suggestionId);
verify(suggestionRepository, times(1)).save(createdSuggestion);
Assert.assertEquals(SuggestionState.NEW, createdSuggestion.getState());
Assert.assertEquals(aUserInAServer, createdSuggestion.getSuggester());
Assert.assertEquals(server, createdSuggestion.getServer());
}
@Test
public void testCreateSuggestionViaMember() {
Member member = Mockito.mock(Member.class);
String text = "text";
Guild guild = Mockito.mock(Guild.class);
Message message = Mockito.mock(Message.class);
MessageChannel messageChannel = Mockito.mock(MessageChannel.class);
when(messageChannel.getIdLong()).thenReturn(CHANNEL_ID);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(aUser);
when(message.getChannel()).thenReturn(messageChannel);
when(message.getGuild()).thenReturn(guild);
when(guild.getId()).thenReturn("5");
when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(aUserInAServer);
long suggestionId = 1L;
Suggestion createdSuggestion = testUnit.createSuggestion(member, text, message, suggestionId);
verify(suggestionRepository, times(1)).save(createdSuggestion);
Assert.assertEquals(SuggestionState.NEW, createdSuggestion.getState());
Assert.assertEquals(aUserInAServer, createdSuggestion.getSuggester());
Assert.assertEquals(server, createdSuggestion.getServer());
}
@Test
public void testGetSuggestion() {
Suggestion foundSuggestion = createSuggestion();
when(suggestionRepository.findById(new ServerSpecificId(SERVER_ID, SUGGESTION_ID))).thenReturn(Optional.of(foundSuggestion));
Optional<Suggestion> suggestionOptional = testUnit.getSuggestion(SUGGESTION_ID, SERVER_ID);
Assert.assertTrue(suggestionOptional.isPresent());
suggestionOptional.ifPresent(suggestion -> Assert.assertEquals(SUGGESTION_ID, suggestion.getSuggestionId().getId().longValue()));
}
@Test
public void testGetSuggestionNotFound() {
when(suggestionRepository.findById(new ServerSpecificId(SERVER_ID, SUGGESTION_ID))).thenReturn(Optional.empty());
Optional<Suggestion> suggestionOptional = testUnit.getSuggestion(SUGGESTION_ID, SERVER_ID);
Assert.assertFalse(suggestionOptional.isPresent());
}
@Test
public void setSuggestionState() {
Suggestion suggestion = createSuggestion();
testUnit.setSuggestionState(suggestion, SuggestionState.ACCEPTED);
verify(suggestion, times(1)).setState(SuggestionState.ACCEPTED);
verify(suggestionRepository, times(1)).save(suggestion);
}
private Suggestion createSuggestion() {
Suggestion foundSuggestion = Mockito.mock(Suggestion.class);
ServerSpecificId suggestionId = Mockito.mock(ServerSpecificId.class);
when(suggestionId.getId()).thenReturn(SUGGESTION_ID);
when(suggestionId.getServerId()).thenReturn(SERVER_ID);
when(foundSuggestion.getSuggestionId()).thenReturn(suggestionId);
return foundSuggestion;
}
}