Compare commits

..

9 Commits

Author SHA1 Message Date
Wojtek Krzywiec
6dc153309b add spock reports 2020-10-23 07:58:22 +02:00
Wojtek Krzywiec
3218fb056e small changes 2020-10-20 07:05:30 +02:00
Wojtek Krzywiec
8306b5158a move stubed services to a tests class 2020-10-14 06:42:34 +02:00
Wojtek Krzywiec
967a349b4c bring back JUnit4 deps 2020-10-13 06:34:26 +02:00
Wojtek Krzywiec
42b08c8995 BorrowingFacadeSpec added 2020-09-20 12:32:04 +02:00
Wojtek Krzywiec
58a9a0f95b add spock dependencies to pom.xml 2020-09-19 12:41:34 +02:00
Wojtek Krzywiec
9b0c37ffd7 Merge branch 'polishing' 2020-06-14 15:09:45 +02:00
Wojtek Krzywiec
53939113f2 fixes 2020-06-14 15:08:53 +02:00
Wojtek Krzywiec
a9fd37ec5a Merge pull request #18 from wkrzywiec/polishing
Polishing
2020-06-08 21:33:59 +02:00
15 changed files with 248 additions and 129 deletions

51
pom.xml
View File

@@ -91,27 +91,48 @@
<version>1.2.32</version>
</dependency>
<!-- TEST dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.3-groovy-2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.athaydes</groupId>
<artifactId>spock-reports</artifactId>
<version>1.8.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>0.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
</dependency>
</dependencies>
<build>
@@ -144,8 +165,25 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<reportsDirectory>${surefire.and.failsafe.report.dir}</reportsDirectory>
<includes>
<include>**/*Spec.java</include>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.10.0</version>
<executions>
<execution>
<goals>
<goal>addTestSources</goal>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
@@ -263,6 +301,7 @@
<sonar.sources>.</sonar.sources>
<sonar.inclusions>src/main/java/**,src/main/resources/**</sonar.inclusions>
<sonar.exclusions>${code.coverage.exclusions}</sonar.exclusions>
<sonat.tests>src/test/groovy,src/test/java</sonat.tests>
<sonar.projectKey>wkrzywiec_library-hexagonal</sonar.projectKey>
<sonar.organization>wkrzywiec</sonar.organization>
<sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>

View File

@@ -11,7 +11,7 @@ public class OverdueReservationScheduler {
@Qualifier("CancelOverdueReservations")
private final CancelOverdueReservations overdueReservations;
@Scheduled(fixedRate = 10 * 1000)
@Scheduled(fixedRate = 60 * 1000)
public void checkOverdueReservations(){
overdueReservations.cancelOverdueReservations();
}

View File

@@ -6,7 +6,6 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservatio
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservedEvent;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.DueDate;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.MakeBookAvailableCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReservation;
@@ -24,8 +23,6 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.incoming.Reser
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOverdueReservations, BorrowBook, GiveBackBook {
@@ -61,10 +58,9 @@ public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOv
@Override
public void cancelOverdueReservations() {
DueDate dueDate = new DueDate(Instant.now().plus(3L, ChronoUnit.DAYS));
List<OverdueReservation> overdueReservationList = database.findReservationsAfter(dueDate);
List<OverdueReservation> overdueReservationList = database.findReservationsForMoreThan(3L);
overdueReservationList.forEach(
overdue -> database.save(new AvailableBook(overdue.getBookIdentificationAsLong())));
overdueBook -> database.save(new AvailableBook(overdueBook.getBookIdentificationAsLong())));
}
@Override

View File

@@ -2,10 +2,14 @@ package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.TooManyBooksAssignedToUserException;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.LinkedHashMap;
import java.util.List;
@EqualsAndHashCode
@ToString
public class ActiveUser {
private final Long id;

View File

@@ -1,8 +1,10 @@
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@EqualsAndHashCode
@ToString
public class AvailableBook implements Book {
private final Long id;

View File

@@ -3,8 +3,10 @@ package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class BookReservationCommand {

View File

@@ -1,6 +1,7 @@
package io.wkrzywiec.hexagonal.library.domain.borrowing.core.model;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.Instant;

View File

@@ -17,7 +17,7 @@ public interface BorrowingDatabase {
void save(BorrowedBook borrowedBook);
Optional<AvailableBook> getAvailableBook(Long bookId);
Optional<ActiveUser> getActiveUser(Long userId);
List<OverdueReservation> findReservationsAfter(DueDate dueDate);
List<OverdueReservation> findReservationsForMoreThan(Long days);
Optional<ReservedBook> getReservedBook(Long bookId);
Optional<BorrowedBook> getBorrowedBook(Long bookId);
}

View File

@@ -110,11 +110,11 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase {
}
@Override
public List<OverdueReservation> findReservationsAfter(DueDate dueDate) {
public List<OverdueReservation> findReservationsForMoreThan(Long days) {
List<OverdueReservationEntity> entities = jdbcTemplate.query(
"SELECT id AS reservationId, book_id AS bookIdentification FROM reserved WHERE reserved_date > ?",
"SELECT id AS reservationId, book_id AS bookIdentification FROM reserved WHERE DATEADD(day, ?, reserved_date) > NOW()",
new BeanPropertyRowMapper<OverdueReservationEntity>(OverdueReservationEntity.class),
Timestamp.from(dueDate.asInstant()));
days);
return entities.stream()
.map(entity -> new OverdueReservation(entity.getReservationId(), entity.getBookIdentification()))
.collect(Collectors.toList());

View File

@@ -0,0 +1,42 @@
package io.wkrzywiec.hexagonal.library.domain.borrowing
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.BorrowingFacade
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.MakeBookAvailableCommand
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Title("Unit tests for borrowing book logic")
@Narrative("""
BorrowingFacade class encapsulate a logic of
managing books in a library. It contains methods
responsible for registering new book, borrowing it,
reserving it and taking it back.
""")
@Subject(BorrowingFacade)
class BorrowingFacadeSpec extends Specification {
private BorrowingFacade facade
private InMemoryBorrowingDatabase database
private BorrowingEventPublisher eventPublisher
def setup() {
database = new InMemoryBorrowingDatabase()
eventPublisher = new BorrowingEventPublisherFake()
facade = new BorrowingFacade(database, eventPublisher)
}
def "Make a book available"() {
given: "prepare a command"
def makeBookAvailableCommand = new MakeBookAvailableCommand(100)
when: "receive MakeBookAvailableCommand"
facade.handle(makeBookAvailableCommand)
then: "check database to have this book as available"
database.availableBooks[100L] == new AvailableBook(100)
}
}

View File

@@ -1,12 +0,0 @@
package io.wkrzywiec.hexagonal.library.domain.borrowing;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservedEvent;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher;
public class BorrowingEventPublisherFake implements BorrowingEventPublisher {
@Override
public void publish(BookReservedEvent event) {
}
}

View File

@@ -4,14 +4,19 @@ import io.wkrzywiec.hexagonal.library.domain.borrowing.core.BorrowingFacade;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ActiveUser;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservationCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BookReservedEvent;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowBookCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.GiveBackBookCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.MakeBookAvailableCommand;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReservation;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDetails;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationId;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.ActiveUserNotFoundException;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.AvailableBookNotFoundExeption;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.exception.TooManyBooksAssignedToUserException;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingEventPublisher;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.jupiter.api.BeforeEach;
@@ -23,12 +28,16 @@ import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class BorrowingFacadeTest {
class BorrowingFacadeTest {
private BorrowingFacade facade;
private InMemoryBorrowingDatabase database;
@@ -143,7 +152,7 @@ public class BorrowingFacadeTest {
public void givenBookIsReserved_when3daysPass_thenBookIsAvailable(){
//given
ReservedBook reservedBook = ReservationTestData.anyReservedBook(100L, 100L);
changeReservationTimeFor(reservedBook, 4L);
changeReservationTimeFor(reservedBook, Instant.now().minus(4, ChronoUnit.DAYS));
database.reservedBooks.put(100L, reservedBook);
//when
@@ -158,7 +167,7 @@ public class BorrowingFacadeTest {
public void givenBookIsReserved_when2daysPass_thenBookIsStillReserved(){
//given
ReservedBook reservedBook = ReservationTestData.anyReservedBook(100L, 100L);
changeReservationTimeFor(reservedBook, 2L);
changeReservationTimeFor(reservedBook, Instant.now().minus(2, ChronoUnit.DAYS));
database.reservedBooks.put(100L, reservedBook);
//when
@@ -168,14 +177,6 @@ public class BorrowingFacadeTest {
assertEquals(1, database.reservedBooks.size());
}
private void changeReservationTimeFor(ReservedBook reservedBook, Long daysFromNow) {
try {
FieldUtils.writeField(reservedBook, "reservedDate", Instant.now().plus(daysFromNow, ChronoUnit.DAYS), true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Test
@DisplayName("Successfully borrow a book")
public void givenReservedBookAndActiveUser_whenBorrowing_thenBookIsBorrowed(){
@@ -213,4 +214,89 @@ public class BorrowingFacadeTest {
assertEquals(1, database.availableBooks.size());
assertEquals(0, database.activeUsers.get(activeUser.getIdAsLong()).getBorrowedBookList().size());
}
private void changeReservationTimeFor(ReservedBook reservedBook, Instant reservationDate) {
try {
FieldUtils.writeField(reservedBook, "reservedDate", reservationDate, true);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class InMemoryBorrowingDatabase implements BorrowingDatabase {
ConcurrentHashMap<Long, ActiveUser> activeUsers = new ConcurrentHashMap<>();
ConcurrentHashMap<Long, AvailableBook> availableBooks = new ConcurrentHashMap<>();
ConcurrentHashMap<Long, ReservedBook> reservedBooks = new ConcurrentHashMap<>();
ConcurrentHashMap<Long, BorrowedBook> borrowedBooks = new ConcurrentHashMap<>();
@Override
public void save(AvailableBook availableBook) {
availableBooks.put(availableBook.getIdAsLong(), availableBook);
reservedBooks.remove(availableBook.getIdAsLong());
borrowedBooks.remove(availableBook.getIdAsLong());
}
@Override
public Optional<AvailableBook> getAvailableBook(Long bookId) {
if (availableBooks.containsKey(bookId)) {
return Optional.of(availableBooks.get(bookId));
} else {
return Optional.empty();
}
}
@Override
public Optional<ActiveUser> getActiveUser(Long userId) {
if (activeUsers.containsKey(userId)) {
return Optional.of(activeUsers.get(userId));
} else {
return Optional.empty();
}
}
@Override
public ReservationDetails save(ReservedBook reservedBook) {
Long reservationId = new Random().nextLong();
availableBooks.remove(reservedBook.getIdAsLong());
reservedBooks.put(reservationId, reservedBook);
return new ReservationDetails(new ReservationId(reservationId), reservedBook);
}
@Override
public void save(BorrowedBook borrowedBook) {
reservedBooks.remove(borrowedBook.getIdAsLong());
borrowedBooks.put(borrowedBook.getIdAsLong(), borrowedBook);
}
@Override
public List<OverdueReservation> findReservationsForMoreThan(Long days) {
return reservedBooks.values().stream()
.filter(reservedBook ->
Instant.now().isAfter(
reservedBook.getReservedDateAsInstant().plus(days, ChronoUnit.DAYS)))
.map(reservedBook ->
new OverdueReservation(
1L,
reservedBook.getIdAsLong()))
.collect(Collectors.toList());
}
@Override
public Optional<ReservedBook> getReservedBook(Long bookId) {
return Optional.of(reservedBooks.get(bookId));
}
@Override
public Optional<BorrowedBook> getBorrowedBook(Long bookId) {
return Optional.of(borrowedBooks.get(bookId));
}
}
class BorrowingEventPublisherFake implements BorrowingEventPublisher {
@Override
public void publish(BookReservedEvent event) {
}
}

View File

@@ -1,87 +0,0 @@
package io.wkrzywiec.hexagonal.library.domain.borrowing;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ActiveUser;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.AvailableBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.BorrowedBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.DueDate;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.OverdueReservation;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationDetails;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservationId;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.model.ReservedBook;
import io.wkrzywiec.hexagonal.library.domain.borrowing.core.ports.outgoing.BorrowingDatabase;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class InMemoryBorrowingDatabase implements BorrowingDatabase {
ConcurrentHashMap<Long, ActiveUser> activeUsers = new ConcurrentHashMap<>();
ConcurrentHashMap<Long, AvailableBook> availableBooks = new ConcurrentHashMap<>();
ConcurrentHashMap<Long, ReservedBook> reservedBooks = new ConcurrentHashMap<>();
ConcurrentHashMap<Long, BorrowedBook> borrowedBooks = new ConcurrentHashMap<>();
@Override
public void save(AvailableBook availableBook) {
availableBooks.put(availableBook.getIdAsLong(), availableBook);
reservedBooks.remove(availableBook.getIdAsLong());
borrowedBooks.remove(availableBook.getIdAsLong());
}
@Override
public Optional<AvailableBook> getAvailableBook(Long bookId) {
if (availableBooks.containsKey(bookId)) {
return Optional.of(availableBooks.get(bookId));
} else {
return Optional.empty();
}
}
@Override
public Optional<ActiveUser> getActiveUser(Long userId) {
if (activeUsers.containsKey(userId)) {
return Optional.of(activeUsers.get(userId));
} else {
return Optional.empty();
}
}
@Override
public ReservationDetails save(ReservedBook reservedBook) {
Long reservationId = new Random().nextLong();
availableBooks.remove(reservedBook.getIdAsLong());
reservedBooks.put(reservationId, reservedBook);
return new ReservationDetails(new ReservationId(reservationId), reservedBook);
}
@Override
public void save(BorrowedBook borrowedBook) {
reservedBooks.remove(borrowedBook.getIdAsLong());
borrowedBooks.put(borrowedBook.getIdAsLong(), borrowedBook);
}
@Override
public List<OverdueReservation> findReservationsAfter(DueDate dueDate) {
return reservedBooks.values().stream()
.filter(reservedBook ->
reservedBook.getReservedDateAsInstant()
.isAfter(dueDate.asInstant()))
.map(reservedBook ->
new OverdueReservation(
1L,
reservedBook.getIdAsLong()))
.collect(Collectors.toList());
}
@Override
public Optional<ReservedBook> getReservedBook(Long bookId) {
return Optional.of(reservedBooks.get(bookId));
}
@Override
public Optional<BorrowedBook> getBorrowedBook(Long bookId) {
return Optional.of(borrowedBooks.get(bookId));
}
}

View File

@@ -147,7 +147,6 @@ public class BorrowingDatabaseAdapterITCase {
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
public void shouldFindOverdueReservations(){
//given
DueDate thirdDayAfterReservation = new DueDate(Instant.now().plus(3, ChronoUnit.DAYS));
Long overdueBookId = databaseHelper.getHomoDeusBookId();
Long johnDoeUserId = databaseHelper.getJohnDoeUserId();
jdbcTemplate.update(
@@ -157,7 +156,7 @@ public class BorrowingDatabaseAdapterITCase {
Instant.now().plus(4, ChronoUnit.DAYS));
//when
OverdueReservation overdueReservation = database.findReservationsAfter(thirdDayAfterReservation).get(0);
OverdueReservation overdueReservation = database.findReservationsForMoreThan(3L).get(0);
//then
assertEquals(overdueBookId, overdueReservation.getBookIdentificationAsLong());

View File

@@ -0,0 +1,47 @@
# Name of the implementation class(es) of report creator(s) to enable (separate multiple entries with commas)
# Currently supported classes are:
# 1. com.athaydes.spockframework.report.internal.HtmlReportCreator
# 2. com.athaydes.spockframework.report.template.TemplateReportCreator
com.athaydes.spockframework.report.IReportCreator=com.athaydes.spockframework.report.internal.HtmlReportCreator
# Set properties of the report creator
# For the HtmlReportCreator, the only properties available are
# (the location of the css files is relative to the classpath):
com.athaydes.spockframework.report.internal.HtmlReportCreator.featureReportCss=spock-feature-report.css
com.athaydes.spockframework.report.internal.HtmlReportCreator.summaryReportCss=spock-summary-report.css
com.athaydes.spockframework.report.internal.HtmlReportCreator.printThrowableStackTrace=false
com.athaydes.spockframework.report.internal.HtmlReportCreator.inlineCss=true
com.athaydes.spockframework.report.internal.HtmlReportCreator.enabled=true
# options are: "class_name_and_title", "class_name", "title"
com.athaydes.spockframework.report.internal.HtmlReportCreator.specSummaryNameOption=class_name_and_title
# exclude Specs Table of Contents
com.athaydes.spockframework.report.internal.HtmlReportCreator.excludeToc=false
# Output directory (where the spock reports will be created) - relative to working directory
com.athaydes.spockframework.report.outputDir=target/spock-reports
# Output directory where to store the aggregated JSON report (used to support parallel builds)
com.athaydes.spockframework.report.aggregatedJsonReportDir=
# If set to true, hides blocks which do not have any description
com.athaydes.spockframework.report.hideEmptyBlocks=false
# Set the name of the project under test so it can be displayed in the report
com.athaydes.spockframework.report.projectName=
# Set the version of the project under test so it can be displayed in the report
com.athaydes.spockframework.report.projectVersion=Unknown
# Show the source code for each block
com.athaydes.spockframework.report.showCodeBlocks=true
# Set the root location of the Spock test source code (only used if showCodeBlocks is 'true')
com.athaydes.spockframework.report.testSourceRoots=src/test/groovy
# Set properties specific to the TemplateReportCreator
com.athaydes.spockframework.report.template.TemplateReportCreator.specTemplateFile=/templateReportCreator/spec-template.md
com.athaydes.spockframework.report.template.TemplateReportCreator.reportFileExtension=md
com.athaydes.spockframework.report.template.TemplateReportCreator.summaryTemplateFile=/templateReportCreator/summary-template.md
com.athaydes.spockframework.report.template.TemplateReportCreator.summaryFileName=summary.md
com.athaydes.spockframework.report.template.TemplateReportCreator.enabled=true