diff --git a/OddJava/build.gradle b/OddJava/build.gradle index 278d88fe0..f71f87f00 100644 --- a/OddJava/build.gradle +++ b/OddJava/build.gradle @@ -1,39 +1,41 @@ plugins { - id 'org.springframework.boot' version '3.3.2' + id 'org.springframework.boot' version '3.5.6' id 'io.spring.dependency-management' version '1.1.6' id 'java' + id 'groovy' } group = 'com.oddball' version = '0.0.2-SNAPSHOT' -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - repositories { mavenCentral() + mavenLocal() } java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } } dependencies { - annotationProcessor 'org.projectlombok:lombok' - compileOnly 'org.projectlombok:lombok' - developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.h2database:h2:2.3.230' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.apache.commons:commons-csv:1.11.0' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.postgresql:postgresql' - testImplementation 'org.junit.jupiter:junit-jupiter:5.10.3' + annotationProcessor 'org.projectlombok:lombok' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.apache.commons:commons-csv:1.11.0' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.postgresql:postgresql' + + runtimeOnly 'com.h2database:h2:2.3.230' + runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator' + runtimeOnly 'com.github.tylerwilliams:performance-tracer:0.0.1' + + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.3' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.spockframework:spock-core:2.4-M6-groovy-4.0' + testImplementation 'org.spockframework:spock-spring:2.4-M6-groovy-4.0' } test { diff --git a/OddJava/gradle/wrapper/gradle-wrapper.properties b/OddJava/gradle/wrapper/gradle-wrapper.properties index 09523c0e5..2e1113280 100644 --- a/OddJava/gradle/wrapper/gradle-wrapper.properties +++ b/OddJava/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/OddJava/src/main/java/com/oddball/challenges/BadDay.java b/OddJava/src/main/java/com/oddball/challenges/BadDay.java new file mode 100644 index 000000000..ecdfe3e02 --- /dev/null +++ b/OddJava/src/main/java/com/oddball/challenges/BadDay.java @@ -0,0 +1,5 @@ +package com.oddball.challenges; + +import java.time.LocalDate; + +public record BadDay(long userId, String userName, LocalDate date) {} diff --git a/OddJava/src/main/java/com/oddball/challenges/BadDayController.java b/OddJava/src/main/java/com/oddball/challenges/BadDayController.java new file mode 100644 index 000000000..69ac836dd --- /dev/null +++ b/OddJava/src/main/java/com/oddball/challenges/BadDayController.java @@ -0,0 +1,27 @@ +package com.oddball.challenges; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDate; +import java.util.List; + +@RestController +@RequestMapping("/bad-day") +@RequiredArgsConstructor +public class BadDayController { + private final BadDayService badDayService; + + @GetMapping("/weeks/{weekToCheck}") + public List getBadWeeks(@PathVariable LocalDate weekToCheck) { + return badDayService.getBadWeeks(weekToCheck); + } + + @GetMapping("/streaks") + public List getBadDayStreaks() { + return badDayService.getBadDayStreaks(); + } +} diff --git a/OddJava/src/main/java/com/oddball/challenges/BadDayService.java b/OddJava/src/main/java/com/oddball/challenges/BadDayService.java new file mode 100644 index 000000000..ea05bc17c --- /dev/null +++ b/OddJava/src/main/java/com/oddball/challenges/BadDayService.java @@ -0,0 +1,93 @@ +package com.oddball.challenges; + +import com.oddball.challenges.mood.MoodRepository; +import com.oddball.challenges.stress.StressRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.stream.Stream; + +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toCollection; + +@Service +@RequiredArgsConstructor +public class BadDayService { + private static final int MIN_BAD_DAYS_THRESHOLD = 2; + private static final int MAX_BAD_DAY_STREAKS = 5; + + private final MoodRepository moodRepository; + private final StressRepository stressRepository; + + public List getBadWeeks(LocalDate weekToCheck) { + LocalDate startOfWeek = getStartOfWeek(weekToCheck); + LocalDate endOfWeek = startOfWeek.plusDays(6); + + Map> badDaysByUser = getBadDaysByUser(startOfWeek, endOfWeek); + + return badDaysByUser.values() + .stream() + .map(userBadDays -> { + // each batch of bad days belong to a single user, so we can just grab the first one to get the username + String userName = userBadDays.getFirst().userName(); + return new BadWeek(userName, userBadDays.size()); + }) + .filter(bw -> bw.numberOfBadDays() >= MIN_BAD_DAYS_THRESHOLD) + .sorted(comparing(BadWeek::numberOfBadDays).reversed()) + .toList(); + } + + public List getBadDayStreaks() { + Map> allBadDays = getBadDaysByUser(null, null); + + List> allBadDayStreaks = new ArrayList<>(); + for (TreeSet userBadDays : allBadDays.values()) { + List> userStreaks = userBadDays.stream() + .gather(BadDayStreakGatherer.INSTANCE) +// .peek(streak -> +// System.out.println("Found streak:\n" + streak.stream().map(o -> " " + o).collect(joining("\n")))) + .toList(); + allBadDayStreaks.addAll(userStreaks); + } + + return allBadDayStreaks.stream() + .sorted((a, b) -> Integer.compare(b.size(), a.size())) // largest streaks to smallest + .limit(MAX_BAD_DAY_STREAKS) + .map(streak -> { + BadDay firstBadDay = streak.getFirst(); + return new BadDayStreak(firstBadDay.userName(), firstBadDay.date(), streak.size()); + }) + .toList(); + } + + /** + * Get all bad days (from mood and stress) grouped by user for a given date range inclusive + * @param from Starting day (inclusive) to retrieve bad days for. Null = no lower bound + * @param to Ending day (inclusive) to retrieve bad days for. Null = no upper bound + * @return A mapping of user IDs to a sorted set of bad days (sorted by day) which fall within the specified date range + */ + private Map> getBadDaysByUser(LocalDate from, LocalDate to) { + List badMoodDays = moodRepository.getBadMoodDays(from, to); + List badStressDays = stressRepository.getBadStressDays(from, to); + + Stream allBadDays = Stream.concat(badMoodDays.stream(), badStressDays.stream()); + return allBadDays.collect( + groupingBy(BadDay::userId, toCollection(() -> new TreeSet<>(comparing(BadDay::date))))); + } + + /** + * @return The Sunday of the week that the given date falls within + */ + private static LocalDate getStartOfWeek(LocalDate date) { + return date.getDayOfWeek() == DayOfWeek.SUNDAY + ? date + : date.minusDays(date.getDayOfWeek().getValue()); + } +} diff --git a/OddJava/src/main/java/com/oddball/challenges/BadDayStreak.java b/OddJava/src/main/java/com/oddball/challenges/BadDayStreak.java new file mode 100644 index 000000000..b2af58579 --- /dev/null +++ b/OddJava/src/main/java/com/oddball/challenges/BadDayStreak.java @@ -0,0 +1,6 @@ +package com.oddball.challenges; + +import java.time.LocalDate; + +public record BadDayStreak(String userName, LocalDate startDate, int streakLength) { +} diff --git a/OddJava/src/main/java/com/oddball/challenges/BadDayStreakGatherer.java b/OddJava/src/main/java/com/oddball/challenges/BadDayStreakGatherer.java new file mode 100644 index 000000000..22f34b9f9 --- /dev/null +++ b/OddJava/src/main/java/com/oddball/challenges/BadDayStreakGatherer.java @@ -0,0 +1,57 @@ +package com.oddball.challenges; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Gatherer; + +/** + * A {@link Gatherer} that collects consecutive {@link BadDay}s into streaks and emits each one as a list. + * This gatherer assumes that the input stream of {@link BadDay}s are sorted in chronological order. + */ +class BadDayStreakGatherer implements Gatherer, List> { + static final BadDayStreakGatherer INSTANCE = new BadDayStreakGatherer(); + + private BadDayStreakGatherer() {} + + @Override + public Supplier> initializer() { + return ArrayList::new; + } + + @Override + public Gatherer.Integrator, BadDay, List> integrator() { + return Integrator.ofGreedy((currentStreak, nextDay, downstream) -> { + if (currentStreak.isEmpty()) { + // initial streak + currentStreak.add(nextDay); + return true; + } + + BadDay previousBadDay = currentStreak.getLast(); + boolean isConsecutive = nextDay.date().minusDays(1).equals(previousBadDay.date()); + if (isConsecutive) { + // streak continues + currentStreak.add(nextDay); + return true; + } + + // streak is broken - emit the current streak and start a new one + downstream.push(List.copyOf(currentStreak)); + currentStreak.clear(); + currentStreak.add(nextDay); + return true; + }); + } + + @Override + public BiConsumer, Downstream>> finisher() { + // emit the last streak if we have a running one + return (currentStreak, downstream) -> { + if (!currentStreak.isEmpty()) { + downstream.push(List.copyOf(currentStreak)); + } + }; + } +} \ No newline at end of file diff --git a/OddJava/src/main/java/com/oddball/challenges/BadWeek.java b/OddJava/src/main/java/com/oddball/challenges/BadWeek.java new file mode 100644 index 000000000..9b1d6b563 --- /dev/null +++ b/OddJava/src/main/java/com/oddball/challenges/BadWeek.java @@ -0,0 +1,3 @@ +package com.oddball.challenges; + +public record BadWeek(String userName, int numberOfBadDays) {} diff --git a/OddJava/src/main/java/com/oddball/challenges/OddJavaApp.java b/OddJava/src/main/java/com/oddball/challenges/OddJavaApp.java index 13103f158..cb26a3447 100644 --- a/OddJava/src/main/java/com/oddball/challenges/OddJavaApp.java +++ b/OddJava/src/main/java/com/oddball/challenges/OddJavaApp.java @@ -5,7 +5,7 @@ @SpringBootApplication public class OddJavaApp { - public static void main(String[] args) { + static void main(String[] args) { SpringApplication.run(OddJavaApp.class, args); } } \ No newline at end of file diff --git a/OddJava/src/main/java/com/oddball/challenges/mood/Mood.java b/OddJava/src/main/java/com/oddball/challenges/mood/Mood.java index 8e77b40fa..79dee2a42 100644 --- a/OddJava/src/main/java/com/oddball/challenges/mood/Mood.java +++ b/OddJava/src/main/java/com/oddball/challenges/mood/Mood.java @@ -1,21 +1,23 @@ package com.oddball.challenges.mood; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import java.util.Date; +import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.time.LocalDate; + @Getter @Setter @NoArgsConstructor @Entity -@Table(name = "moods") +@Table( + name = "moods", + indexes = { + @Index(name = "idx_mood_date", columnList = "date"), + @Index(name = "idx_mood_user", columnList = "userId"), + } +) public class Mood { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -26,18 +28,11 @@ public class Mood { private long userId; @Column(name = "date", columnDefinition = "date") - private Date date; + private LocalDate date; @Column(name = "mood", length = 1) private int mood; - @Column(name = "description", length = 255) + @Column(name = "description") private String description; - - Mood(long userId, Date date, int mood, String description) { - this.userId = userId; - this.date = date; - this.mood = mood; - this.description = description; - } } \ No newline at end of file diff --git a/OddJava/src/main/java/com/oddball/challenges/mood/MoodRepository.java b/OddJava/src/main/java/com/oddball/challenges/mood/MoodRepository.java index c8e3dabe9..0248dbf89 100644 --- a/OddJava/src/main/java/com/oddball/challenges/mood/MoodRepository.java +++ b/OddJava/src/main/java/com/oddball/challenges/mood/MoodRepository.java @@ -1,10 +1,27 @@ package com.oddball.challenges.mood; +import com.oddball.challenges.BadDay; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; + @Repository public interface MoodRepository extends CrudRepository { + @Query(""" + select new com.oddball.challenges.BadDay(m.userId, u.userName, m.date) + from Mood m + join User u on m.userId = u.id + where m.mood in (1, 2) + and (:startDate is null or m.date >= :startDate) + and (:endDate is null or m.date <= :endDate) + """) + List getBadMoodDays(@Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate); + } diff --git a/OddJava/src/main/java/com/oddball/challenges/stress/Stress.java b/OddJava/src/main/java/com/oddball/challenges/stress/Stress.java index 8f630dbbc..fff1fffbd 100644 --- a/OddJava/src/main/java/com/oddball/challenges/stress/Stress.java +++ b/OddJava/src/main/java/com/oddball/challenges/stress/Stress.java @@ -1,21 +1,23 @@ package com.oddball.challenges.stress; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import java.util.Date; +import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.time.LocalDate; + @Getter @Setter @NoArgsConstructor @Entity -@Table(name = "stress") +@Table( + name = "stress", + indexes = { + @Index(name = "idx_stress_date", columnList = "date"), + @Index(name = "idx_stress_user", columnList = "userId") + } +) public class Stress { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -26,14 +28,8 @@ public class Stress { private long userId; @Column(name="date", columnDefinition = "date") - private Date date; + private LocalDate date; @Column(name="stress", length = 1) private int stress; - - Stress(long userId, Date date, int stress) { - this.userId = userId; - this.date = date; - this.stress = stress; - } } \ No newline at end of file diff --git a/OddJava/src/main/java/com/oddball/challenges/stress/StressRepository.java b/OddJava/src/main/java/com/oddball/challenges/stress/StressRepository.java index 47d9392ab..c93d8b669 100644 --- a/OddJava/src/main/java/com/oddball/challenges/stress/StressRepository.java +++ b/OddJava/src/main/java/com/oddball/challenges/stress/StressRepository.java @@ -1,10 +1,27 @@ package com.oddball.challenges.stress; +import com.oddball.challenges.BadDay; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.util.List; + @Repository public interface StressRepository extends CrudRepository { + @Query(""" + select new com.oddball.challenges.BadDay(s.userId, u.userName, s.date) + from Stress s + join User u on s.userId = u.id + left join Mood m on m.userId=s.userId and m.date=s.date + where (s.stress in (4, 5) and coalesce(m.mood, 3) = 3) + and (:startDate is null or s.date >= :startDate) + and (:endDate is null or s.date <= :endDate) + """) + List getBadStressDays(@Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate); } diff --git a/OddJava/src/main/java/com/oddball/challenges/user/UserRepository.java b/OddJava/src/main/java/com/oddball/challenges/user/UserRepository.java index f5e6a3275..150dbd131 100644 --- a/OddJava/src/main/java/com/oddball/challenges/user/UserRepository.java +++ b/OddJava/src/main/java/com/oddball/challenges/user/UserRepository.java @@ -4,7 +4,5 @@ import org.springframework.stereotype.Repository; @Repository -public interface UserRepository extends CrudRepository { - -} +public interface UserRepository extends CrudRepository { } diff --git a/OddJava/src/main/resources/application.yml b/OddJava/src/main/resources/application.yml index be6d0209e..355c4c2fb 100644 --- a/OddJava/src/main/resources/application.yml +++ b/OddJava/src/main/resources/application.yml @@ -1,4 +1,6 @@ debug: false +server: + port: 8090 spring: datasource: url: jdbc:h2:mem:oddballdb @@ -11,4 +13,4 @@ spring: generate-ddl: true hibernate.ddl-auto: create-drop show-sql: true - output.ansi.enabled: always + output.ansi.enabled: always \ No newline at end of file diff --git a/OddJava/src/test/groovy/com/oddball/challenges/BadDayServiceSpec.groovy b/OddJava/src/test/groovy/com/oddball/challenges/BadDayServiceSpec.groovy new file mode 100644 index 000000000..fae015dee --- /dev/null +++ b/OddJava/src/test/groovy/com/oddball/challenges/BadDayServiceSpec.groovy @@ -0,0 +1,166 @@ +package com.oddball.challenges + +import com.oddball.challenges.mood.Mood +import com.oddball.challenges.stress.Stress +import com.oddball.challenges.user.User +import jakarta.persistence.EntityManager +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.context.annotation.Import +import spock.lang.Specification + +import javax.sql.DataSource +import java.time.LocalDate + +@DataJpaTest +@Import(BadDayService) +class BadDayServiceSpec extends Specification { + @Autowired + BadDayService badDayService + + @Autowired + DataSource dataSource + + @Autowired + EntityManager entityManager + + def setup() { + // clear existing test data out from import.sql since we'll manage this ourselves in the test + dataSource.connection.withCloseable { + it.createStatement().executeUpdate('DELETE FROM users') + it.createStatement().executeUpdate('DELETE FROM moods') + it.createStatement().executeUpdate('DELETE FROM stress') + } + } + + def 'get bad weeks returns correct data'() { + given: 'A week to query bad days for' + LocalDate sunday_9_28 = LocalDate.of(2025, 9, 28) + LocalDate monday_9_29 = sunday_9_28.plusDays(1) + LocalDate tuesday_9_30 = sunday_9_28.plusDays(2) + LocalDate wednesday_10_1 = sunday_9_28.plusDays(3) + + and: 'A user with a good day and a lot of bad days' + User userSomeBadDays = new User(name: 'someBadDays', userName: 'someBadDays_username', zipCode: '22222') + entityManager.persist(userSomeBadDays) + + // sunday (good mood and good stress - GOOD DAY) + entityManager.persist(new Mood(userId: userSomeBadDays.id, mood: 4, date: sunday_9_28)) + entityManager.persist(new Stress(userId: userSomeBadDays.id, stress: 3, date: sunday_9_28)) + + // monday (bad mood, good stress - BAD DAY) + entityManager.persist(new Mood(userId: userSomeBadDays.id, mood: 2, date: monday_9_29)) + entityManager.persist(new Stress(userId: userSomeBadDays.id, stress: 2, date: monday_9_29)) + + // tuesday (neutral mood, but bad stress - BAD DAY) + entityManager.persist(new Mood(userId: userSomeBadDays.id, mood: 3, date: tuesday_9_30)) + entityManager.persist(new Stress(userId: userSomeBadDays.id, stress: 4, date: tuesday_9_30)) + + // wednesday (no mood, but bad stress - BAD DAY) + entityManager.persist(new Stress(userId: userSomeBadDays.id, stress: 4, date: wednesday_10_1)) + + and: 'A user with only bad days, but fewer bad days than the previous' + User userAllBadDays = new User(name: 'allBadDays', userName: 'allBadDays_username', zipCode: '3333') + entityManager.persist(userAllBadDays) + + // sunday (bad mood and good stress - BAD DAY) + entityManager.persist(new Mood(userId: userAllBadDays.id, mood: 2, date: sunday_9_28)) + entityManager.persist(new Stress(userId: userAllBadDays.id, stress: 1, date: sunday_9_28)) + + // monday (bad mood, bad stress - BAD DAY) + entityManager.persist(new Mood(userId: userAllBadDays.id, mood: 1, date: monday_9_29)) + entityManager.persist(new Stress(userId: userAllBadDays.id, stress: 4, date: monday_9_29)) + + and: 'A user with no bad days' + User userNoBadDays = new User(name: 'noBadDays', userName: 'noBadDays_username', zipCode: '55124') + entityManager.persist(userNoBadDays) + + // good mood, good stress for all 7 days + (0..6).each { dayOffset -> + entityManager.persist(new Mood(userId: userNoBadDays.id, date: sunday_9_28.plusDays(dayOffset), mood: 4)) + entityManager.persist(new Stress(userId: userNoBadDays.id, date: sunday_9_28, stress: 1)) + } + + when: 'we ask for bad weeks' + List badWeeks = badDayService.getBadWeeks(sunday_9_28) + + then: 'we have 2 entries for the 2 users with bad days' + badWeeks.size() == 2 + + and: 'the first entry is for the user with the most bad days' + BadWeek mostBadDays = badWeeks[0] + verifyAll(mostBadDays) { + userName() == userSomeBadDays.userName + numberOfBadDays() == 3 + } + + and: 'the second entry is for the user with only bad days' + BadWeek secondMostBadDays = badWeeks[1] + verifyAll(secondMostBadDays) { + userName() == userAllBadDays.userName + numberOfBadDays() == 2 + } + } + + def 'get bad day streaks returns correct data'() { + given: 'An arbitrary day to start creating moods and stresses for' + LocalDate sunday_9_28 = LocalDate.of(2025, 9, 28) + LocalDate monday_9_29 = sunday_9_28.plusDays(1) + LocalDate tuesday_9_30 = sunday_9_28.plusDays(2) + LocalDate wednesday_10_1 = sunday_9_28.plusDays(3) + LocalDate thursday_10_2 = sunday_9_28.plusDays(4) + LocalDate friday_10_3 = sunday_9_28.plusDays(5) + LocalDate saturday_10_4 = sunday_9_28.plusDays(6) + + and: 'A user with a streak of 2 bad days, a good day, then 3 bad days' + User userWithStreaks = new User(name: 'streaks', userName: 'streaks_username', zipCode: '44444') + entityManager.persist(userWithStreaks) + + // sunday (bad mood, good stress - BAD DAY) + entityManager.persist(new Mood(userId: userWithStreaks.id, mood: 2, date: sunday_9_28)) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 2, date: sunday_9_28)) + + // monday (neutral mood, but bad stress - BAD DAY) + entityManager.persist(new Mood(userId: userWithStreaks.id, mood: 3, date: monday_9_29)) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 4, date: monday_9_29)) + + // tuesday (good mood and good stress - GOOD DAY) + entityManager.persist(new Mood(userId: userWithStreaks.id, mood: 4, date: tuesday_9_30)) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 1, date: tuesday_9_30)) + + // wednesday (no mood, but bad stress - BAD DAY) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 4, date: wednesday_10_1)) + + // thursday (bad mood and bad stress - BAD DAY) + entityManager.persist(new Mood(userId: userWithStreaks.id, mood: 1, date: thursday_10_2)) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 4, date: thursday_10_2)) + + // friday (bad mood and neutral stress - BAD DAY) + entityManager.persist(new Mood(userId: userWithStreaks.id, mood: 2, date: friday_10_3)) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 3, date: friday_10_3)) + + // saturday (good mood and good stress - GOOD DAY) + entityManager.persist(new Mood(userId: userWithStreaks.id, mood: 4, date: saturday_10_4)) + entityManager.persist(new Stress(userId: userWithStreaks.id, stress: 1, date: saturday_10_4)) + + when: 'we ask for bad day streaks' + List badDayStreaks = badDayService.getBadDayStreaks() + + then: 'we have 2 entries for the 2 bad day streaks, ordered by their length' + badDayStreaks.size() == 2 + + BadDayStreak longestStreak = badDayStreaks[0] + verifyAll(longestStreak) { + userName() == userWithStreaks.userName + streakLength() == 3 + startDate() == wednesday_10_1 + } + + BadDayStreak shorterStreak = badDayStreaks[1] + verifyAll(shorterStreak) { + userName() == userWithStreaks.userName + streakLength() == 2 + startDate() == sunday_9_28 + } + } +} diff --git a/OddJava/test_report_1.bat b/OddJava/test_report_1.bat new file mode 100644 index 000000000..0c48c5eea --- /dev/null +++ b/OddJava/test_report_1.bat @@ -0,0 +1 @@ +curl http://localhost:8090/bad-day/weeks/2017-05-01 | jq \ No newline at end of file diff --git a/OddJava/test_report_2.bat b/OddJava/test_report_2.bat new file mode 100644 index 000000000..acdb64032 --- /dev/null +++ b/OddJava/test_report_2.bat @@ -0,0 +1 @@ +curl http://localhost:8090/bad-day/streaks | jq \ No newline at end of file