Added template service tests

This commit is contained in:
Sheldan
2020-05-29 23:55:23 +02:00
committed by GitHub
20 changed files with 951 additions and 32 deletions

View File

@@ -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

View File

@@ -86,6 +86,11 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/*Test.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>report-aggregate</id>

View File

@@ -33,12 +33,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<excludes>
<exclude>**/*IntegrationTest.java</exclude>
</excludes>
<forkCount>1</forkCount>
</configuration>
</plugin>
<plugin>
@@ -50,7 +44,12 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version>
<version>0.8.5</version>
<configuration>
<excludes>
<exclude>**/*Test.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-unit-tests</id>
@@ -63,6 +62,7 @@
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>

View File

@@ -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");
}

View File

@@ -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<StringBuilder> 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<String, Object> getEmbedCountParameters(Integer count, List objects) {
private HashMap<String, Object> getEmbedCountParameters(Integer count, List<? extends Object> objects) {
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("count", count);
parameters.put("list", objects);

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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<EmbedBuilder> embedBuilders, EmbedConfiguration configuration) {
private void createFieldsForEmbed(List<EmbedBuilder> 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<String, Object> 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

View File

@@ -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)));
}
}

View File

@@ -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<Object>());
}
@Test(expected = IllegalArgumentException.class)
public void testIncorrectDateFormat() throws TemplateModelException {
dateMethod.exec(getIncorrectDateFormat());
}
@Test(expected = TemplateModelException.class)
public void incorrectPassedObject() throws TemplateModelException {
dateMethod.exec(getIncorrectTimeParameter());
}
private List<Object> getIncorrectDateFormat() {
List<Object> params = new ArrayList<>();
params.add(getInstantDateObject());
params.add(incorrectDateFormat());
return params;
}
private List<Object> getIncorrectTimeParameter() {
List<Object> params = new ArrayList<>();
params.add(getNotCompatibleObject());
params.add(incorrectDateFormat());
return params;
}
private List<Object> getCorrectParametersInstant() {
List<Object> params = new ArrayList<>();
params.add(getInstantDateObject());
params.add(simpleDateFormat());
return params;
}
private List<Object> getCorrectParametersOffsetDateTime() {
List<Object> params = new ArrayList<>();
params.add(getOffsetDateTimeObject());
params.add(simpleDateFormat());
return params;
}
private SimpleScalar simpleDateFormat() {
return new SimpleScalar(DATE_TIME_FORMAT);
}
private SimpleScalar incorrectDateFormat() {
return new SimpleScalar("INCORRECT");
}
private StringModel getInstantDateObject() {
DefaultObjectWrapper wrapper = getWrapper();
return new StringModel(DATE, wrapper);
}
private DefaultObjectWrapper getWrapper() {
return new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_0).build();
}
private StringModel getOffsetDateTimeObject() {
DefaultObjectWrapper wrapper = getWrapper();
return new StringModel(OffsetDateTime.ofInstant(DATE, ZoneId.systemDefault()), wrapper);
}
private StringModel getNotCompatibleObject() {
DefaultObjectWrapper wrapper = getWrapper();
return new StringModel("", wrapper);
}
}

View File

@@ -0,0 +1,124 @@
package dev.sheldan.abstracto.templating.methods;
import dev.sheldan.abstracto.templating.service.TemplateService;
import freemarker.ext.beans.StringModel;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.TemplateModelException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class DurationMethodTest {
public static final long SECOND_AMOUNT = 10L;
public static final long MINUTES_AMOUNT = 2L;
public static final long HOURS_AMOUNT = 3L;
public static final long DAYS_AMOUNT = 5L;
public static final String DURATION_TEMPLATE = "duration_formatting";
public static final String SECONDS = "seconds";
public static final String HOURS = "hours";
public static final String MINUTES = "minutes";
public static final String DAYS = "days";
@InjectMocks
private DurationMethod durationMethod;
@Mock
private TemplateService templateService;
@Test
public void testSeconds() throws TemplateModelException {
durationMethod.exec(getSecondParameters());
verify(templateService, times(1)).renderTemplateWithMap(DURATION_TEMPLATE, getHashMap(0, 0, 0, SECOND_AMOUNT));
}
@Test
public void testMinutes() throws TemplateModelException {
durationMethod.exec(getMinuteParameter());
verify(templateService, times(1)).renderTemplateWithMap(DURATION_TEMPLATE, getHashMap(0, 0, MINUTES_AMOUNT, 0));
}
@Test
public void testHours() throws TemplateModelException {
durationMethod.exec(getHourParameter());
verify(templateService, times(1)).renderTemplateWithMap(DURATION_TEMPLATE, getHashMap(0, HOURS_AMOUNT, 0, 0));
}
@Test
public void testDays() throws TemplateModelException {
durationMethod.exec(getDayParameter());
verify(templateService, times(1)).renderTemplateWithMap(DURATION_TEMPLATE, getHashMap(DAYS_AMOUNT, 0, 0, 0));
}
@Test
public void testAllTime() throws TemplateModelException {
durationMethod.exec(getMixedParameter());
verify(templateService, times(1)).renderTemplateWithMap(DURATION_TEMPLATE, getHashMap(DAYS_AMOUNT, HOURS_AMOUNT, MINUTES_AMOUNT, SECOND_AMOUNT));
}
@Test(expected = TemplateModelException.class)
public void testNoParamGiven() throws TemplateModelException {
durationMethod.exec(Collections.emptyList());
}
@Test(expected = TemplateModelException.class)
public void testNoDurationObject() throws TemplateModelException {
durationMethod.exec(Arrays.asList(new StringModel("", getWrapper())));
}
@Test(expected = TemplateModelException.class)
public void testNoStringModelObject() throws TemplateModelException {
durationMethod.exec(Arrays.asList(""));
}
private List<Object> getSecondParameters() {
return Arrays.asList(new StringModel(Duration.ofSeconds(SECOND_AMOUNT), getWrapper()));
}
private List<Object> getMinuteParameter() {
return Arrays.asList(new StringModel(Duration.ofMinutes(MINUTES_AMOUNT), getWrapper()));
}
private List<Object> getHourParameter() {
return Arrays.asList(new StringModel(Duration.ofHours(HOURS_AMOUNT), getWrapper()));
}
private List<Object> getDayParameter() {
return Arrays.asList(new StringModel(Duration.ofDays(DAYS_AMOUNT), getWrapper()));
}
private List<Object> getMixedParameter() {
return Arrays.asList(new StringModel(Duration.ofSeconds(SECOND_AMOUNT)
.plus(Duration.ofMinutes(MINUTES_AMOUNT))
.plus(Duration.ofHours(HOURS_AMOUNT))
.plus(Duration.ofDays(DAYS_AMOUNT)), getWrapper()));
}
private DefaultObjectWrapper getWrapper() {
return new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_0).build();
}
private HashMap<String, Object> getHashMap(long days, long hours, long minutes, long seconds) {
HashMap<String, Object> map = new HashMap<>();
map.put(DAYS, days);
map.put(HOURS, hours);
map.put(MINUTES, minutes);
map.put(SECONDS, seconds);
return map;
}
}

View File

@@ -0,0 +1,140 @@
package dev.sheldan.abstracto.templating.methods;
import dev.sheldan.abstracto.templating.service.TemplateService;
import freemarker.template.*;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.times;
@RunWith(MockitoJUnitRunner.class)
public class SafeFieldIterationsTest {
public static final String TEMPLATE_KEY = "template";
public static final String FIELD_NAME_TEMPLATE = "fieldName";
public static final String FIELD_NAME_VALUE = "fieldName";
public static final String EXPECTED_START_PART = "{ \"name\": \"" + FIELD_NAME_VALUE + "\", \"inline\": \"true\", \"value\": \"";
public static final String INLINE_VALUE = "true";
public static final String FIRST_LIST_ENTRY = "text";
public static final String SIX_HUNDRED_CHARACTERS = RandomStringUtils.randomAlphabetic(600);
@InjectMocks
private SafeFieldIterations safeFieldIterations;
@Mock
private TemplateService templateService;
@Captor
private ArgumentCaptor<String> templateKeyCaptor;
@Before
public void setup() {
when(templateService.renderTemplateWithMap(eq(FIELD_NAME_TEMPLATE), any())).thenReturn(FIELD_NAME_VALUE);
}
@Test
public void testEmptyList() throws TemplateModelException {
String resultingValue = (String) safeFieldIterations.exec(getSimpleParameters());
verify(templateService, times(1)).renderTemplateWithMap(templateKeyCaptor.capture(), any());
List<String> usedTemplateKeys = templateKeyCaptor.getAllValues();
assertEquals(FIELD_NAME_TEMPLATE, usedTemplateKeys.get(0));
assertEquals(EXPECTED_START_PART + "\"}", resultingValue);
}
@Test
public void testOneElement() throws TemplateModelException {
when(templateService.renderTemplateWithMap(eq(TEMPLATE_KEY), any())).thenReturn(FIRST_LIST_ENTRY);
String resultingValue = (String) safeFieldIterations.exec(oneListEntryParameter());
assertEquals(EXPECTED_START_PART + FIRST_LIST_ENTRY + "\"}", resultingValue);
}
@Test
public void testTwoElements() throws TemplateModelException {
when(templateService.renderTemplateWithMap(eq(TEMPLATE_KEY), any())).thenReturn(FIRST_LIST_ENTRY);
String resultingValue = (String) safeFieldIterations.exec(twoListEntryParameter());
assertEquals(EXPECTED_START_PART + FIRST_LIST_ENTRY + FIRST_LIST_ENTRY + "\"}", resultingValue);
}
@Test
public void testElementsStaySolo() throws TemplateModelException {
when(templateService.renderTemplateWithMap(eq(TEMPLATE_KEY), any())).thenReturn(SIX_HUNDRED_CHARACTERS);
String resultingValue = (String) safeFieldIterations.exec(twoListEntryParameter());
assertEquals(EXPECTED_START_PART + SIX_HUNDRED_CHARACTERS + "\"}," + EXPECTED_START_PART + SIX_HUNDRED_CHARACTERS + "\"}", resultingValue);
}
@Test(expected = TemplateModelException.class)
public void testTooLittleParameters() throws TemplateModelException {
safeFieldIterations.exec(Arrays.asList(""));
}
@Test(expected = TemplateModelException.class)
public void testWrongListAdapterType() throws TemplateModelException {
safeFieldIterations.exec(wrongListAdapter());
}
@Test(expected = TemplateModelException.class)
public void testWrongTemplateKeyParameterType() throws TemplateModelException {
safeFieldIterations.exec(wrongTemplateKeyParameterType());
}
@Test(expected = TemplateModelException.class)
public void testWrongFieldNameTemplateKeyParameterType() throws TemplateModelException {
safeFieldIterations.exec(wrongFieldNameTemplateKeyParameterType());
}
@Test(expected = TemplateModelException.class)
public void testWrongInlineParameterType() throws TemplateModelException {
safeFieldIterations.exec(wrongInLineValueParameterType());
}
public List<Object> wrongListAdapter() {
return Arrays.asList(new Object(), new Object(), new SimpleScalar(FIELD_NAME_TEMPLATE), new SimpleScalar(INLINE_VALUE));
}
public List<Object> wrongTemplateKeyParameterType() {
return Arrays.asList(validEmptyList(), new Object(), new SimpleScalar(FIELD_NAME_TEMPLATE), new SimpleScalar(INLINE_VALUE));
}
public List<Object> wrongFieldNameTemplateKeyParameterType() {
return Arrays.asList(validEmptyList(), new SimpleScalar(TEMPLATE_KEY), new Object(), new SimpleScalar(INLINE_VALUE));
}
public List<Object> wrongInLineValueParameterType() {
return Arrays.asList(validEmptyList(), new SimpleScalar(TEMPLATE_KEY), new SimpleScalar(FIELD_NAME_TEMPLATE), new Object());
}
public List<Object> getSimpleParameters() {
return Arrays.asList(validEmptyList(), new SimpleScalar(TEMPLATE_KEY), new SimpleScalar(FIELD_NAME_TEMPLATE), new SimpleScalar(INLINE_VALUE));
}
private DefaultListAdapter validEmptyList() {
return DefaultListAdapter.adapt(new ArrayList<Object>(), getWrapper());
}
public List<Object> oneListEntryParameter() {
return Arrays.asList(DefaultListAdapter.adapt(Arrays.asList("testing"), getWrapper()), new SimpleScalar(TEMPLATE_KEY), new SimpleScalar(FIELD_NAME_TEMPLATE), new SimpleScalar(INLINE_VALUE));
}
public List<Object> twoListEntryParameter() {
return Arrays.asList(DefaultListAdapter.adapt(Arrays.asList("testing", "othertext"), getWrapper()), new SimpleScalar(TEMPLATE_KEY), new SimpleScalar(FIELD_NAME_TEMPLATE), new SimpleScalar(INLINE_VALUE));
}
private DefaultObjectWrapper getWrapper() {
return new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_0).build();
}
}

View File

@@ -0,0 +1,330 @@
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 freemarker.template.TemplateNotFoundException;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
import java.awt.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class TemplateServiceBeanTest {
public static final String EXAMPLE_URL = "https://example.com";
public static final String FIELD_VALUE = "value";
public static final String EMBED_PAGE_COUNT_TEMPLATE = "embed_page_count";
public static final String FIELD_TEMPLATE = "{\n" +
"\"name\": \"name\",\n" +
"\"value\": \"value\",\n" +
"\"inline\": \"true\"\n" +
"}";
public static final String FIELD_TEMPLATE_WITH_VALUE = "{\n" +
"\"name\": \"name\",\n" +
"\"value\": \"%s\",\n" +
"\"inline\": \"true\"\n" +
"}";
@InjectMocks
private TemplateServiceBean templateServiceBean;
@Mock
private Configuration configuration;
@Mock
private Gson gson;
@Autowired
private Gson actualGsonInstance;
private static final String SIMPLE_TEMPLATE_SOURCE = "source";
private static final String TEMPLATE_KEY = "template";
@Test
public void testSimpleTemplate() throws IOException, TemplateException {
when(configuration.getTemplate(TEMPLATE_KEY)).thenReturn(getSimpleTemplate());
String rendered = templateServiceBean.renderSimpleTemplate(TEMPLATE_KEY);
Assert.assertEquals(SIMPLE_TEMPLATE_SOURCE, rendered);
}
@Test
public void renderTemplatable() throws IOException, TemplateException {
when(configuration.getTemplate(TEMPLATE_KEY)).thenReturn(getSimpleTemplate());
Templatable templatable = getTemplatableWithSimpleTemplate();
String rendered = templateServiceBean.renderTemplatable(templatable);
Assert.assertEquals(SIMPLE_TEMPLATE_SOURCE, rendered);
}
@Test
public void testTemplateWithMapParameter() throws IOException, TemplateException {
when(configuration.getTemplate(TEMPLATE_KEY)).thenReturn(getSimpleTemplate());
String rendered = templateServiceBean.renderTemplateWithMap(TEMPLATE_KEY, new HashMap<>());
Assert.assertEquals(SIMPLE_TEMPLATE_SOURCE, rendered);
}
@Test
public void testEmbedWithDescription() throws IOException, TemplateException {
String descriptionText = "test";
String fullEmbedTemplateKey = getEmbedTemplateKey();
when(configuration.getTemplate(fullEmbedTemplateKey)).thenReturn(getEmbedTemplateWithDescription(descriptionText));
when(gson.fromJson(embedTemplateWithDescription(descriptionText), EmbedConfiguration.class)).thenReturn(embedConfigWithDescription(descriptionText));
MessageToSend messageToSend = templateServiceBean.renderEmbedTemplate(TEMPLATE_KEY, new HashMap<>());
Assert.assertEquals(descriptionText, messageToSend.getEmbeds().get(0).getDescription());
}
@Test
public void testEmbedWithAllUsableAttributes() throws IOException, TemplateException {
when(configuration.getTemplate(getEmbedTemplateKey())).thenReturn(getEmbedTemplateWithFallFieldsUsedOnce());
when(gson.fromJson(getFullEmbedConfigString(), EmbedConfiguration.class)).thenReturn(getFullEmbedConfiguration());
MessageToSend messageToSend = templateServiceBean.renderEmbedTemplate(TEMPLATE_KEY, new HashMap<>());
Assert.assertEquals(messageToSend.getMessage(), "additionalMessage");
MessageEmbed onlyEmbed = messageToSend.getEmbeds().get(0);
Assert.assertEquals(onlyEmbed.getAuthor().getIconUrl(), EXAMPLE_URL);
Assert.assertEquals(onlyEmbed.getAuthor().getName(), "name");
Assert.assertEquals(onlyEmbed.getDescription(), "description");
MessageEmbed.Field onlyField = onlyEmbed.getFields().get(0);
Assert.assertEquals(onlyField.getName(), "name");
Assert.assertEquals(onlyField.getValue(), FIELD_VALUE);
Assert.assertTrue(onlyField.isInline());
Color color = onlyEmbed.getColor();
Assert.assertEquals(color.getBlue(), 255);
Assert.assertEquals(color.getRed(), 255);
Assert.assertEquals(color.getGreen(), 255);
Assert.assertEquals(onlyEmbed.getUrl(), EXAMPLE_URL);
Assert.assertEquals(onlyEmbed.getFooter().getText(), "text");
Assert.assertEquals(onlyEmbed.getFooter().getIconUrl(), EXAMPLE_URL);
Assert.assertEquals(onlyEmbed.getImage().getUrl(), EXAMPLE_URL);
Assert.assertEquals(onlyEmbed.getThumbnail().getUrl(), EXAMPLE_URL);
Assert.assertEquals(1, messageToSend.getEmbeds().size());
}
@Test
public void testEmbedWithTooLongDescription() throws IOException, TemplateException {
int tooMuchCharacterCount = 1024;
String descriptionText = RandomStringUtils.randomAlphabetic(MessageEmbed.TEXT_MAX_LENGTH + tooMuchCharacterCount);
when(configuration.getTemplate(getEmbedTemplateKey())).thenReturn(getEmbedTemplateWithDescription(descriptionText));
when(configuration.getTemplate(EMBED_PAGE_COUNT_TEMPLATE)).thenReturn(getPageCountTemplate(1));
when(gson.fromJson(embedTemplateWithDescription(descriptionText), EmbedConfiguration.class)).thenReturn(embedConfigWithDescription(descriptionText));
MessageToSend messageToSend = templateServiceBean.renderEmbedTemplate(TEMPLATE_KEY, new HashMap<>());
MessageEmbed firstEmbed = messageToSend.getEmbeds().get(0);
Assert.assertEquals(MessageEmbed.TEXT_MAX_LENGTH, firstEmbed.getDescription().length());
Assert.assertEquals(descriptionText.substring(0, MessageEmbed.TEXT_MAX_LENGTH), firstEmbed.getDescription());
MessageEmbed secondEmbed = messageToSend.getEmbeds().get(1);
Assert.assertEquals(tooMuchCharacterCount, secondEmbed.getDescription().length());
Assert.assertEquals(descriptionText.substring(MessageEmbed.TEXT_MAX_LENGTH, MessageEmbed.TEXT_MAX_LENGTH + tooMuchCharacterCount), secondEmbed.getDescription());
}
@Test
public void testEmbedWithTooManyFields() throws IOException, TemplateException {
int totalFieldCount = 30;
when(configuration.getTemplate(getEmbedTemplateKey())).thenReturn(getEmbedTemplateWithFieldCount(totalFieldCount));
when(configuration.getTemplate(EMBED_PAGE_COUNT_TEMPLATE)).thenReturn(getPageCountTemplate(1));
when(gson.fromJson(getFieldsEmbedConfigAsString(totalFieldCount), EmbedConfiguration.class)).thenReturn(getTooManyFieldsEmbedConfiguration());
MessageToSend messageToSend = templateServiceBean.renderEmbedTemplate(TEMPLATE_KEY, new HashMap<>());
MessageEmbed firstEmbed = messageToSend.getEmbeds().get(0);
Assert.assertEquals(25, firstEmbed.getFields().size());
MessageEmbed secondEmbed = messageToSend.getEmbeds().get(1);
Assert.assertEquals(totalFieldCount % 25, secondEmbed.getFields().size());
}
@Test
public void testEmbedWithTooLongField() throws IOException, TemplateException {
String fieldValue = RandomStringUtils.randomAlphabetic(1500);
when(configuration.getTemplate(getEmbedTemplateKey())).thenReturn(getEmbedTemplateWithTooLongField(fieldValue));
when(gson.fromJson(getSingleFieldWithValue(fieldValue), EmbedConfiguration.class)).thenReturn(getEmbedWithSingleFieldOfValue(fieldValue));
MessageToSend messageToSend = templateServiceBean.renderEmbedTemplate(TEMPLATE_KEY, new HashMap<>());
MessageEmbed firstEmbed = messageToSend.getEmbeds().get(0);
Assert.assertEquals(2, firstEmbed.getFields().size());
Assert.assertEquals(fieldValue.substring(0, MessageEmbed.VALUE_MAX_LENGTH), firstEmbed.getFields().get(0).getValue());
Assert.assertEquals(fieldValue.substring(MessageEmbed.VALUE_MAX_LENGTH), firstEmbed.getFields().get(1).getValue());
}
@Test(expected = TemplatingException.class)
public void tryToRenderMissingTemplate() throws IOException {
when(configuration.getTemplate(TEMPLATE_KEY)).thenThrow(new TemplateNotFoundException(TEMPLATE_KEY, new Object(), ""));
templateServiceBean.renderSimpleTemplate(TEMPLATE_KEY);
}
@Test(expected = TemplatingException.class)
public void tryToRenderMissingEmbedTemplate() throws IOException {
when(configuration.getTemplate(getEmbedTemplateKey())).thenThrow(new TemplateNotFoundException(TEMPLATE_KEY, new Object(), ""));
templateServiceBean.renderEmbedTemplate(TEMPLATE_KEY, new Object());
}
@NotNull
private String getEmbedTemplateKey() {
return TEMPLATE_KEY + "_embed";
}
@Test(expected = TemplatingException.class)
public void tryToRenderMissingTemplateWithMap() throws IOException {
when(configuration.getTemplate(TEMPLATE_KEY)).thenThrow(new TemplateNotFoundException(TEMPLATE_KEY, new Object(), ""));
templateServiceBean.renderTemplateWithMap(TEMPLATE_KEY, new HashMap<>());
}
private EmbedConfiguration embedConfigWithDescription(String descriptionText) {
return EmbedConfiguration.builder().description(descriptionText).build();
}
private EmbedConfiguration getEmbedWithSingleFieldOfValue(String value) {
List<EmbedField> fields = new ArrayList<>();
fields.add(EmbedField.builder().name("name").value(value).build());
return EmbedConfiguration.builder().fields(fields).build();
}
private EmbedConfiguration getTooManyFieldsEmbedConfiguration() {
List<EmbedField> fields = new ArrayList<>();
for (int i = 0; i < 30; i++) {
EmbedField field = EmbedField
.builder()
.value(FIELD_VALUE)
.inline(true)
.name("name")
.build();
fields.add(field);
}
return EmbedConfiguration.builder().fields(fields).build();
}
private Templatable getTemplatableWithSimpleTemplate() {
return new Templatable() {
@Override
public String getTemplateName() {
return TEMPLATE_KEY;
}
@Override
public Object getTemplateModel() {
return new Object();
}
};
}
private EmbedConfiguration getFullEmbedConfiguration() {
EmbedAuthor author = EmbedAuthor
.builder()
.avatar(EXAMPLE_URL)
.name("name")
.url(EXAMPLE_URL)
.build();
EmbedColor color = EmbedColor
.builder()
.r(255)
.g(255)
.b(255)
.build();
EmbedField field = EmbedField
.builder()
.value(FIELD_VALUE)
.inline(true)
.name("name")
.build();
EmbedFooter footer = EmbedFooter
.builder()
.icon(EXAMPLE_URL)
.text("text")
.build();
EmbedTitle title = EmbedTitle
.builder()
.title("title")
.url(EXAMPLE_URL)
.build();
return EmbedConfiguration
.builder()
.fields(Arrays.asList(field))
.footer(footer)
.author(author)
.title(title)
.color(color)
.description("description")
.additionalMessage("additionalMessage")
.imageUrl(EXAMPLE_URL)
.thumbnail(EXAMPLE_URL)
.build();
}
private Template getSimpleTemplate() throws IOException, TemplateException {
return new Template(TEMPLATE_KEY, SIMPLE_TEMPLATE_SOURCE, getNonMockedConfiguration());
}
private Template getEmbedTemplateWithDescription(String description) throws IOException, TemplateException {
return new Template(getEmbedTemplateKey(), embedTemplateWithDescription(description), getNonMockedConfiguration());
}
private Template getPageCountTemplate(Integer page) throws IOException, TemplateException {
return new Template(EMBED_PAGE_COUNT_TEMPLATE, getEmbedPageCount(page), getNonMockedConfiguration());
}
private Template getEmbedTemplateWithFallFieldsUsedOnce() throws IOException, TemplateException {
return new Template(getEmbedTemplateKey(), getFullEmbedConfigString(), getNonMockedConfiguration());
}
private Template getEmbedTemplateWithFieldCount(Integer count) throws IOException, TemplateException {
return new Template(getEmbedTemplateKey(), getFieldsEmbedConfigAsString(count), getNonMockedConfiguration());
}
private Template getEmbedTemplateWithTooLongField(String value) throws IOException, TemplateException {
return new Template(getEmbedTemplateKey(), getSingleFieldWithValue(value), getNonMockedConfiguration());
}
private String getFullEmbedConfigString() throws IOException {
return IOUtils.toString(this.getClass().getResourceAsStream("/full_embed.json"), StandardCharsets.UTF_8);
}
private String getFieldsEmbedConfigAsString(Integer count) {
StringBuilder sb = new StringBuilder();
sb.append("{\"fields\": [");
for (int i = 0; i < count - 1; i++) {
sb.append(FIELD_TEMPLATE + ",");
}
sb.append(FIELD_TEMPLATE +
"]\n" +
"}");
return sb.toString();
}
private String getSingleFieldWithValue(String value) {
return String.format("{\"fields\": [" + FIELD_TEMPLATE_WITH_VALUE + "]\n}",value);
}
private String embedTemplateWithDescription(String description) {
return String.format("{ \"description\": \"%s\"}", description);
}
private String getEmbedPageCount(Integer page) {
return String.format("Page %d", page);
}
private Configuration getNonMockedConfiguration() throws IOException, TemplateException {
return new FreeMarkerConfigurationFactory().createConfiguration();
}
}

View File

@@ -0,0 +1,67 @@
package dev.sheldan.abstracto.templating.service.management;
import dev.sheldan.abstracto.templating.model.database.Template;
import dev.sheldan.abstracto.templating.repository.TemplateRepository;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class TemplateManagementServiceBeanTest {
@InjectMocks
private TemplateManagementServiceBean templateManagementServiceBean;
@Mock
private TemplateRepository repository;
private static final String TEMPLATE_KEY = "templateKey";
private static final String TEMPLATE_SOURCE = "source";
@Test
public void testFindByKey() {
when(repository.findById(TEMPLATE_KEY)).thenReturn(Optional.of(getTemplate()));
Optional<Template> templateByKey = templateManagementServiceBean.getTemplateByKey(TEMPLATE_KEY);
templateByKey.ifPresent(template -> {
Assert.assertEquals(TEMPLATE_KEY, template.getKey());
Assert.assertEquals(TEMPLATE_SOURCE, template.getContent());
});
Assert.assertTrue(templateByKey.isPresent());
}
@Test
public void testNotFindTemplate() {
when(repository.findById(TEMPLATE_KEY)).thenReturn(Optional.empty());
Optional<Template> templateByKey = templateManagementServiceBean.getTemplateByKey(TEMPLATE_KEY);
Assert.assertFalse(templateByKey.isPresent());
}
@Test
public void testTemplateExists() {
when(repository.existsById(TEMPLATE_KEY)).thenReturn(true);
Assert.assertTrue(templateManagementServiceBean.templateExists(TEMPLATE_KEY));
}
@Test
public void testCreateTemplate() {
Template template = templateManagementServiceBean.createTemplate(TEMPLATE_KEY, TEMPLATE_SOURCE);
Assert.assertEquals(template.getContent(), TEMPLATE_SOURCE);
Assert.assertEquals(template.getKey(), TEMPLATE_KEY);
verify(repository, times(1)).save(any(Template.class));
Assert.assertTrue(Duration.between(template.getLastModified(), Instant.now()).getSeconds() < 1);
}
private Template getTemplate() {
return Template.builder().content(TEMPLATE_SOURCE).key(TEMPLATE_KEY).build();
}
}

View File

@@ -0,0 +1,28 @@
{
"author": {
"name": "name",
"avatar": "https://example.com",
"url": "https://example.com"
},
"color" : {
"r": 255,
"g": 255,
"b": 255
},
"description": "description",
"thumbnail": "https://example.com",
"imageURL": "https://example.com",
"fields": [
{
"name": "name",
"value": "value",
"inline": "true"
}
],
"footer": {
"text": "text",
"icon": "https://example.com"
},
"timeStamp": "timestamp",
"additionalMessage": "additionalMessage"
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.templating.exception;
public class TemplatingException extends RuntimeException {
public TemplatingException(Throwable cause) {
super(cause);
}
}