diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e1943121b..a09db78b2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -36,3 +36,4 @@ jobs:
-Dsonar.projectKey=abstracto-abstracto
-Dsonar.java.binaries=**/target/classes
-Dsonar.coverage.jacoco.xmlReportPaths=abstracto-application/coverage/target/site/jacoco-aggregate/jacoco.xml
+ -Dsonar.coverage.exclusions=**/*Test.java
diff --git a/abstracto-application/coverage/pom.xml b/abstracto-application/coverage/pom.xml
index 5adf8bbff..70b57073e 100644
--- a/abstracto-application/coverage/pom.xml
+++ b/abstracto-application/coverage/pom.xml
@@ -86,6 +86,11 @@
org.jacoco
jacoco-maven-plugin
+
+
+ **/*Test.java
+
+
report-aggregate
diff --git a/abstracto-application/pom.xml b/abstracto-application/pom.xml
index 5f77b768d..77cf50638 100644
--- a/abstracto-application/pom.xml
+++ b/abstracto-application/pom.xml
@@ -33,12 +33,6 @@
org.apache.maven.plugins
maven-surefire-plugin
2.19.1
-
-
- **/*IntegrationTest.java
-
- 1
-
@@ -50,7 +44,12 @@
org.jacoco
jacoco-maven-plugin
- 0.7.9
+ 0.8.5
+
+
+ **/*Test.java
+
+
prepare-unit-tests
@@ -63,6 +62,7 @@
+
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/DurationMethod.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/DurationMethod.java
index a202f5ddb..d436cb4e2 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/DurationMethod.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/DurationMethod.java
@@ -34,7 +34,11 @@ public class DurationMethod implements TemplateMethodModelEx {
if (arguments.size() != 1) {
throw new TemplateModelException("Incorrect parameters passed.");
}
- Object wrappedObject = ((StringModel) arguments.get(0)).getWrappedObject();
+ Object o = arguments.get(0);
+ if(!(o instanceof StringModel)) {
+ throw new TemplateModelException("Passed object was not a StringModel.");
+ }
+ Object wrappedObject = ((StringModel) o).getWrappedObject();
if(!(wrappedObject instanceof Duration)) {
throw new TemplateModelException("Passed argument was not a duration object");
}
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/SafeFieldIterations.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/SafeFieldIterations.java
index 7496c41a8..986f70aba 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/SafeFieldIterations.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/methods/SafeFieldIterations.java
@@ -20,15 +20,38 @@ public class SafeFieldIterations implements TemplateMethodModelEx {
@Override
public Object exec(List arguments) throws TemplateModelException {
- List wrappedObject = (List) ((DefaultListAdapter) arguments.get(0)).getWrappedObject();
- String appliedTemplate = ((SimpleScalar) arguments.get(1)).getAsString();
- String nameTemplate = ((SimpleScalar) arguments.get(2)).getAsString();
- String inline = ((SimpleScalar) arguments.get(3)).getAsString();
+ if(arguments.size() != 4) {
+ throw new TemplateModelException("Incorrect amount of parameters.");
+ }
+ Object adapterObject = arguments.get(0);
+ if(!(adapterObject instanceof DefaultListAdapter)) {
+ throw new TemplateModelException("Passed object was not a DefaultListAdapter.");
+ }
+ Object wrappedList = ((DefaultListAdapter) adapterObject).getWrappedObject();
+ if(!(wrappedList instanceof List)) {
+ throw new TemplateModelException("Passed object was not a List.");
+ }
+ List wrappedObject = (List) wrappedList;
+ Object templateParameter = arguments.get(1);
+ if(!(templateParameter instanceof SimpleScalar)) {
+ throw new TemplateModelException("Passed object for template was not a SimpleScalar.");
+ }
+ String appliedTemplate = ((SimpleScalar) templateParameter).getAsString();
+ Object fieldNameTemplateParameter = arguments.get(2);
+ if(!(fieldNameTemplateParameter instanceof SimpleScalar)) {
+ throw new TemplateModelException("Passed object for field name template was not a SimpleScalar.");
+ }
+ String fieldNameTemplate = ((SimpleScalar) fieldNameTemplateParameter).getAsString();
+ Object inlineParameter = arguments.get(3);
+ if(!(inlineParameter instanceof SimpleScalar)) {
+ throw new TemplateModelException("Passed object for inline was not a SimpleScalar.");
+ }
+ String inline = ((SimpleScalar) inlineParameter).getAsString();
List result = new ArrayList<>();
StringBuilder currentBuilder = new StringBuilder();
- String firstEmbedTitle = service.renderTemplateWithMap(nameTemplate, getEmbedCountParameters(1, wrappedObject));
+ String firstEmbedTitle = service.renderTemplateWithMap(fieldNameTemplate, getEmbedCountParameters(1, wrappedObject));
currentBuilder.append(newFieldHeader(firstEmbedTitle, inline));
String finalClosingString = "\"}";
String closingString = finalClosingString + ",";
@@ -42,7 +65,7 @@ public class SafeFieldIterations implements TemplateMethodModelEx {
result.add(currentBuilder);
currentBuilder = new StringBuilder();
splitFieldCounts += 1;
- String renderedName = service.renderTemplateWithMap(nameTemplate, getEmbedCountParameters(splitFieldCounts, wrappedObject));
+ String renderedName = service.renderTemplateWithMap(fieldNameTemplate, getEmbedCountParameters(splitFieldCounts, wrappedObject));
currentBuilder.append(newFieldHeader(renderedName, inline));
}
currentBuilder.append(s);
@@ -54,14 +77,14 @@ public class SafeFieldIterations implements TemplateMethodModelEx {
for (StringBuilder innerBuilder: result) {
bigBuilder.append(innerBuilder.toString());
}
- return bigBuilder;
+ return bigBuilder.toString();
}
private String newFieldHeader(String name, String inline) {
return String.format("{ \"name\": \"%s\", \"inline\": \"%s\", \"value\": \"", name, inline);
}
- private HashMap getEmbedCountParameters(Integer count, List objects) {
+ private HashMap getEmbedCountParameters(Integer count, List extends Object> objects) {
HashMap parameters = new HashMap<>();
parameters.put("count", count);
parameters.put("list", objects);
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedAuthor.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedAuthor.java
index f1cf139b6..5b4e4d8d7 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedAuthor.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedAuthor.java
@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.templating.model;
+import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -9,6 +10,7 @@ import lombok.Setter;
*/
@Getter
@Setter
+@Builder
public class EmbedAuthor {
/**
* The name used in the {@link net.dv8tion.jda.api.entities.MessageEmbed} author
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedColor.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedColor.java
index d19343a80..5b967836f 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedColor.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedColor.java
@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.templating.model;
+import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -8,6 +9,7 @@ import lombok.Setter;
*/
@Setter
@Getter
+@Builder
public class EmbedColor {
/**
* The red part of RGB
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedConfiguration.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedConfiguration.java
index 885983691..93e956724 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedConfiguration.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedConfiguration.java
@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.templating.model;
+import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -12,6 +13,7 @@ import java.util.List;
*/
@Getter
@Setter
+@Builder
public class EmbedConfiguration {
/**
* The {@link EmbedAuthor} object holding the configuration for the author of the {@link net.dv8tion.jda.api.entities.MessageEmbed}
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedFooter.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedFooter.java
index 9db206e2b..d80b975b7 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedFooter.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedFooter.java
@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.templating.model;
+import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -8,6 +9,7 @@ import lombok.Setter;
*/
@Getter
@Setter
+@Builder
public class EmbedFooter {
/**
* The text which is going to be used as the footer text
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedTitle.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedTitle.java
index 9162bdd2c..95d8a782c 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedTitle.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/model/EmbedTitle.java
@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.templating.model;
+import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -8,6 +9,7 @@ import lombok.Setter;
*/
@Getter
@Setter
+@Builder
public class EmbedTitle {
/**
* The text which is going to be used as the title of the embed
diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/service/TemplateServiceBean.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/service/TemplateServiceBean.java
index 6fefe8f05..b6ff0419b 100644
--- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/service/TemplateServiceBean.java
+++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/service/TemplateServiceBean.java
@@ -2,18 +2,20 @@ package dev.sheldan.abstracto.templating.service;
import com.google.gson.Gson;
import dev.sheldan.abstracto.templating.Templatable;
+import dev.sheldan.abstracto.templating.exception.TemplatingException;
import dev.sheldan.abstracto.templating.model.*;
import freemarker.template.Configuration;
+import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import java.awt.*;
import java.io.IOException;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -89,7 +91,7 @@ public class TemplateServiceBean implements TemplateService {
firstBuilder.setFooter(footer.getText(), footer.getIcon());
}
if(embedConfiguration.getFields() != null) {
- createFieldsForEmbed(key, embedBuilders, embedConfiguration);
+ createFieldsForEmbed(embedBuilders, embedConfiguration);
}
firstBuilder.setTimestamp(embedConfiguration.getTimeStamp());
@@ -112,18 +114,14 @@ public class TemplateServiceBean implements TemplateService {
.build();
}
- private void createFieldsForEmbed(String key, List embedBuilders, EmbedConfiguration configuration) {
+ private void createFieldsForEmbed(List embedBuilders, EmbedConfiguration configuration) {
for (int i = 0; i < configuration.getFields().size(); i++) {
EmbedField field = configuration.getFields().get(i);
- if(field != null && field.getValue() != null) {
- if(field.getValue().length() > MessageEmbed.VALUE_MAX_LENGTH) {
- String substring = field.getValue().substring(MessageEmbed.VALUE_MAX_LENGTH);
- field.setValue(field.getValue().substring(0, MessageEmbed.VALUE_MAX_LENGTH));
- EmbedField secondPart = EmbedField.builder().inline(field.getInline()).name(field.getName() + " 2").value(substring).build();
- configuration.getFields().add(i + 1, secondPart);
- }
- } else {
- log.warn("Field {} in template {} is null.", i, key);
+ if(field != null && field.getValue() != null && field.getValue().length() > MessageEmbed.VALUE_MAX_LENGTH) {
+ String substring = field.getValue().substring(MessageEmbed.VALUE_MAX_LENGTH);
+ field.setValue(field.getValue().substring(0, MessageEmbed.VALUE_MAX_LENGTH));
+ EmbedField secondPart = EmbedField.builder().inline(field.getInline()).name(field.getName() + " 2").value(substring).build();
+ configuration.getFields().add(i + 1, secondPart);
}
}
double neededIndex = Math.ceil(configuration.getFields().size() / 25D) - 1;
@@ -161,10 +159,10 @@ public class TemplateServiceBean implements TemplateService {
@Override
public String renderTemplateWithMap(String key, HashMap parameters) {
try {
- return FreeMarkerTemplateUtils.processTemplateIntoString(configuration.getTemplate(key), parameters);
+ return renderTemplateToString(key, parameters);
} catch (IOException | TemplateException e) {
log.warn("Failed to render template. ", e);
- throw new RuntimeException(e);
+ throw new TemplatingException(e);
}
}
@@ -177,13 +175,28 @@ public class TemplateServiceBean implements TemplateService {
@Override
public String renderTemplate(String key, Object model) {
try {
- return FreeMarkerTemplateUtils.processTemplateIntoString(configuration.getTemplate(key), model);
+ return renderTemplateToString(key, model);
} catch (IOException | TemplateException e) {
log.warn("Failed to render template. ", e);
- throw new RuntimeException(e);
+ throw new TemplatingException(e);
}
}
+ /**
+ * Loads the given key as a template, and renders it, returns the result as a String
+ * @param key The key of the template to render
+ * @param model The parameters which are given to the template
+ * @return The rendered template in a String
+ * @throws freemarker.template.TemplateNotFoundException In case the template could not be found
+ * @throws TemplateException In case the rendering failed
+ */
+ private String renderTemplateToString(String key, Object model) throws IOException, TemplateException {
+ StringWriter result = new StringWriter();
+ Template template = configuration.getTemplate(key);
+ template.process(model, result);
+ return result.toString();
+ }
+
/**
* Renders a simple template identified by key without any model. This will cause exceptions in case there are references to a model in the provided template.
* @param key The key of the template to be rendered
diff --git a/abstracto-application/templating/templating-impl/src/test/java/dev/sheldan/abstracto/templating/loading/TemplateLoaderTest.java b/abstracto-application/templating/templating-impl/src/test/java/dev/sheldan/abstracto/templating/loading/TemplateLoaderTest.java
new file mode 100644
index 000000000..6bd245557
--- /dev/null
+++ b/abstracto-application/templating/templating-impl/src/test/java/dev/sheldan/abstracto/templating/loading/TemplateLoaderTest.java
@@ -0,0 +1,53 @@
+package dev.sheldan.abstracto.templating.loading;
+
+import dev.sheldan.abstracto.templating.model.database.Template;
+import dev.sheldan.abstracto.templating.service.management.TemplateManagementService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Optional;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TemplateLoaderTest {
+
+ public static final String TEST_CONTENT = "test";
+ public static final String TEMPLATE_KEY = "key";
+
+ @InjectMocks
+ private DatabaseTemplateLoader loader;
+
+ @Mock
+ private TemplateManagementService templateManagementService;
+
+
+ @Test
+ public void testProperLoading() throws IOException {
+ Template mocked = Template.builder().key(TEMPLATE_KEY).content(TEST_CONTENT).build();
+ when(templateManagementService.getTemplateByKey(TEMPLATE_KEY)).thenReturn(Optional.of(mocked));
+ Template templateSource = (Template) loader.findTemplateSource(TEMPLATE_KEY);
+ assertThat(TEST_CONTENT, equalTo(templateSource.getContent()));
+ }
+
+ @Test(expected = IOException.class)
+ public void testMissingTemplate() throws IOException {
+ loader.findTemplateSource(TEMPLATE_KEY);
+ }
+
+ @Test
+ public void testReader() throws IOException {
+ Template mocked = Template.builder().key(TEMPLATE_KEY).content(TEST_CONTENT).build();
+ Reader reader = loader.getReader(mocked, null);
+ char[] chars = new char[4];
+ reader.read(chars, 0, 4);
+ assertThat(TEST_CONTENT, equalTo(new String(chars)));
+ }
+}
diff --git a/abstracto-application/templating/templating-impl/src/test/java/dev/sheldan/abstracto/templating/methods/DateMethodTest.java b/abstracto-application/templating/templating-impl/src/test/java/dev/sheldan/abstracto/templating/methods/DateMethodTest.java
new file mode 100644
index 000000000..395343ca2
--- /dev/null
+++ b/abstracto-application/templating/templating-impl/src/test/java/dev/sheldan/abstracto/templating/methods/DateMethodTest.java
@@ -0,0 +1,113 @@
+package dev.sheldan.abstracto.templating.methods;
+
+import freemarker.ext.beans.StringModel;
+import freemarker.template.*;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DateMethodTest {
+
+ public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ public static final Instant DATE = Instant.ofEpochSecond(1590615937);
+
+ @InjectMocks
+ private DateMethod dateMethod;
+
+ @Test
+ public void testInstantFormat() throws TemplateModelException {
+ String exec = (String) dateMethod.exec(getCorrectParametersInstant());
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)
+ .withZone(ZoneId.systemDefault());
+ Assert.assertEquals(exec, formatter.format(DATE));
+ }
+
+ @Test
+ public void testOffsetDateTimeObject() throws TemplateModelException {
+ String exec = (String) dateMethod.exec(getCorrectParametersOffsetDateTime());
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)
+ .withZone(ZoneId.systemDefault());
+ Assert.assertEquals(exec, formatter.format(DATE));
+ }
+
+ @Test(expected = TemplateModelException.class)
+ public void incorrectParameterCount() throws TemplateModelException {
+ dateMethod.exec(new ArrayList