Skip to content

Commit

Permalink
Merge pull request #226 from Denise-Ng/master
Browse files Browse the repository at this point in the history
StartDate before or equals to ExpiryDate
  • Loading branch information
alcen authored Apr 2, 2020
2 parents 52c20a2 + 29d5ce3 commit 9abc9c8
Show file tree
Hide file tree
Showing 15 changed files with 319 additions and 74 deletions.
Binary file modified docs/images/Calendar_CouponsOnDateWindow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/Calendar_HighlightedCell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions src/main/java/csdev/couponstash/commons/util/DateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ public class DateUtil {

public static final String MONTH_YEAR_VALIDATION_REGEX = "\\d{1,2}-\\d{4}";

public static final String YEAR_VALIDATION_REGEX = "\\d{4}";

// for Calendar (and SavedCommand)
public static final DateTimeFormatter DAY_MONTH_YEAR_FORMATTER_FOR_CALENDAR = DateTimeFormatter
.ofPattern("d LLLL yyyy");
public static final DateTimeFormatter MONTH_YEAR_FORMATTER_FOR_CALENDAR = DateTimeFormatter.ofPattern("LLLL yyyy");

private static final int MINIMUM_YEAR = 2000;
private static final int MAXIMUM_YEAR = 2200;
public static final int MINIMUM_YEAR = 2000;
public static final int MAXIMUM_YEAR = 2200;


/**
Expand Down Expand Up @@ -73,7 +75,7 @@ public static boolean isValidYearMonth(String test) {
* @return True if the specified year is valid.
*/
public static boolean isValidYear(int year) {
return year >= MINIMUM_YEAR && year <= MAXIMUM_YEAR;
return year >= MINIMUM_YEAR && year <= MAXIMUM_YEAR && String.valueOf(year).matches(YEAR_VALIDATION_REGEX);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import csdev.couponstash.logic.commands.exceptions.CommandException;
import csdev.couponstash.model.Model;
import csdev.couponstash.model.coupon.Coupon;
import csdev.couponstash.model.coupon.StartDate;
import csdev.couponstash.model.coupon.savings.PercentageAmount;

/**
Expand Down Expand Up @@ -62,6 +63,10 @@ public CommandResult execute(Model model, String commandText) throws CommandExce
throw new CommandException(MESSAGE_DUPLICATE_COUPON);
}

if (!toAdd.getExpiryDate().isAfterOrEqual(toAdd.getStartDate())) {
throw new CommandException(StartDate.MESSAGE_CONSTRAINTS);
}

model.addCoupon(toAdd, commandText);

return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ public CommandResult execute(Model model, String commandText) throws CommandExce
throw new CommandException(MESSAGE_DUPLICATE_COUPON);
}

if (!couponToEdit.getExpiryDate().isAfterOrEqual(editedCoupon.getStartDate())) {
throw new CommandException(StartDate.MESSAGE_CONSTRAINTS);
}

if (!editedCoupon.getExpiryDate().isAfterOrEqual(couponToEdit.getStartDate())) {
throw new CommandException(ExpiryDate.MESSAGE_CONSTRAINTS);
}

Integer currentUsage = Integer.parseInt(couponToEdit.getUsage().value);
Integer editedLimit = Integer.parseInt(editedCoupon.getLimit().value);
if (currentUsage > editedLimit) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/csdev/couponstash/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public static Savings parseSavings(Collection<String> savings, String moneySymbo
public static ExpiryDate parseExpiryDate(String expiryDate) throws ParseException {
requireNonNull(expiryDate);
String trimmedDate = expiryDate.trim();
if (!ExpiryDate.isValidExpiryDate(trimmedDate)) {
if (!DateUtil.isValidDate(trimmedDate)) {
throw new ParseException(ExpiryDate.MESSAGE_CONSTRAINTS);
}
return new ExpiryDate(trimmedDate);
Expand Down Expand Up @@ -199,7 +199,7 @@ public static YearMonth parseYearMonth(String yearMonth) throws ParseException {
public static StartDate parseStartDate(String startDate) throws ParseException {
requireNonNull(startDate);
String trimmedDate = startDate.trim();
if (!StartDate.isValidStartDate(trimmedDate)) {
if (!DateUtil.isValidDate(trimmedDate)) {
throw new ParseException(StartDate.MESSAGE_CONSTRAINTS);
}
return new StartDate(trimmedDate);
Expand Down
28 changes: 16 additions & 12 deletions src/main/java/csdev/couponstash/model/coupon/ExpiryDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@
import static java.util.Objects.requireNonNull;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;

import csdev.couponstash.commons.util.DateUtil;

/**
* Represents a Coupon's expiry date in the CouponStash.
* Guarantees: immutable; is valid as declared in {@link #isValidExpiryDate(String)}
* Guarantees: immutable; is valid as declared in {@link DateUtil#isValidDate(String)}
*/
public class ExpiryDate {

public static final String MESSAGE_CONSTRAINTS =
"Expiry Dates should be a date in the D-M-YYYY format.";
"Expiry Dates should be a date before the start date (in the D-M-YYYY format).";
public final LocalDate date;
public final String value;

Expand All @@ -26,21 +25,26 @@ public class ExpiryDate {
*/
public ExpiryDate(String expiryDate) {
requireNonNull(expiryDate);
checkArgument(isValidExpiryDate(expiryDate), MESSAGE_CONSTRAINTS);
checkArgument(DateUtil.isValidDate(expiryDate), MESSAGE_CONSTRAINTS);
value = expiryDate;
date = DateUtil.parseStringToDate(value);
}

/**
* Returns true if a given string is a valid expiry date.
* Returns true if the {@StartDate} is after or equals to the {@ExpiryDate}.
* @param sd The StartDate
* @return True if date is after or equals to the expiry date
*/
public static boolean isValidExpiryDate(String test) {
try {
DateUtil.parseStringToDate(test);
} catch (DateTimeParseException ex) {
return false;
}
return test.matches(DateUtil.DATE_VALIDATION_REGEX);
public boolean isAfterOrEqual(StartDate sd) {
return date.isAfter(sd.getDate()) || date.isEqual(sd.getDate());
}

/**
* Returns true if the {@ExpiryDate} is after today.
* @return True if date is after today.
*/
public boolean hasExpired() {
return date.isAfter(LocalDate.now());
}

/**
Expand Down
19 changes: 3 additions & 16 deletions src/main/java/csdev/couponstash/model/coupon/StartDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@
import static java.util.Objects.requireNonNull;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;

import csdev.couponstash.commons.util.DateUtil;

/**
* Represents a Coupon's start date in the CouponStash.
* Guarantees: immutable; is valid as declared in {@link #isValidStartDate(String)}
* Guarantees: immutable; is valid as declared in {@link DateUtil#isValidDate(String)}
*/
public class StartDate {

public static final String MESSAGE_CONSTRAINTS =
"Start Dates should be a date in the D-M-YYYY format.";
"Start Dates should be before the Expiry Date (in the D-M-YYYY format).";
public final LocalDate date;
public final String value;

Expand All @@ -26,23 +25,11 @@ public class StartDate {
*/
public StartDate(String startDate) {
requireNonNull(startDate);
checkArgument(isValidStartDate(startDate), MESSAGE_CONSTRAINTS);
checkArgument(DateUtil.isValidDate(startDate), MESSAGE_CONSTRAINTS);
value = startDate;
date = DateUtil.parseStringToDate(value);
}

/**
* Returns true if a given string is a valid start date.
*/
public static boolean isValidStartDate(String test) {
try {
DateUtil.parseStringToDate(test);
} catch (DateTimeParseException ex) {
return false;
}
return (test.matches(DateUtil.DATE_VALIDATION_REGEX));
}

/**
* Returns the start date as a {@LocalDate}.
* @return Start Date as a {@LocalDate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;

import csdev.couponstash.commons.exceptions.IllegalValueException;
import csdev.couponstash.commons.util.DateUtil;
import csdev.couponstash.model.coupon.Archived;
import csdev.couponstash.model.coupon.Condition;
import csdev.couponstash.model.coupon.Coupon;
Expand Down Expand Up @@ -131,7 +132,7 @@ public Coupon toModelType() throws IllegalValueException {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
ExpiryDate.class.getSimpleName()));
}
if (!ExpiryDate.isValidExpiryDate(expiryDate)) {
if (!DateUtil.isValidDate(expiryDate)) {
throw new IllegalValueException(ExpiryDate.MESSAGE_CONSTRAINTS);
}
final ExpiryDate modelExpiryDate = new ExpiryDate(expiryDate);
Expand All @@ -140,7 +141,7 @@ public Coupon toModelType() throws IllegalValueException {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
StartDate.class.getSimpleName()));
}
if (!StartDate.isValidStartDate(startDate)) {
if (!DateUtil.isValidDate(startDate)) {
throw new IllegalValueException(StartDate.MESSAGE_CONSTRAINTS);
}
final StartDate modelStartDate = new StartDate(startDate);
Expand Down
134 changes: 134 additions & 0 deletions src/test/java/csdev/couponstash/commons/util/DateUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package csdev.couponstash.commons.util;

import static csdev.couponstash.commons.util.DateUtil.MAXIMUM_YEAR;
import static csdev.couponstash.commons.util.DateUtil.MINIMUM_YEAR;
import static csdev.couponstash.testutil.Assert.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeParseException;

import org.junit.jupiter.api.Test;

public class DateUtilTest {

@Test
public void isValidDate_validInputs_correctResult() {
// null Expiry Date
assertThrows(NullPointerException.class, () -> DateUtil.isValidDate(null));

// invalid expiry dates
assertFalse(DateUtil.isValidDate("")); // empty string
assertFalse(DateUtil.isValidDate(" ")); // spaces only
assertFalse(DateUtil.isValidDate("1-1-11")); // only 2 numbers for yyyy
assertFalse(DateUtil.isValidDate("date")); // non-numeric
assertFalse(DateUtil.isValidDate("1 - 30 - 2020")); // spaces within digits
assertFalse(DateUtil.isValidDate("30/2/2020")); // wrong date format
assertFalse(DateUtil.isValidDate("30-2-2020")); // non-existent dates

// valid expiry dates
assertTrue(DateUtil.isValidDate("01-08-2020"));
assertTrue(DateUtil.isValidDate("1-8-2020"));
}

@Test
public void isValidYearMonth_validInputs_correctResult() {
// null YearMonth
assertThrows(NullPointerException.class, () -> DateUtil.isValidYearMonth(null));

// invalid YearMonth
assertFalse(DateUtil.isValidYearMonth("")); // empty string
assertFalse(DateUtil.isValidYearMonth(" ")); // spaces only
assertFalse(DateUtil.isValidYearMonth("1-11")); // only 2 numbers for yyyy
assertFalse(DateUtil.isValidYearMonth("yearMonth")); // non-numeric
assertFalse(DateUtil.isValidYearMonth(" 30 - 2020")); // spaces within digits
assertFalse(DateUtil.isValidYearMonth("2/2020")); // wrong MonthYear format
assertFalse(DateUtil.isValidYearMonth("13-2020")); // non-existent dates

// valid YearMonth
assertTrue(DateUtil.isValidYearMonth("08-2020"));
assertTrue(DateUtil.isValidYearMonth("8-2020"));
}

@Test
public void isValidYear() {
// invalid Year
assertFalse(DateUtil.isValidYear(11)); // only 2 numbers for yyyy
assertFalse(DateUtil.isValidYear(MAXIMUM_YEAR + 1)); // More than maximum year
assertFalse(DateUtil.isValidYear(MINIMUM_YEAR - 1)); // Less than minimum year


// valid Year
assertTrue(DateUtil.isValidYear(MAXIMUM_YEAR));
assertTrue(DateUtil.isValidYear(MAXIMUM_YEAR - 1));
assertTrue(DateUtil.isValidYear(MINIMUM_YEAR));
assertTrue(DateUtil.isValidYear(MINIMUM_YEAR + 1));
}

@Test
public void getYearMonthOfDate() {
LocalDate date = LocalDate.of(2020, 8, 30);
YearMonth actual = DateUtil.getYearMonthOfDate(date);
YearMonth expected = YearMonth.of(2020, 8);
assertEquals(actual, expected);
}

@Test
public void formatDateToString() {
LocalDate date = LocalDate.of(2020, 8, 30);
String actual = DateUtil.formatDateToString(date);
String expected = date.format(DateUtil.DATE_FORMATTER);
assertEquals(actual, expected);
}

@Test
public void formatYearMonthToString() {
YearMonth ym = YearMonth.of(2020, 8);
String actual = DateUtil.formatYearMonthToString(ym);
String expected = ym.format(DateUtil.YEAR_MONTH_FORMATTER);
assertEquals(actual, expected);
}

@Test
public void formatDateStringToYearMonthString() {
LocalDate date = LocalDate.of(2020, 8, 30);
String dateString = date.format(DateUtil.DATE_FORMATTER);

String actual = DateUtil.formatDateStringToYearMonthString(dateString);

YearMonth ym = YearMonth.of(date.getYear(), date.getMonthValue());
String expected = ym.format(DateUtil.YEAR_MONTH_FORMATTER);
assertEquals(actual, expected);
}

@Test
public void parseStringToDate() {
String dateString = "30-8-2020";
LocalDate actual = DateUtil.parseStringToDate(dateString);
LocalDate expected = LocalDate.of(2020, 8, 30);
assertEquals(actual, expected);
}

@Test
public void parseStringToDate_invalidString_throwsDateTimeParseException() {
String dateString = "8-30-2020";
assertThrows(DateTimeParseException.class, () -> DateUtil.parseStringToDate(dateString));
}
@Test
public void parseStringToYearMonth () {
String ymString = "8-2020";
YearMonth actual = DateUtil.parseStringToYearMonth(ymString);
YearMonth expected = YearMonth.of(2020, 8);
assertEquals(actual, expected);
}

@Test
public void parseStringToYearMonth_invalidString_throwsDateTimeParseException() {
String ymString = "30-2020";
assertThrows(DateTimeParseException.class, () -> DateUtil.parseStringToDate(ymString));
}

}
14 changes: 14 additions & 0 deletions src/test/java/csdev/couponstash/logic/commands/AddCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import csdev.couponstash.model.ReadOnlyCouponStash;
import csdev.couponstash.model.ReadOnlyUserPrefs;
import csdev.couponstash.model.coupon.Coupon;
import csdev.couponstash.model.coupon.StartDate;
import csdev.couponstash.model.element.ObservableMonthView;
import csdev.couponstash.testutil.CouponBuilder;

Expand All @@ -46,6 +47,16 @@ public void execute_couponAcceptedByModel_addSuccessful() throws Exception {
assertEquals(Arrays.asList(validCoupon), modelStub.couponsAdded);
}

@Test
public void execute_startDateIsAfterExpiryDate_throwsCommandException() {
Coupon validCoupon = new CouponBuilder().build();
Coupon invalidCoupon = new CouponBuilder().withExpiryDate("30-8-2020").withStartDate("31-12-2020").build();
AddCommand addCommand = new AddCommand(invalidCoupon);
ModelStub modelStub = new ModelStubWithCoupon(validCoupon);

assertThrows(CommandException.class, StartDate.MESSAGE_CONSTRAINTS, () -> addCommand.execute(modelStub));
}

@Test
public void execute_duplicateCoupon_throwsCommandException() {
Coupon validCoupon = new CouponBuilder().build();
Expand All @@ -55,6 +66,9 @@ public void execute_duplicateCoupon_throwsCommandException() {
assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_COUPON, () -> addCommand.execute(modelStub));
}


//

@Test
public void equals() {
Coupon alice = new CouponBuilder().withName("Alice").build();
Expand Down
Loading

0 comments on commit 9abc9c8

Please sign in to comment.