From 8470c548d4af1b171e0bf6aa1369d1d27f9472c7 Mon Sep 17 00:00:00 2001 From: John King <53827445+jejking-tw@users.noreply.github.com> Date: Sat, 27 Apr 2024 16:50:55 +0200 Subject: [PATCH] adds spotless plugin and applies Google Java styleguide (#38) --- .github/workflows/gradle.yml | 22 +- build.gradle.kts | 8 + gradle.properties | 3 +- settings.gradle.kts | 2 + .../java/uk/tw/energy/EndpointTest.java | 124 +++++----- src/main/java/uk/tw/energy/App.java | 6 +- .../SeedingApplicationDataConfiguration.java | 96 ++++---- .../controller/MeterReadingController.java | 60 ++--- .../PricePlanComparatorController.java | 95 ++++---- .../tw/energy/domain/ElectricityReading.java | 4 +- .../uk/tw/energy/domain/MeterReadings.java | 4 +- .../java/uk/tw/energy/domain/PricePlan.java | 87 +++---- .../ElectricityReadingsGenerator.java | 31 +-- .../uk/tw/energy/service/AccountService.java | 17 +- .../energy/service/MeterReadingService.java | 29 ++- .../tw/energy/service/PricePlanService.java | 96 ++++---- .../energy/builders/MeterReadingsBuilder.java | 41 ++-- .../MeterReadingControllerTest.java | 160 ++++++------ .../PricePlanComparatorControllerTest.java | 228 ++++++++++-------- .../uk/tw/energy/domain/PricePlanTest.java | 80 +++--- .../tw/energy/service/AccountServiceTest.java | 34 +-- .../service/MeterReadingServiceTest.java | 36 +-- 22 files changed, 667 insertions(+), 596 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 49eedb42..bd5e0369 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -26,13 +26,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 - - name: Run Tests and Build - run: ./gradlew clean build + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + - name: Run Linter + run: ./gradlew spotlessCheck + - name: Run Build + run: ./gradlew build diff --git a/build.gradle.kts b/build.gradle.kts index cfa89bff..5ab7064f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ plugins { id("org.springframework.boot") id("io.spring.dependency-management") id("com.github.ben-manes.versions") + id("com.diffplug.spotless") } java { @@ -92,4 +93,11 @@ tasks.withType { isNonStable(candidate.version) } gradleReleaseChannel="current" +} + +spotless { + java { + googleJavaFormat() + formatAnnotations() + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 7d8c0c8c..5833dd85 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,4 @@ versions_version=0.51.0 spring_boot_plugin_version=3.2.5 -spring_dependency_management_plugin_version=1.1.4 \ No newline at end of file +spring_dependency_management_plugin_version=1.1.4 +spotless_version=6.25.0 \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index dd30e0f6..845f2754 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,9 +4,11 @@ pluginManagement { val versions_version: String by settings val spring_boot_plugin_version: String by settings val spring_dependency_management_plugin_version: String by settings + val spotless_version: String by settings plugins { id("io.spring.dependency-management") version spring_dependency_management_plugin_version id("org.springframework.boot") version spring_boot_plugin_version id("com.github.ben-manes.versions") version versions_version + id("com.diffplug.spotless") version spotless_version } } diff --git a/src/functional-test/java/uk/tw/energy/EndpointTest.java b/src/functional-test/java/uk/tw/energy/EndpointTest.java index 8504e594..dec20f83 100644 --- a/src/functional-test/java/uk/tw/energy/EndpointTest.java +++ b/src/functional-test/java/uk/tw/energy/EndpointTest.java @@ -1,5 +1,7 @@ package uk.tw.energy; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -14,69 +16,73 @@ import uk.tw.energy.builders.MeterReadingsBuilder; import uk.tw.energy.domain.MeterReadings; -import static org.assertj.core.api.Assertions.assertThat; - @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class) public class EndpointTest { - @Autowired - private TestRestTemplate restTemplate; - @Autowired - private ObjectMapper mapper; - - @Test - public void shouldStoreReadings() throws JsonProcessingException { - MeterReadings meterReadings = new MeterReadingsBuilder().generateElectricityReadings().build(); - HttpEntity entity = getStringHttpEntity(meterReadings); - - ResponseEntity response = restTemplate.postForEntity("/readings/store", entity, String.class); - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - public void givenMeterIdShouldReturnAMeterReadingAssociatedWithMeterId() throws JsonProcessingException { - String smartMeterId = "bob"; - populateMeterReadingsForMeter(smartMeterId); - - ResponseEntity response = restTemplate.getForEntity("/readings/read/" + smartMeterId, String.class); + @Autowired private TestRestTemplate restTemplate; + @Autowired private ObjectMapper mapper; - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } + @Test + public void shouldStoreReadings() throws JsonProcessingException { + MeterReadings meterReadings = new MeterReadingsBuilder().generateElectricityReadings().build(); + HttpEntity entity = getStringHttpEntity(meterReadings); - @Test - public void shouldCalculateAllPrices() throws JsonProcessingException { - String smartMeterId = "bob"; - populateMeterReadingsForMeter(smartMeterId); - - ResponseEntity response = restTemplate.getForEntity("/price-plans/compare-all/" + smartMeterId, String.class); - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - @Test - public void givenMeterIdAndLimitShouldReturnRecommendedCheapestPricePlans() throws JsonProcessingException { - String smartMeterId = "bob"; - populateMeterReadingsForMeter(smartMeterId); - - ResponseEntity response = - restTemplate.getForEntity("/price-plans/recommend/" + smartMeterId + "?limit=2", String.class); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } - - private HttpEntity getStringHttpEntity(Object object) throws JsonProcessingException { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - String jsonMeterData = mapper.writeValueAsString(object); - return (HttpEntity) new HttpEntity(jsonMeterData, headers); - } - - private void populateMeterReadingsForMeter(String smartMeterId) throws JsonProcessingException { - MeterReadings readings = new MeterReadingsBuilder().setSmartMeterId(smartMeterId) - .generateElectricityReadings(20) - .build(); - - HttpEntity entity = getStringHttpEntity(readings); + ResponseEntity response = restTemplate.postForEntity("/readings/store", entity, String.class); - } + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + public void givenMeterIdShouldReturnAMeterReadingAssociatedWithMeterId() + throws JsonProcessingException { + String smartMeterId = "bob"; + populateMeterReadingsForMeter(smartMeterId); + + ResponseEntity response = + restTemplate.getForEntity("/readings/read/" + smartMeterId, String.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + public void shouldCalculateAllPrices() throws JsonProcessingException { + String smartMeterId = "bob"; + populateMeterReadingsForMeter(smartMeterId); + + ResponseEntity response = + restTemplate.getForEntity("/price-plans/compare-all/" + smartMeterId, String.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + public void givenMeterIdAndLimitShouldReturnRecommendedCheapestPricePlans() + throws JsonProcessingException { + String smartMeterId = "bob"; + populateMeterReadingsForMeter(smartMeterId); + + ResponseEntity response = + restTemplate.getForEntity( + "/price-plans/recommend/" + smartMeterId + "?limit=2", String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + private HttpEntity getStringHttpEntity(Object object) throws JsonProcessingException { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + String jsonMeterData = mapper.writeValueAsString(object); + return (HttpEntity) new HttpEntity(jsonMeterData, headers); + } + + private void populateMeterReadingsForMeter(String smartMeterId) throws JsonProcessingException { + MeterReadings readings = + new MeterReadingsBuilder() + .setSmartMeterId(smartMeterId) + .generateElectricityReadings(20) + .build(); + + HttpEntity entity = getStringHttpEntity(readings); + restTemplate.postForEntity("/readings/store", entity, String.class); + } } diff --git a/src/main/java/uk/tw/energy/App.java b/src/main/java/uk/tw/energy/App.java index 7d6f868f..38a5cf76 100644 --- a/src/main/java/uk/tw/energy/App.java +++ b/src/main/java/uk/tw/energy/App.java @@ -6,7 +6,7 @@ @SpringBootApplication public class App { - public static void main(String[] args) { - SpringApplication.run(App.class); - } + public static void main(String[] args) { + SpringApplication.run(App.class); + } } diff --git a/src/main/java/uk/tw/energy/SeedingApplicationDataConfiguration.java b/src/main/java/uk/tw/energy/SeedingApplicationDataConfiguration.java index 3c32673e..c93822ba 100644 --- a/src/main/java/uk/tw/energy/SeedingApplicationDataConfiguration.java +++ b/src/main/java/uk/tw/energy/SeedingApplicationDataConfiguration.java @@ -1,7 +1,14 @@ package uk.tw.energy; +import static java.util.Collections.emptyList; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -10,56 +17,55 @@ import uk.tw.energy.domain.PricePlan; import uk.tw.energy.generator.ElectricityReadingsGenerator; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static java.util.Collections.emptyList; - @Configuration public class SeedingApplicationDataConfiguration { - private static final String MOST_EVIL_PRICE_PLAN_ID = "price-plan-0"; - private static final String RENEWABLES_PRICE_PLAN_ID = "price-plan-1"; - private static final String STANDARD_PRICE_PLAN_ID = "price-plan-2"; + private static final String MOST_EVIL_PRICE_PLAN_ID = "price-plan-0"; + private static final String RENEWABLES_PRICE_PLAN_ID = "price-plan-1"; + private static final String STANDARD_PRICE_PLAN_ID = "price-plan-2"; - @Bean - public List pricePlans() { - final List pricePlans = new ArrayList<>(); - pricePlans.add(new PricePlan(MOST_EVIL_PRICE_PLAN_ID, "Dr Evil's Dark Energy", BigDecimal.TEN, emptyList())); - pricePlans.add(new PricePlan(RENEWABLES_PRICE_PLAN_ID, "The Green Eco", BigDecimal.valueOf(2), emptyList())); - pricePlans.add(new PricePlan(STANDARD_PRICE_PLAN_ID, "Power for Everyone", BigDecimal.ONE, emptyList())); - return pricePlans; - } + @Bean + public List pricePlans() { + final List pricePlans = new ArrayList<>(); + pricePlans.add( + new PricePlan( + MOST_EVIL_PRICE_PLAN_ID, "Dr Evil's Dark Energy", BigDecimal.TEN, emptyList())); + pricePlans.add( + new PricePlan( + RENEWABLES_PRICE_PLAN_ID, "The Green Eco", BigDecimal.valueOf(2), emptyList())); + pricePlans.add( + new PricePlan(STANDARD_PRICE_PLAN_ID, "Power for Everyone", BigDecimal.ONE, emptyList())); + return pricePlans; + } - @Bean - public Map> perMeterElectricityReadings() { - final Map> readings = new HashMap<>(); - final ElectricityReadingsGenerator electricityReadingsGenerator = new ElectricityReadingsGenerator(); - smartMeterToPricePlanAccounts() - .keySet() - .forEach(smartMeterId -> readings.put(smartMeterId, electricityReadingsGenerator.generate(20))); - return readings; - } + @Bean + public Map> perMeterElectricityReadings() { + final Map> readings = new HashMap<>(); + final ElectricityReadingsGenerator electricityReadingsGenerator = + new ElectricityReadingsGenerator(); + smartMeterToPricePlanAccounts() + .keySet() + .forEach( + smartMeterId -> readings.put(smartMeterId, electricityReadingsGenerator.generate(20))); + return readings; + } - @Bean - public Map smartMeterToPricePlanAccounts() { - final Map smartMeterToPricePlanAccounts = new HashMap<>(); - smartMeterToPricePlanAccounts.put("smart-meter-0", MOST_EVIL_PRICE_PLAN_ID); - smartMeterToPricePlanAccounts.put("smart-meter-1", RENEWABLES_PRICE_PLAN_ID); - smartMeterToPricePlanAccounts.put("smart-meter-2", MOST_EVIL_PRICE_PLAN_ID); - smartMeterToPricePlanAccounts.put("smart-meter-3", STANDARD_PRICE_PLAN_ID); - smartMeterToPricePlanAccounts.put("smart-meter-4", RENEWABLES_PRICE_PLAN_ID); - return smartMeterToPricePlanAccounts; - } + @Bean + public Map smartMeterToPricePlanAccounts() { + final Map smartMeterToPricePlanAccounts = new HashMap<>(); + smartMeterToPricePlanAccounts.put("smart-meter-0", MOST_EVIL_PRICE_PLAN_ID); + smartMeterToPricePlanAccounts.put("smart-meter-1", RENEWABLES_PRICE_PLAN_ID); + smartMeterToPricePlanAccounts.put("smart-meter-2", MOST_EVIL_PRICE_PLAN_ID); + smartMeterToPricePlanAccounts.put("smart-meter-3", STANDARD_PRICE_PLAN_ID); + smartMeterToPricePlanAccounts.put("smart-meter-4", RENEWABLES_PRICE_PLAN_ID); + return smartMeterToPricePlanAccounts; + } - @Bean - @Primary - public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { - ObjectMapper objectMapper = builder.createXmlMapper(false).build(); - objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - return objectMapper; - } + @Bean + @Primary + public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { + ObjectMapper objectMapper = builder.createXmlMapper(false).build(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return objectMapper; + } } diff --git a/src/main/java/uk/tw/energy/controller/MeterReadingController.java b/src/main/java/uk/tw/energy/controller/MeterReadingController.java index 3a388e9c..0370ba02 100644 --- a/src/main/java/uk/tw/energy/controller/MeterReadingController.java +++ b/src/main/java/uk/tw/energy/controller/MeterReadingController.java @@ -1,5 +1,7 @@ package uk.tw.energy.controller; +import java.util.List; +import java.util.Optional; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -12,40 +14,40 @@ import uk.tw.energy.domain.MeterReadings; import uk.tw.energy.service.MeterReadingService; -import java.util.List; -import java.util.Optional; - @RestController @RequestMapping("/readings") public class MeterReadingController { - private final MeterReadingService meterReadingService; + private final MeterReadingService meterReadingService; - public MeterReadingController(MeterReadingService meterReadingService) { - this.meterReadingService = meterReadingService; - } - - @PostMapping("/store") - public ResponseEntity storeReadings(@RequestBody MeterReadings meterReadings) { - if (!isMeterReadingsValid(meterReadings)) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - meterReadingService.storeReadings(meterReadings.smartMeterId(), meterReadings.electricityReadings()); - return ResponseEntity.ok().build(); - } - - private boolean isMeterReadingsValid(MeterReadings meterReadings) { - String smartMeterId = meterReadings.smartMeterId(); - List electricityReadings = meterReadings.electricityReadings(); - return smartMeterId != null && !smartMeterId.isEmpty() - && electricityReadings != null && !electricityReadings.isEmpty(); - } + public MeterReadingController(MeterReadingService meterReadingService) { + this.meterReadingService = meterReadingService; + } - @GetMapping("/read/{smartMeterId}") - public ResponseEntity readReadings(@PathVariable String smartMeterId) { - Optional> readings = meterReadingService.getReadings(smartMeterId); - return readings.isPresent() - ? ResponseEntity.ok(readings.get()) - : ResponseEntity.notFound().build(); + @PostMapping("/store") + public ResponseEntity storeReadings(@RequestBody MeterReadings meterReadings) { + if (!isMeterReadingsValid(meterReadings)) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } + meterReadingService.storeReadings( + meterReadings.smartMeterId(), meterReadings.electricityReadings()); + return ResponseEntity.ok().build(); + } + + private boolean isMeterReadingsValid(MeterReadings meterReadings) { + String smartMeterId = meterReadings.smartMeterId(); + List electricityReadings = meterReadings.electricityReadings(); + return smartMeterId != null + && !smartMeterId.isEmpty() + && electricityReadings != null + && !electricityReadings.isEmpty(); + } + + @GetMapping("/read/{smartMeterId}") + public ResponseEntity readReadings(@PathVariable String smartMeterId) { + Optional> readings = meterReadingService.getReadings(smartMeterId); + return readings.isPresent() + ? ResponseEntity.ok(readings.get()) + : ResponseEntity.notFound().build(); + } } diff --git a/src/main/java/uk/tw/energy/controller/PricePlanComparatorController.java b/src/main/java/uk/tw/energy/controller/PricePlanComparatorController.java index a72837c0..8f1a7101 100644 --- a/src/main/java/uk/tw/energy/controller/PricePlanComparatorController.java +++ b/src/main/java/uk/tw/energy/controller/PricePlanComparatorController.java @@ -1,5 +1,12 @@ package uk.tw.energy.controller; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -9,64 +16,60 @@ import uk.tw.energy.service.AccountService; import uk.tw.energy.service.PricePlanService; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - @RestController @RequestMapping("/price-plans") public class PricePlanComparatorController { - public final static String PRICE_PLAN_ID_KEY = "pricePlanId"; - public final static String PRICE_PLAN_COMPARISONS_KEY = "pricePlanComparisons"; - private final PricePlanService pricePlanService; - private final AccountService accountService; - - public PricePlanComparatorController(PricePlanService pricePlanService, AccountService accountService) { - this.pricePlanService = pricePlanService; - this.accountService = accountService; - } - - @GetMapping("/compare-all/{smartMeterId}") - public ResponseEntity> calculatedCostForEachPricePlan(@PathVariable String smartMeterId) { - String pricePlanId = accountService.getPricePlanIdForSmartMeterId(smartMeterId); - Optional> consumptionsForPricePlans = - pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(smartMeterId); + public static final String PRICE_PLAN_ID_KEY = "pricePlanId"; + public static final String PRICE_PLAN_COMPARISONS_KEY = "pricePlanComparisons"; + private final PricePlanService pricePlanService; + private final AccountService accountService; - if (!consumptionsForPricePlans.isPresent()) { - return ResponseEntity.notFound().build(); - } + public PricePlanComparatorController( + PricePlanService pricePlanService, AccountService accountService) { + this.pricePlanService = pricePlanService; + this.accountService = accountService; + } - Map pricePlanComparisons = new HashMap<>(); - pricePlanComparisons.put(PRICE_PLAN_ID_KEY, pricePlanId); - pricePlanComparisons.put(PRICE_PLAN_COMPARISONS_KEY, consumptionsForPricePlans.get()); + @GetMapping("/compare-all/{smartMeterId}") + public ResponseEntity> calculatedCostForEachPricePlan( + @PathVariable String smartMeterId) { + String pricePlanId = accountService.getPricePlanIdForSmartMeterId(smartMeterId); + Optional> consumptionsForPricePlans = + pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(smartMeterId); - return consumptionsForPricePlans.isPresent() - ? ResponseEntity.ok(pricePlanComparisons) - : ResponseEntity.notFound().build(); + if (!consumptionsForPricePlans.isPresent()) { + return ResponseEntity.notFound().build(); } - @GetMapping("/recommend/{smartMeterId}") - public ResponseEntity>> recommendCheapestPricePlans(@PathVariable String smartMeterId, - @RequestParam(value = "limit", required = false) Integer limit) { - Optional> consumptionsForPricePlans = - pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(smartMeterId); + Map pricePlanComparisons = new HashMap<>(); + pricePlanComparisons.put(PRICE_PLAN_ID_KEY, pricePlanId); + pricePlanComparisons.put(PRICE_PLAN_COMPARISONS_KEY, consumptionsForPricePlans.get()); - if (!consumptionsForPricePlans.isPresent()) { - return ResponseEntity.notFound().build(); - } + return consumptionsForPricePlans.isPresent() + ? ResponseEntity.ok(pricePlanComparisons) + : ResponseEntity.notFound().build(); + } - List> recommendations = new ArrayList<>(consumptionsForPricePlans.get().entrySet()); - recommendations.sort(Comparator.comparing(Map.Entry::getValue)); + @GetMapping("/recommend/{smartMeterId}") + public ResponseEntity>> recommendCheapestPricePlans( + @PathVariable String smartMeterId, + @RequestParam(value = "limit", required = false) Integer limit) { + Optional> consumptionsForPricePlans = + pricePlanService.getConsumptionCostOfElectricityReadingsForEachPricePlan(smartMeterId); - if (limit != null && limit < recommendations.size()) { - recommendations = recommendations.subList(0, limit); - } + if (!consumptionsForPricePlans.isPresent()) { + return ResponseEntity.notFound().build(); + } - return ResponseEntity.ok(recommendations); + List> recommendations = + new ArrayList<>(consumptionsForPricePlans.get().entrySet()); + recommendations.sort(Comparator.comparing(Map.Entry::getValue)); + + if (limit != null && limit < recommendations.size()) { + recommendations = recommendations.subList(0, limit); } + + return ResponseEntity.ok(recommendations); + } } diff --git a/src/main/java/uk/tw/energy/domain/ElectricityReading.java b/src/main/java/uk/tw/energy/domain/ElectricityReading.java index 29294253..f509be6c 100644 --- a/src/main/java/uk/tw/energy/domain/ElectricityReading.java +++ b/src/main/java/uk/tw/energy/domain/ElectricityReading.java @@ -6,6 +6,4 @@ /** * @param reading kW */ -public record ElectricityReading(Instant time, BigDecimal reading) { - -} +public record ElectricityReading(Instant time, BigDecimal reading) {} diff --git a/src/main/java/uk/tw/energy/domain/MeterReadings.java b/src/main/java/uk/tw/energy/domain/MeterReadings.java index 6f0908aa..60a3868d 100644 --- a/src/main/java/uk/tw/energy/domain/MeterReadings.java +++ b/src/main/java/uk/tw/energy/domain/MeterReadings.java @@ -2,6 +2,4 @@ import java.util.List; -public record MeterReadings(String smartMeterId, List electricityReadings) { - -} +public record MeterReadings(String smartMeterId, List electricityReadings) {} diff --git a/src/main/java/uk/tw/energy/domain/PricePlan.java b/src/main/java/uk/tw/energy/domain/PricePlan.java index a2d7cabd..f7632281 100644 --- a/src/main/java/uk/tw/energy/domain/PricePlan.java +++ b/src/main/java/uk/tw/energy/domain/PricePlan.java @@ -7,47 +7,50 @@ public class PricePlan { - private final String energySupplier; - private final String planName; - private final BigDecimal unitRate; // unit price per kWh - private final List peakTimeMultipliers; - - public PricePlan(String planName, String energySupplier, BigDecimal unitRate, List peakTimeMultipliers) { - this.planName = planName; - this.energySupplier = energySupplier; - this.unitRate = unitRate; - this.peakTimeMultipliers = peakTimeMultipliers; - } - - public String getEnergySupplier() { - return energySupplier; - } - - public String getPlanName() { - return planName; - } - - public BigDecimal getUnitRate() { - return unitRate; - } - - public BigDecimal getPrice(LocalDateTime dateTime) { - return peakTimeMultipliers.stream() - .filter(multiplier -> multiplier.dayOfWeek.equals(dateTime.getDayOfWeek())) - .findFirst() - .map(multiplier -> unitRate.multiply(multiplier.multiplier)) - .orElse(unitRate); - } - - - static class PeakTimeMultiplier { - - DayOfWeek dayOfWeek; - BigDecimal multiplier; - - public PeakTimeMultiplier(DayOfWeek dayOfWeek, BigDecimal multiplier) { - this.dayOfWeek = dayOfWeek; - this.multiplier = multiplier; - } + private final String energySupplier; + private final String planName; + private final BigDecimal unitRate; // unit price per kWh + private final List peakTimeMultipliers; + + public PricePlan( + String planName, + String energySupplier, + BigDecimal unitRate, + List peakTimeMultipliers) { + this.planName = planName; + this.energySupplier = energySupplier; + this.unitRate = unitRate; + this.peakTimeMultipliers = peakTimeMultipliers; + } + + public String getEnergySupplier() { + return energySupplier; + } + + public String getPlanName() { + return planName; + } + + public BigDecimal getUnitRate() { + return unitRate; + } + + public BigDecimal getPrice(LocalDateTime dateTime) { + return peakTimeMultipliers.stream() + .filter(multiplier -> multiplier.dayOfWeek.equals(dateTime.getDayOfWeek())) + .findFirst() + .map(multiplier -> unitRate.multiply(multiplier.multiplier)) + .orElse(unitRate); + } + + static class PeakTimeMultiplier { + + DayOfWeek dayOfWeek; + BigDecimal multiplier; + + public PeakTimeMultiplier(DayOfWeek dayOfWeek, BigDecimal multiplier) { + this.dayOfWeek = dayOfWeek; + this.multiplier = multiplier; } + } } diff --git a/src/main/java/uk/tw/energy/generator/ElectricityReadingsGenerator.java b/src/main/java/uk/tw/energy/generator/ElectricityReadingsGenerator.java index 8f9b207a..c634e445 100644 --- a/src/main/java/uk/tw/energy/generator/ElectricityReadingsGenerator.java +++ b/src/main/java/uk/tw/energy/generator/ElectricityReadingsGenerator.java @@ -1,7 +1,5 @@ package uk.tw.energy.generator; -import uk.tw.energy.domain.ElectricityReading; - import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Instant; @@ -9,22 +7,25 @@ import java.util.Comparator; import java.util.List; import java.util.Random; +import uk.tw.energy.domain.ElectricityReading; public class ElectricityReadingsGenerator { - public List generate(int number) { - List readings = new ArrayList<>(); - Instant now = Instant.now(); + public List generate(int number) { + List readings = new ArrayList<>(); + Instant now = Instant.now(); - Random readingRandomiser = new Random(); - for (int i = 0; i < number; i++) { - double positiveRandomValue = Math.abs(readingRandomiser.nextGaussian()); - BigDecimal randomReading = BigDecimal.valueOf(positiveRandomValue).setScale(4, RoundingMode.CEILING); - ElectricityReading electricityReading = new ElectricityReading(now.minusSeconds(i * 10), randomReading); - readings.add(electricityReading); - } - - readings.sort(Comparator.comparing(ElectricityReading::time)); - return readings; + Random readingRandomiser = new Random(); + for (int i = 0; i < number; i++) { + double positiveRandomValue = Math.abs(readingRandomiser.nextGaussian()); + BigDecimal randomReading = + BigDecimal.valueOf(positiveRandomValue).setScale(4, RoundingMode.CEILING); + ElectricityReading electricityReading = + new ElectricityReading(now.minusSeconds(i * 10L), randomReading); + readings.add(electricityReading); } + + readings.sort(Comparator.comparing(ElectricityReading::time)); + return readings; + } } diff --git a/src/main/java/uk/tw/energy/service/AccountService.java b/src/main/java/uk/tw/energy/service/AccountService.java index 8831e265..080cea34 100644 --- a/src/main/java/uk/tw/energy/service/AccountService.java +++ b/src/main/java/uk/tw/energy/service/AccountService.java @@ -1,19 +1,18 @@ package uk.tw.energy.service; -import org.springframework.stereotype.Service; - import java.util.Map; +import org.springframework.stereotype.Service; @Service public class AccountService { - private final Map smartMeterToPricePlanAccounts; + private final Map smartMeterToPricePlanAccounts; - public AccountService(Map smartMeterToPricePlanAccounts) { - this.smartMeterToPricePlanAccounts = smartMeterToPricePlanAccounts; - } + public AccountService(Map smartMeterToPricePlanAccounts) { + this.smartMeterToPricePlanAccounts = smartMeterToPricePlanAccounts; + } - public String getPricePlanIdForSmartMeterId(String smartMeterId) { - return smartMeterToPricePlanAccounts.get(smartMeterId); - } + public String getPricePlanIdForSmartMeterId(String smartMeterId) { + return smartMeterToPricePlanAccounts.get(smartMeterId); + } } diff --git a/src/main/java/uk/tw/energy/service/MeterReadingService.java b/src/main/java/uk/tw/energy/service/MeterReadingService.java index e0df0fd8..1115e7bd 100644 --- a/src/main/java/uk/tw/energy/service/MeterReadingService.java +++ b/src/main/java/uk/tw/energy/service/MeterReadingService.java @@ -1,30 +1,29 @@ package uk.tw.energy.service; -import org.springframework.stereotype.Service; -import uk.tw.energy.domain.ElectricityReading; - import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; +import org.springframework.stereotype.Service; +import uk.tw.energy.domain.ElectricityReading; @Service public class MeterReadingService { - private final Map> meterAssociatedReadings; + private final Map> meterAssociatedReadings; - public MeterReadingService(Map> meterAssociatedReadings) { - this.meterAssociatedReadings = meterAssociatedReadings; - } + public MeterReadingService(Map> meterAssociatedReadings) { + this.meterAssociatedReadings = meterAssociatedReadings; + } - public Optional> getReadings(String smartMeterId) { - return Optional.ofNullable(meterAssociatedReadings.get(smartMeterId)); - } + public Optional> getReadings(String smartMeterId) { + return Optional.ofNullable(meterAssociatedReadings.get(smartMeterId)); + } - public void storeReadings(String smartMeterId, List electricityReadings) { - if (!meterAssociatedReadings.containsKey(smartMeterId)) { - meterAssociatedReadings.put(smartMeterId, new ArrayList<>()); - } - meterAssociatedReadings.get(smartMeterId).addAll(electricityReadings); + public void storeReadings(String smartMeterId, List electricityReadings) { + if (!meterAssociatedReadings.containsKey(smartMeterId)) { + meterAssociatedReadings.put(smartMeterId, new ArrayList<>()); } + meterAssociatedReadings.get(smartMeterId).addAll(electricityReadings); + } } diff --git a/src/main/java/uk/tw/energy/service/PricePlanService.java b/src/main/java/uk/tw/energy/service/PricePlanService.java index 3f2deec2..dc2f356c 100644 --- a/src/main/java/uk/tw/energy/service/PricePlanService.java +++ b/src/main/java/uk/tw/energy/service/PricePlanService.java @@ -1,9 +1,5 @@ package uk.tw.energy.service; -import org.springframework.stereotype.Service; -import uk.tw.energy.domain.ElectricityReading; -import uk.tw.energy.domain.PricePlan; - import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Duration; @@ -12,55 +8,63 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import uk.tw.energy.domain.ElectricityReading; +import uk.tw.energy.domain.PricePlan; @Service public class PricePlanService { - private final List pricePlans; - private final MeterReadingService meterReadingService; - - public PricePlanService(List pricePlans, MeterReadingService meterReadingService) { - this.pricePlans = pricePlans; - this.meterReadingService = meterReadingService; - } - - public Optional> getConsumptionCostOfElectricityReadingsForEachPricePlan(String smartMeterId) { - Optional> electricityReadings = meterReadingService.getReadings(smartMeterId); - - if (!electricityReadings.isPresent()) { - return Optional.empty(); - } - - return Optional.of(pricePlans.stream().collect( - Collectors.toMap(PricePlan::getPlanName, t -> calculateCost(electricityReadings.get(), t)))); - } - - private BigDecimal calculateCost(List electricityReadings, PricePlan pricePlan) { - BigDecimal average = calculateAverageReading(electricityReadings); - BigDecimal timeElapsed = calculateTimeElapsed(electricityReadings); - - BigDecimal averagedCost = average.divide(timeElapsed, RoundingMode.HALF_UP); - return averagedCost.multiply(pricePlan.getUnitRate()); - } - - private BigDecimal calculateAverageReading(List electricityReadings) { - BigDecimal summedReadings = electricityReadings.stream() - .map(ElectricityReading::reading) - .reduce(BigDecimal.ZERO, (reading, accumulator) -> reading.add(accumulator)); - - return summedReadings.divide(BigDecimal.valueOf(electricityReadings.size()), RoundingMode.HALF_UP); - } + private final List pricePlans; + private final MeterReadingService meterReadingService; - private BigDecimal calculateTimeElapsed(List electricityReadings) { - ElectricityReading first = electricityReadings.stream() - .min(Comparator.comparing(ElectricityReading::time)) - .get(); + public PricePlanService(List pricePlans, MeterReadingService meterReadingService) { + this.pricePlans = pricePlans; + this.meterReadingService = meterReadingService; + } - ElectricityReading last = electricityReadings.stream() - .max(Comparator.comparing(ElectricityReading::time)) - .get(); + public Optional> getConsumptionCostOfElectricityReadingsForEachPricePlan( + String smartMeterId) { + Optional> electricityReadings = + meterReadingService.getReadings(smartMeterId); - return BigDecimal.valueOf(Duration.between(first.time(), last.time()).getSeconds() / 3600.0); + if (!electricityReadings.isPresent()) { + return Optional.empty(); } + return Optional.of( + pricePlans.stream() + .collect( + Collectors.toMap( + PricePlan::getPlanName, t -> calculateCost(electricityReadings.get(), t)))); + } + + private BigDecimal calculateCost( + List electricityReadings, PricePlan pricePlan) { + BigDecimal average = calculateAverageReading(electricityReadings); + BigDecimal timeElapsed = calculateTimeElapsed(electricityReadings); + + BigDecimal averagedCost = average.divide(timeElapsed, RoundingMode.HALF_UP); + return averagedCost.multiply(pricePlan.getUnitRate()); + } + + private BigDecimal calculateAverageReading(List electricityReadings) { + BigDecimal summedReadings = + electricityReadings.stream() + .map(ElectricityReading::reading) + .reduce(BigDecimal.ZERO, (reading, accumulator) -> reading.add(accumulator)); + + return summedReadings.divide( + BigDecimal.valueOf(electricityReadings.size()), RoundingMode.HALF_UP); + } + + private BigDecimal calculateTimeElapsed(List electricityReadings) { + ElectricityReading first = + electricityReadings.stream().min(Comparator.comparing(ElectricityReading::time)).get(); + + ElectricityReading last = + electricityReadings.stream().max(Comparator.comparing(ElectricityReading::time)).get(); + + return BigDecimal.valueOf(Duration.between(first.time(), last.time()).getSeconds() / 3600.0); + } } diff --git a/src/test/java/uk/tw/energy/builders/MeterReadingsBuilder.java b/src/test/java/uk/tw/energy/builders/MeterReadingsBuilder.java index 07ccc2c7..6b454e20 100644 --- a/src/test/java/uk/tw/energy/builders/MeterReadingsBuilder.java +++ b/src/test/java/uk/tw/energy/builders/MeterReadingsBuilder.java @@ -1,35 +1,34 @@ package uk.tw.energy.builders; +import java.util.ArrayList; +import java.util.List; import uk.tw.energy.domain.ElectricityReading; import uk.tw.energy.domain.MeterReadings; import uk.tw.energy.generator.ElectricityReadingsGenerator; -import java.util.ArrayList; -import java.util.List; - public class MeterReadingsBuilder { - private static final String DEFAULT_METER_ID = "id"; + private static final String DEFAULT_METER_ID = "id"; - private String smartMeterId = DEFAULT_METER_ID; - private List electricityReadings = new ArrayList<>(); + private String smartMeterId = DEFAULT_METER_ID; + private List electricityReadings = new ArrayList<>(); - public MeterReadingsBuilder setSmartMeterId(String smartMeterId) { - this.smartMeterId = smartMeterId; - return this; - } + public MeterReadingsBuilder setSmartMeterId(String smartMeterId) { + this.smartMeterId = smartMeterId; + return this; + } - public MeterReadingsBuilder generateElectricityReadings() { - return generateElectricityReadings(5); - } + public MeterReadingsBuilder generateElectricityReadings() { + return generateElectricityReadings(5); + } - public MeterReadingsBuilder generateElectricityReadings(int number) { - ElectricityReadingsGenerator readingsBuilder = new ElectricityReadingsGenerator(); - this.electricityReadings = readingsBuilder.generate(number); - return this; - } + public MeterReadingsBuilder generateElectricityReadings(int number) { + ElectricityReadingsGenerator readingsBuilder = new ElectricityReadingsGenerator(); + this.electricityReadings = readingsBuilder.generate(number); + return this; + } - public MeterReadings build() { - return new MeterReadings(smartMeterId, electricityReadings); - } + public MeterReadings build() { + return new MeterReadings(smartMeterId, electricityReadings); + } } diff --git a/src/test/java/uk/tw/energy/controller/MeterReadingControllerTest.java b/src/test/java/uk/tw/energy/controller/MeterReadingControllerTest.java index 07d8c6f2..9c90fbf5 100644 --- a/src/test/java/uk/tw/energy/controller/MeterReadingControllerTest.java +++ b/src/test/java/uk/tw/energy/controller/MeterReadingControllerTest.java @@ -1,5 +1,11 @@ package uk.tw.energy.controller; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; @@ -8,81 +14,85 @@ import uk.tw.energy.domain.MeterReadings; import uk.tw.energy.service.MeterReadingService; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; - public class MeterReadingControllerTest { - private static final String SMART_METER_ID = "10101010"; - private MeterReadingController meterReadingController; - private MeterReadingService meterReadingService; - - @BeforeEach - public void setUp() { - this.meterReadingService = new MeterReadingService(new HashMap<>()); - this.meterReadingController = new MeterReadingController(meterReadingService); - } - - @Test - public void givenNoMeterIdIsSuppliedWhenStoringShouldReturnErrorResponse() { - MeterReadings meterReadings = new MeterReadings(null, Collections.emptyList()); - assertThat(meterReadingController.storeReadings(meterReadings).getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - @Test - public void givenEmptyMeterReadingShouldReturnErrorResponse() { - MeterReadings meterReadings = new MeterReadings(SMART_METER_ID, Collections.emptyList()); - assertThat(meterReadingController.storeReadings(meterReadings).getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - @Test - public void givenNullReadingsAreSuppliedWhenStoringShouldReturnErrorResponse() { - MeterReadings meterReadings = new MeterReadings(SMART_METER_ID, null); - assertThat(meterReadingController.storeReadings(meterReadings).getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); - } - - @Test - public void givenMultipleBatchesOfMeterReadingsShouldStore() { - MeterReadings meterReadings = new MeterReadingsBuilder().setSmartMeterId(SMART_METER_ID) - .generateElectricityReadings() - .build(); - - MeterReadings otherMeterReadings = new MeterReadingsBuilder().setSmartMeterId(SMART_METER_ID) - .generateElectricityReadings() - .build(); - - meterReadingController.storeReadings(meterReadings); - meterReadingController.storeReadings(otherMeterReadings); - - List expectedElectricityReadings = new ArrayList<>(); - expectedElectricityReadings.addAll(meterReadings.electricityReadings()); - expectedElectricityReadings.addAll(otherMeterReadings.electricityReadings()); - - assertThat(meterReadingService.getReadings(SMART_METER_ID).get()).isEqualTo(expectedElectricityReadings); - } - - @Test - public void givenMeterReadingsAssociatedWithTheUserShouldStoreAssociatedWithUser() { - MeterReadings meterReadings = new MeterReadingsBuilder().setSmartMeterId(SMART_METER_ID) - .generateElectricityReadings() - .build(); - - MeterReadings otherMeterReadings = new MeterReadingsBuilder().setSmartMeterId("00001") - .generateElectricityReadings() - .build(); - - meterReadingController.storeReadings(meterReadings); - meterReadingController.storeReadings(otherMeterReadings); - - assertThat(meterReadingService.getReadings(SMART_METER_ID).get()).isEqualTo(meterReadings.electricityReadings()); - } - - @Test - public void givenMeterIdThatIsNotRecognisedShouldReturnNotFound() { - assertThat(meterReadingController.readReadings(SMART_METER_ID).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - } + private static final String SMART_METER_ID = "10101010"; + private MeterReadingController meterReadingController; + private MeterReadingService meterReadingService; + + @BeforeEach + public void setUp() { + this.meterReadingService = new MeterReadingService(new HashMap<>()); + this.meterReadingController = new MeterReadingController(meterReadingService); + } + + @Test + public void givenNoMeterIdIsSuppliedWhenStoringShouldReturnErrorResponse() { + MeterReadings meterReadings = new MeterReadings(null, Collections.emptyList()); + assertThat(meterReadingController.storeReadings(meterReadings).getStatusCode()) + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + public void givenEmptyMeterReadingShouldReturnErrorResponse() { + MeterReadings meterReadings = new MeterReadings(SMART_METER_ID, Collections.emptyList()); + assertThat(meterReadingController.storeReadings(meterReadings).getStatusCode()) + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + public void givenNullReadingsAreSuppliedWhenStoringShouldReturnErrorResponse() { + MeterReadings meterReadings = new MeterReadings(SMART_METER_ID, null); + assertThat(meterReadingController.storeReadings(meterReadings).getStatusCode()) + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + public void givenMultipleBatchesOfMeterReadingsShouldStore() { + MeterReadings meterReadings = + new MeterReadingsBuilder() + .setSmartMeterId(SMART_METER_ID) + .generateElectricityReadings() + .build(); + + MeterReadings otherMeterReadings = + new MeterReadingsBuilder() + .setSmartMeterId(SMART_METER_ID) + .generateElectricityReadings() + .build(); + + meterReadingController.storeReadings(meterReadings); + meterReadingController.storeReadings(otherMeterReadings); + + List expectedElectricityReadings = new ArrayList<>(); + expectedElectricityReadings.addAll(meterReadings.electricityReadings()); + expectedElectricityReadings.addAll(otherMeterReadings.electricityReadings()); + + assertThat(meterReadingService.getReadings(SMART_METER_ID).get()) + .isEqualTo(expectedElectricityReadings); + } + + @Test + public void givenMeterReadingsAssociatedWithTheUserShouldStoreAssociatedWithUser() { + MeterReadings meterReadings = + new MeterReadingsBuilder() + .setSmartMeterId(SMART_METER_ID) + .generateElectricityReadings() + .build(); + + MeterReadings otherMeterReadings = + new MeterReadingsBuilder().setSmartMeterId("00001").generateElectricityReadings().build(); + + meterReadingController.storeReadings(meterReadings); + meterReadingController.storeReadings(otherMeterReadings); + + assertThat(meterReadingService.getReadings(SMART_METER_ID).get()) + .isEqualTo(meterReadings.electricityReadings()); + } + + @Test + public void givenMeterIdThatIsNotRecognisedShouldReturnNotFound() { + assertThat(meterReadingController.readReadings(SMART_METER_ID).getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND); + } } diff --git a/src/test/java/uk/tw/energy/controller/PricePlanComparatorControllerTest.java b/src/test/java/uk/tw/energy/controller/PricePlanComparatorControllerTest.java index 13c640d5..cdfd7ea1 100644 --- a/src/test/java/uk/tw/energy/controller/PricePlanComparatorControllerTest.java +++ b/src/test/java/uk/tw/energy/controller/PricePlanComparatorControllerTest.java @@ -1,13 +1,6 @@ package uk.tw.energy.controller; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import uk.tw.energy.domain.ElectricityReading; -import uk.tw.energy.domain.PricePlan; -import uk.tw.energy.service.AccountService; -import uk.tw.energy.service.MeterReadingService; -import uk.tw.energy.service.PricePlanService; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import java.math.BigDecimal; import java.time.Instant; @@ -17,101 +10,132 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import uk.tw.energy.domain.ElectricityReading; +import uk.tw.energy.domain.PricePlan; +import uk.tw.energy.service.AccountService; +import uk.tw.energy.service.MeterReadingService; +import uk.tw.energy.service.PricePlanService; public class PricePlanComparatorControllerTest { - private static final String PRICE_PLAN_1_ID = "test-supplier"; - private static final String PRICE_PLAN_2_ID = "best-supplier"; - private static final String PRICE_PLAN_3_ID = "second-best-supplier"; - private static final String SMART_METER_ID = "smart-meter-id"; - private PricePlanComparatorController controller; - private MeterReadingService meterReadingService; - private AccountService accountService; - - @BeforeEach - public void setUp() { - meterReadingService = new MeterReadingService(new HashMap<>()); - PricePlan pricePlan1 = new PricePlan(PRICE_PLAN_1_ID, null, BigDecimal.TEN, null); - PricePlan pricePlan2 = new PricePlan(PRICE_PLAN_2_ID, null, BigDecimal.ONE, null); - PricePlan pricePlan3 = new PricePlan(PRICE_PLAN_3_ID, null, BigDecimal.valueOf(2), null); - - List pricePlans = Arrays.asList(pricePlan1, pricePlan2, pricePlan3); - PricePlanService tariffService = new PricePlanService(pricePlans, meterReadingService); - - Map meterToTariffs = new HashMap<>(); - meterToTariffs.put(SMART_METER_ID, PRICE_PLAN_1_ID); - accountService = new AccountService(meterToTariffs); - - controller = new PricePlanComparatorController(tariffService, accountService); - } - - @Test - public void shouldCalculateCostForMeterReadingsForEveryPricePlan() { - - ElectricityReading electricityReading = new ElectricityReading(Instant.now().minusSeconds(3600), BigDecimal.valueOf(15.0)); - ElectricityReading otherReading = new ElectricityReading(Instant.now(), BigDecimal.valueOf(5.0)); - meterReadingService.storeReadings(SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); - - Map expectedPricePlanToCost = new HashMap<>(); - expectedPricePlanToCost.put(PRICE_PLAN_1_ID, BigDecimal.valueOf(100.0)); - expectedPricePlanToCost.put(PRICE_PLAN_2_ID, BigDecimal.valueOf(10.0)); - expectedPricePlanToCost.put(PRICE_PLAN_3_ID, BigDecimal.valueOf(20.0)); - - Map expected = new HashMap<>(); - expected.put(PricePlanComparatorController.PRICE_PLAN_ID_KEY, PRICE_PLAN_1_ID); - expected.put(PricePlanComparatorController.PRICE_PLAN_COMPARISONS_KEY, expectedPricePlanToCost); - assertThat(controller.calculatedCostForEachPricePlan(SMART_METER_ID).getBody()).isEqualTo(expected); - } - - @Test - public void shouldRecommendCheapestPricePlansNoLimitForMeterUsage() throws Exception { - - ElectricityReading electricityReading = new ElectricityReading(Instant.now().minusSeconds(1800), BigDecimal.valueOf(35.0)); - ElectricityReading otherReading = new ElectricityReading(Instant.now(), BigDecimal.valueOf(3.0)); - meterReadingService.storeReadings(SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); - - List> expectedPricePlanToCost = new ArrayList<>(); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_2_ID, BigDecimal.valueOf(38.0))); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_3_ID, BigDecimal.valueOf(76.0))); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_1_ID, BigDecimal.valueOf(380.0))); - - assertThat(controller.recommendCheapestPricePlans(SMART_METER_ID, null).getBody()).isEqualTo(expectedPricePlanToCost); - } - - - @Test - public void shouldRecommendLimitedCheapestPricePlansForMeterUsage() throws Exception { - - ElectricityReading electricityReading = new ElectricityReading(Instant.now().minusSeconds(2700), BigDecimal.valueOf(5.0)); - ElectricityReading otherReading = new ElectricityReading(Instant.now(), BigDecimal.valueOf(20.0)); - meterReadingService.storeReadings(SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); - - List> expectedPricePlanToCost = new ArrayList<>(); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_2_ID, BigDecimal.valueOf(16.7))); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_3_ID, BigDecimal.valueOf(33.4))); - - assertThat(controller.recommendCheapestPricePlans(SMART_METER_ID, 2).getBody()).isEqualTo(expectedPricePlanToCost); - } - - @Test - public void shouldRecommendCheapestPricePlansMoreThanLimitAvailableForMeterUsage() throws Exception { - - ElectricityReading electricityReading = new ElectricityReading(Instant.now().minusSeconds(3600), BigDecimal.valueOf(25.0)); - ElectricityReading otherReading = new ElectricityReading(Instant.now(), BigDecimal.valueOf(3.0)); - meterReadingService.storeReadings(SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); - - List> expectedPricePlanToCost = new ArrayList<>(); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_2_ID, BigDecimal.valueOf(14.0))); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_3_ID, BigDecimal.valueOf(28.0))); - expectedPricePlanToCost.add(new AbstractMap.SimpleEntry<>(PRICE_PLAN_1_ID, BigDecimal.valueOf(140.0))); - - assertThat(controller.recommendCheapestPricePlans(SMART_METER_ID, 5).getBody()).isEqualTo(expectedPricePlanToCost); - } - - @Test - public void givenNoMatchingMeterIdShouldReturnNotFound() { - assertThat(controller.calculatedCostForEachPricePlan("not-found").getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - } + private static final String PRICE_PLAN_1_ID = "test-supplier"; + private static final String PRICE_PLAN_2_ID = "best-supplier"; + private static final String PRICE_PLAN_3_ID = "second-best-supplier"; + private static final String SMART_METER_ID = "smart-meter-id"; + private PricePlanComparatorController controller; + private MeterReadingService meterReadingService; + private AccountService accountService; + + @BeforeEach + public void setUp() { + meterReadingService = new MeterReadingService(new HashMap<>()); + PricePlan pricePlan1 = new PricePlan(PRICE_PLAN_1_ID, null, BigDecimal.TEN, null); + PricePlan pricePlan2 = new PricePlan(PRICE_PLAN_2_ID, null, BigDecimal.ONE, null); + PricePlan pricePlan3 = new PricePlan(PRICE_PLAN_3_ID, null, BigDecimal.valueOf(2), null); + + List pricePlans = Arrays.asList(pricePlan1, pricePlan2, pricePlan3); + PricePlanService tariffService = new PricePlanService(pricePlans, meterReadingService); + + Map meterToTariffs = new HashMap<>(); + meterToTariffs.put(SMART_METER_ID, PRICE_PLAN_1_ID); + accountService = new AccountService(meterToTariffs); + + controller = new PricePlanComparatorController(tariffService, accountService); + } + + @Test + public void shouldCalculateCostForMeterReadingsForEveryPricePlan() { + + ElectricityReading electricityReading = + new ElectricityReading(Instant.now().minusSeconds(3600), BigDecimal.valueOf(15.0)); + ElectricityReading otherReading = + new ElectricityReading(Instant.now(), BigDecimal.valueOf(5.0)); + meterReadingService.storeReadings( + SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); + + Map expectedPricePlanToCost = new HashMap<>(); + expectedPricePlanToCost.put(PRICE_PLAN_1_ID, BigDecimal.valueOf(100.0)); + expectedPricePlanToCost.put(PRICE_PLAN_2_ID, BigDecimal.valueOf(10.0)); + expectedPricePlanToCost.put(PRICE_PLAN_3_ID, BigDecimal.valueOf(20.0)); + + Map expected = new HashMap<>(); + expected.put(PricePlanComparatorController.PRICE_PLAN_ID_KEY, PRICE_PLAN_1_ID); + expected.put(PricePlanComparatorController.PRICE_PLAN_COMPARISONS_KEY, expectedPricePlanToCost); + assertThat(controller.calculatedCostForEachPricePlan(SMART_METER_ID).getBody()) + .isEqualTo(expected); + } + + @Test + public void shouldRecommendCheapestPricePlansNoLimitForMeterUsage() throws Exception { + + ElectricityReading electricityReading = + new ElectricityReading(Instant.now().minusSeconds(1800), BigDecimal.valueOf(35.0)); + ElectricityReading otherReading = + new ElectricityReading(Instant.now(), BigDecimal.valueOf(3.0)); + meterReadingService.storeReadings( + SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); + + List> expectedPricePlanToCost = new ArrayList<>(); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_2_ID, BigDecimal.valueOf(38.0))); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_3_ID, BigDecimal.valueOf(76.0))); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_1_ID, BigDecimal.valueOf(380.0))); + + assertThat(controller.recommendCheapestPricePlans(SMART_METER_ID, null).getBody()) + .isEqualTo(expectedPricePlanToCost); + } + + @Test + public void shouldRecommendLimitedCheapestPricePlansForMeterUsage() throws Exception { + + ElectricityReading electricityReading = + new ElectricityReading(Instant.now().minusSeconds(2700), BigDecimal.valueOf(5.0)); + ElectricityReading otherReading = + new ElectricityReading(Instant.now(), BigDecimal.valueOf(20.0)); + meterReadingService.storeReadings( + SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); + + List> expectedPricePlanToCost = new ArrayList<>(); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_2_ID, BigDecimal.valueOf(16.7))); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_3_ID, BigDecimal.valueOf(33.4))); + + assertThat(controller.recommendCheapestPricePlans(SMART_METER_ID, 2).getBody()) + .isEqualTo(expectedPricePlanToCost); + } + + @Test + public void shouldRecommendCheapestPricePlansMoreThanLimitAvailableForMeterUsage() + throws Exception { + + ElectricityReading electricityReading = + new ElectricityReading(Instant.now().minusSeconds(3600), BigDecimal.valueOf(25.0)); + ElectricityReading otherReading = + new ElectricityReading(Instant.now(), BigDecimal.valueOf(3.0)); + meterReadingService.storeReadings( + SMART_METER_ID, Arrays.asList(electricityReading, otherReading)); + + List> expectedPricePlanToCost = new ArrayList<>(); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_2_ID, BigDecimal.valueOf(14.0))); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_3_ID, BigDecimal.valueOf(28.0))); + expectedPricePlanToCost.add( + new AbstractMap.SimpleEntry<>(PRICE_PLAN_1_ID, BigDecimal.valueOf(140.0))); + + assertThat(controller.recommendCheapestPricePlans(SMART_METER_ID, 5).getBody()) + .isEqualTo(expectedPricePlanToCost); + } + + @Test + public void givenNoMatchingMeterIdShouldReturnNotFound() { + assertThat(controller.calculatedCostForEachPricePlan("not-found").getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND); + } } diff --git a/src/test/java/uk/tw/energy/domain/PricePlanTest.java b/src/test/java/uk/tw/energy/domain/PricePlanTest.java index 4f983647..ceb3c316 100644 --- a/src/test/java/uk/tw/energy/domain/PricePlanTest.java +++ b/src/test/java/uk/tw/energy/domain/PricePlanTest.java @@ -1,7 +1,7 @@ package uk.tw.energy.domain; -import org.assertj.core.data.Percentage; -import org.junit.jupiter.api.Test; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import java.math.BigDecimal; import java.time.DayOfWeek; @@ -9,53 +9,59 @@ import java.time.Month; import java.util.Arrays; import java.util.List; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import org.assertj.core.data.Percentage; +import org.junit.jupiter.api.Test; public class PricePlanTest { - private final String ENERGY_SUPPLIER_NAME = "Energy Supplier Name"; + private final String ENERGY_SUPPLIER_NAME = "Energy Supplier Name"; - @Test - public void shouldReturnTheEnergySupplierGivenInTheConstructor() { - PricePlan pricePlan = new PricePlan(null, ENERGY_SUPPLIER_NAME, null, null); + @Test + public void shouldReturnTheEnergySupplierGivenInTheConstructor() { + PricePlan pricePlan = new PricePlan(null, ENERGY_SUPPLIER_NAME, null, null); - assertThat(pricePlan.getEnergySupplier()).isEqualTo(ENERGY_SUPPLIER_NAME); - } + assertThat(pricePlan.getEnergySupplier()).isEqualTo(ENERGY_SUPPLIER_NAME); + } - @Test - public void shouldReturnTheBasePriceGivenAnOrdinaryDateTime() throws Exception { - LocalDateTime normalDateTime = LocalDateTime.of(2017, Month.AUGUST, 31, 12, 0, 0); - PricePlan.PeakTimeMultiplier peakTimeMultiplier = new PricePlan.PeakTimeMultiplier(DayOfWeek.WEDNESDAY, BigDecimal.TEN); - PricePlan pricePlan = new PricePlan(null, null, BigDecimal.ONE, singletonList(peakTimeMultiplier)); + @Test + public void shouldReturnTheBasePriceGivenAnOrdinaryDateTime() throws Exception { + LocalDateTime normalDateTime = LocalDateTime.of(2017, Month.AUGUST, 31, 12, 0, 0); + PricePlan.PeakTimeMultiplier peakTimeMultiplier = + new PricePlan.PeakTimeMultiplier(DayOfWeek.WEDNESDAY, BigDecimal.TEN); + PricePlan pricePlan = + new PricePlan(null, null, BigDecimal.ONE, singletonList(peakTimeMultiplier)); - BigDecimal price = pricePlan.getPrice(normalDateTime); + BigDecimal price = pricePlan.getPrice(normalDateTime); - assertThat(price).isCloseTo(BigDecimal.ONE, Percentage.withPercentage(1)); - } + assertThat(price).isCloseTo(BigDecimal.ONE, Percentage.withPercentage(1)); + } - @Test - public void shouldReturnAnExceptionPriceGivenExceptionalDateTime() throws Exception { - LocalDateTime exceptionalDateTime = LocalDateTime.of(2017, Month.AUGUST, 30, 23, 0, 0); - PricePlan.PeakTimeMultiplier peakTimeMultiplier = new PricePlan.PeakTimeMultiplier(DayOfWeek.WEDNESDAY, BigDecimal.TEN); - PricePlan pricePlan = new PricePlan(null, null, BigDecimal.ONE, singletonList(peakTimeMultiplier)); + @Test + public void shouldReturnAnExceptionPriceGivenExceptionalDateTime() throws Exception { + LocalDateTime exceptionalDateTime = LocalDateTime.of(2017, Month.AUGUST, 30, 23, 0, 0); + PricePlan.PeakTimeMultiplier peakTimeMultiplier = + new PricePlan.PeakTimeMultiplier(DayOfWeek.WEDNESDAY, BigDecimal.TEN); + PricePlan pricePlan = + new PricePlan(null, null, BigDecimal.ONE, singletonList(peakTimeMultiplier)); - BigDecimal price = pricePlan.getPrice(exceptionalDateTime); + BigDecimal price = pricePlan.getPrice(exceptionalDateTime); - assertThat(price).isCloseTo(BigDecimal.TEN, Percentage.withPercentage(1)); - } + assertThat(price).isCloseTo(BigDecimal.TEN, Percentage.withPercentage(1)); + } - @Test - public void shouldReceiveMultipleExceptionalDateTimes() throws Exception { - LocalDateTime exceptionalDateTime = LocalDateTime.of(2017, Month.AUGUST, 30, 23, 0, 0); - PricePlan.PeakTimeMultiplier peakTimeMultiplier = new PricePlan.PeakTimeMultiplier(DayOfWeek.WEDNESDAY, BigDecimal.TEN); - PricePlan.PeakTimeMultiplier otherPeakTimeMultiplier = new PricePlan.PeakTimeMultiplier(DayOfWeek.TUESDAY, BigDecimal.TEN); - List peakTimeMultipliers = Arrays.asList(peakTimeMultiplier, otherPeakTimeMultiplier); - PricePlan pricePlan = new PricePlan(null, null, BigDecimal.ONE, peakTimeMultipliers); + @Test + public void shouldReceiveMultipleExceptionalDateTimes() throws Exception { + LocalDateTime exceptionalDateTime = LocalDateTime.of(2017, Month.AUGUST, 30, 23, 0, 0); + PricePlan.PeakTimeMultiplier peakTimeMultiplier = + new PricePlan.PeakTimeMultiplier(DayOfWeek.WEDNESDAY, BigDecimal.TEN); + PricePlan.PeakTimeMultiplier otherPeakTimeMultiplier = + new PricePlan.PeakTimeMultiplier(DayOfWeek.TUESDAY, BigDecimal.TEN); + List peakTimeMultipliers = + Arrays.asList(peakTimeMultiplier, otherPeakTimeMultiplier); + PricePlan pricePlan = new PricePlan(null, null, BigDecimal.ONE, peakTimeMultipliers); - BigDecimal price = pricePlan.getPrice(exceptionalDateTime); + BigDecimal price = pricePlan.getPrice(exceptionalDateTime); - assertThat(price).isCloseTo(BigDecimal.TEN, Percentage.withPercentage(1)); - } + assertThat(price).isCloseTo(BigDecimal.TEN, Percentage.withPercentage(1)); + } } diff --git a/src/test/java/uk/tw/energy/service/AccountServiceTest.java b/src/test/java/uk/tw/energy/service/AccountServiceTest.java index 0b27d4f2..ae13a64f 100644 --- a/src/test/java/uk/tw/energy/service/AccountServiceTest.java +++ b/src/test/java/uk/tw/energy/service/AccountServiceTest.java @@ -1,30 +1,30 @@ package uk.tw.energy.service; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import java.util.HashMap; import java.util.Map; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class AccountServiceTest { - private static final String PRICE_PLAN_ID = "price-plan-id"; - private static final String SMART_METER_ID = "smart-meter-id"; + private static final String PRICE_PLAN_ID = "price-plan-id"; + private static final String SMART_METER_ID = "smart-meter-id"; - private AccountService accountService; + private AccountService accountService; - @BeforeEach - public void setUp() { - Map smartMeterToPricePlanAccounts = new HashMap<>(); - smartMeterToPricePlanAccounts.put(SMART_METER_ID, PRICE_PLAN_ID); + @BeforeEach + public void setUp() { + Map smartMeterToPricePlanAccounts = new HashMap<>(); + smartMeterToPricePlanAccounts.put(SMART_METER_ID, PRICE_PLAN_ID); - accountService = new AccountService(smartMeterToPricePlanAccounts); - } + accountService = new AccountService(smartMeterToPricePlanAccounts); + } - @Test - public void givenTheSmartMeterIdReturnsThePricePlanId() throws Exception { - assertThat(accountService.getPricePlanIdForSmartMeterId(SMART_METER_ID)).isEqualTo(PRICE_PLAN_ID); - } + @Test + public void givenTheSmartMeterIdReturnsThePricePlanId() throws Exception { + assertThat(accountService.getPricePlanIdForSmartMeterId(SMART_METER_ID)) + .isEqualTo(PRICE_PLAN_ID); + } } diff --git a/src/test/java/uk/tw/energy/service/MeterReadingServiceTest.java b/src/test/java/uk/tw/energy/service/MeterReadingServiceTest.java index a1b656fb..11713553 100644 --- a/src/test/java/uk/tw/energy/service/MeterReadingServiceTest.java +++ b/src/test/java/uk/tw/energy/service/MeterReadingServiceTest.java @@ -1,31 +1,31 @@ package uk.tw.energy.service; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import java.util.ArrayList; import java.util.HashMap; import java.util.Optional; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class MeterReadingServiceTest { - private MeterReadingService meterReadingService; + private MeterReadingService meterReadingService; - @BeforeEach - public void setUp() { - meterReadingService = new MeterReadingService(new HashMap<>()); - } + @BeforeEach + public void setUp() { + meterReadingService = new MeterReadingService(new HashMap<>()); + } - @Test - public void givenMeterIdThatDoesNotExistShouldReturnNull() { - assertThat(meterReadingService.getReadings("unknown-id")).isEqualTo(Optional.empty()); - } + @Test + public void givenMeterIdThatDoesNotExistShouldReturnNull() { + assertThat(meterReadingService.getReadings("unknown-id")).isEqualTo(Optional.empty()); + } - @Test - public void givenMeterReadingThatExistsShouldReturnMeterReadings() { - meterReadingService.storeReadings("random-id", new ArrayList<>()); - assertThat(meterReadingService.getReadings("random-id")).isEqualTo(Optional.of(new ArrayList<>())); - } + @Test + public void givenMeterReadingThatExistsShouldReturnMeterReadings() { + meterReadingService.storeReadings("random-id", new ArrayList<>()); + assertThat(meterReadingService.getReadings("random-id")) + .isEqualTo(Optional.of(new ArrayList<>())); + } }