diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 25ae155c495..7e450e058f3 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -543,17 +543,22 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
**Extensions**
-* 1a. User inputs a non-alphanumeric tag.
+* 1a. User inputs incomplete data.
- * 1a1. KeepInTouch shows a message indicating that tags should be alphanumeric.
+ * 1a1. KeepInTouch shows a message indicating incomplete data.
- Use case resumes at step 1.
+ Use case ends.
-* 1b. User inputs a contact that does not exist.
+* 1b. User inputs a non-alphanumeric tag.
+ * 1b1. KeepInTouch shows a message indicating that tags should be alphanumeric.
+
+ Use case ends.
- * 1b1. KeepInTouch shows a message indicating the contact cannot be found.
+* 1c. User inputs a contact that does not exist.
- Use case resumes at step 1.
+ * 1c1. KeepInTouch shows a message indicating that the contact cannot be found.
+
+ Use case ends.
**Use case: UC12 - Delete tags from a contact**
@@ -572,15 +577,14 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
-* 1b. User inputs a contact that does not exist.
-
- * 1b1. KeepInTouch shows a message indicating that the contact cannot be found.
+* 1b. User inputs a non-alphanumeric tag.
+ * 1b1. KeepInTouch shows a message indicating that tags should be alphanumeric.
Use case ends.
-* 1c. User inputs a tag that does not exist.
+* 1c. User inputs a contact that does not exist.
- * 1c1. KeepInTouch shows a message indicating that the tags cannot be found.
+ * 1c1. KeepInTouch shows a message indicating that the contact cannot be found.
Use case ends.
@@ -600,7 +604,6 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
* 1a1. KeepInTouch shows a message indicating incomplete data.
Use case ends.
-
* 1b. User inputs a contact that does not exist.
* 1b1. KeepInTouch shows a message indicating that the contact cannot be found.
@@ -609,7 +612,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
* 1c. User inputs a tag that does not exist.
- * 1c1. KeepInTouch shows a message indicating that the tags cannot be found.
+ * 1c1. KeepInTouch shows a message indicating that the tag cannot be found.
Use case ends.
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index aea3eec985d..5882f5e565c 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -17,6 +17,8 @@ If you can type fast, KeepInTouch can get your contact management tasks done fas
* [Adding a person: `add contact`](#adding-a-person--add-contact)
* [Listing all persons: `list contact`](#listing-all-persons--list-contact)
* [Deleting a person: `delete contact`](#deleting-a-person--delete-contact)
+ * [Adding tags: `add tag`](#adding-tags--add-tag)
+ * [Deleting tags: `delete tag`](#deleting-tags--delete-tag)
* [Adding a note: `add note`](#adding-notes-to-a-contact--add-note)
* [Listing all notes: `list notes`](#listing-all-notes--list-notes)
* [Deleting a note: `delete note`](#deleting-a-note--delete-note)
@@ -71,6 +73,9 @@ If you can type fast, KeepInTouch can get your contact management tasks done fas
* `CONTACT_ID` is the number that is on the left of the person's name in each person card.
+* Items with `…` after them can be used multiple times.
+ e.g. `[-t TAGNAME]…` can be used as `-t frontend`, `-t frontend -t java` etc.
+
* Parameters can be in any order.
e.g. if the command specifies `-n NAME -t NOTE_TITLE`, `-t NOTE_TITLE -n NAME` is also acceptable.
@@ -117,6 +122,39 @@ Format: `delete contact NAME`
Examples:
* `list contact` followed by `delete contact Aaron` deletes the contact with the name Aaron.
+### Adding tags : `add tag`
+
+Adds one or more tags to a contact.
+
+Format: `add tag -id CONTACT_ID -t TAGNAME...`
+
+* Adds one or more tags to a contact.
+* Duplicates are accepted but only unique tags will be added.
+
+Requirements:
+* `TAGNAME` must be alphanumeric, with no spaces.
+
+Examples:
+* `add tag -id 1 -t frontend` adds a tag with tag name "frontend" to the first contact in the contact list.
+* `add tag -id 1 -t frontend -t java` adds two tags with tag name "frontend" and "java" to the first contact in the contact list.
+
+
+### Deleting tags : `delete tag`
+
+Deletes one or more tags to a contact.
+
+Format: `delete tag -id CONTACT_ID -t TAGNAME...`
+
+* Deletes one or more tags to a contact, regardless if the tag exists in the contact or not.
+* Duplicates are accepted but only unique tags will be added.
+
+Requirements:
+* `TAGNAME` must be alphanumeric, with no spaces.
+
+Examples:
+* `delete tag -id 1 -t frontend` deletes a tag with tag name "frontend" from the first contact in the contact list.
+* `add tag -id 1 -t frontend -t java` deletes two tags with tag name "frontend" and "java" from the first contact in the contact list.
+
### Adding notes to a contact: `add note`
Adds a note to a contact from the contact list.
@@ -232,6 +270,8 @@ Action | Format, Examples
**Add Contact** | `add contact -n NAME -p PHONE_NUMBER -a ADDRESS -e EMAIL`
e.g., `add contact -n Aaron -p 12345678 -a Baker Street 12 -e aaron123@gmail.com`
**Delete Contact** | `delete contact NAME`
e.g., `delete contact Aaron`
**List Contact** | `list contact`
+**Add Tag** | `add tag -id CONTACT_ID -t TAGNAME`
eg., `add tag -id 1 -t frontend`
+**Delete Tag** | `delete tag -id CONTACT_ID -t TAGNAME`
eg., `delete tag -id 1 -t frontend`
**Add Note** | `add note -n NAME -t NOTE_TITLE -c NOTE_CONTENT`
e.g., `add note -n Daniel -t Open Position -e Applications for SWE full-time positions will open soon`
**Delete Note** | `delete note -n NAME -t NOTE_TITLE`
e.g., `delete note -n Aaron -t Meeting Topics`
**List Notes** | `list notes`
diff --git a/src/main/java/seedu/address/logic/commands/AddPersonCommand.java b/src/main/java/seedu/address/logic/commands/AddPersonCommand.java
index f760051044e..abc99454606 100644
--- a/src/main/java/seedu/address/logic/commands/AddPersonCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddPersonCommand.java
@@ -5,6 +5,7 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
@@ -26,12 +27,14 @@ public class AddPersonCommand extends AddCommand {
+ PREFIX_NAME + " NAME "
+ PREFIX_PHONE + " PHONE "
+ PREFIX_EMAIL + " EMAIL "
- + PREFIX_ADDRESS + " ADDRESS" + "\n"
+ + PREFIX_ADDRESS + " ADDRESS "
+ + PREFIX_TAG + " TAGNAME" + "\n"
+ "Example: " + COMMAND_WORD + " " + SECONDARY_COMMAND_WORD
+ PREFIX_NAME + " John Doe "
+ PREFIX_PHONE + " 98765432 "
+ PREFIX_EMAIL + " johnd@example.com "
- + PREFIX_ADDRESS + " 311, Clementi Ave 2, #02-25 ";
+ + PREFIX_ADDRESS + " 311, Clementi Ave 2, #02-25 "
+ + PREFIX_TAG + " frontend ";
public static final String MESSAGE_SUCCESS = "New contact added: %1$s";
public static final String MESSAGE_DUPLICATE_PERSON = "This contact already exists in the contact list";
diff --git a/src/main/java/seedu/address/logic/commands/AddTagCommand.java b/src/main/java/seedu/address/logic/commands/AddTagCommand.java
new file mode 100644
index 00000000000..1d8e52be160
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddTagCommand.java
@@ -0,0 +1,79 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Set;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.ContactID;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+
+/**
+ * The command handler for {@code add tag} command
+ */
+public class AddTagCommand extends AddCommand {
+ public static final String SECONDARY_COMMAND_WORD = "tag";
+ public static final String MESSAGE_SUCCESS = "New tags added: ";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + " " + SECONDARY_COMMAND_WORD
+ + ": Adds tags to a contact from the contact list.\n"
+ + "Usage: add tag -id CONTACT_ID -t TAGNAME";
+ public static final String MESSAGE_PERSON_NOT_FOUND = "Can not find the target contact with ID: ";
+
+ private final Set toAdd;
+ private final int contactId;
+
+ /**
+ * Creates an AddTagCommand to add the specified {@code Tag}(s).
+ */
+ public AddTagCommand(int contactId, Set tagList) {
+ requireNonNull(tagList);
+ this.contactId = contactId;
+ this.toAdd = tagList;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ Person person = model.findPersonByUserFriendlyId(ContactID.fromInt(this.contactId));
+ if (person == null) {
+ throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId);
+ }
+ person.addTags(this.toAdd);
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append(MESSAGE_SUCCESS);
+ toAdd.forEach(builder::append);
+
+ return new CommandResult(builder.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddTagCommand)) {
+ return false;
+ }
+
+ AddTagCommand otherAddNoteCommand = (AddTagCommand) other;
+
+ boolean equalToAdd = toAdd.equals(otherAddNoteCommand.toAdd);
+ boolean equalContactId = (contactId == otherAddNoteCommand.contactId);
+ return equalToAdd && equalContactId;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .add("contactId", contactId)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteTagCommand.java b/src/main/java/seedu/address/logic/commands/DeleteTagCommand.java
new file mode 100644
index 00000000000..eb9f94997a9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteTagCommand.java
@@ -0,0 +1,78 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Set;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.ContactID;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.Tag;
+
+/**
+ * The command handler for {@code delete tag} command
+ */
+public class DeleteTagCommand extends DeleteCommand {
+ public static final String SECONDARY_COMMAND_WORD = "tag";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + " "
+ + SECONDARY_COMMAND_WORD + ": Delete one or more tags from a contact.\n"
+ + "Usage: delete tag -id CONTACT_ID -t TAGNAME";
+ public static final String MESSAGE_PERSON_NOT_FOUND = "Can not find the target contact with ID: ";
+ public static final String MESSAGE_SUCCESS = "Successfully deleted tags: ";
+
+ private final Set toDelete;
+ private final int contactId;
+
+ /**
+ * Creates an DeleteTagCommand to delete the specified {@code Tag}(s).
+ */
+ public DeleteTagCommand(int contactId, Set tagList) {
+ requireNonNull(tagList);
+ this.contactId = contactId;
+ this.toDelete = tagList;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ Person person = model.findPersonByUserFriendlyId(ContactID.fromInt(this.contactId));
+ if (person == null) {
+ throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId);
+ }
+ person.removeTags(toDelete);
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append(MESSAGE_SUCCESS);
+ toDelete.forEach(builder::append);
+
+ return new CommandResult(builder.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteTagCommand)) {
+ return false;
+ }
+
+ DeleteTagCommand otherAddNoteCommand = (DeleteTagCommand) other;
+
+ boolean equalToDelete = toDelete.equals(otherAddNoteCommand.toDelete);
+ boolean equalContactId = (contactId == otherAddNoteCommand.contactId);
+ return equalToDelete && equalContactId;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toDelete", toDelete)
+ .add("contactId", contactId)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index ecae8d1be41..aeab3fcb1cd 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -6,6 +6,7 @@
import seedu.address.logic.commands.AddEventCommand;
import seedu.address.logic.commands.AddNoteCommand;
import seedu.address.logic.commands.AddPersonCommand;
+import seedu.address.logic.commands.AddTagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -24,6 +25,8 @@ public AddCommand parse(String userInput) throws ParseException {
return new AddEventCommandParser().parse(args);
case AddNoteCommand.SECONDARY_COMMAND_WORD:
return new AddNoteCommandParser().parse(args);
+ case AddTagCommand.SECONDARY_COMMAND_WORD:
+ return new AddTagCommandParser().parse(args);
default:
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
diff --git a/src/main/java/seedu/address/logic/parser/AddTagCommandParser.java b/src/main/java/seedu/address/logic/parser/AddTagCommandParser.java
new file mode 100644
index 00000000000..eff1a20282b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddTagCommandParser.java
@@ -0,0 +1,41 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+
+import java.util.Set;
+
+import seedu.address.logic.commands.AddTagCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new AddTagCommand object
+ */
+public class AddTagCommandParser implements Parser {
+ @Override
+ public AddTagCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PERSON_ID,
+ PREFIX_TAG);
+
+ if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PERSON_ID, PREFIX_TAG)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID);
+
+ Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+
+ int contactId = -1;
+ try {
+ contactId = Integer.parseInt(argMultimap.getValue(PREFIX_PERSON_ID).get());
+ } catch (NumberFormatException e) {
+ throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage()));
+ }
+
+ return new AddTagCommand(contactId, tagList);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
index 8d5c3c6d943..4ac76b6d0b6 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -6,6 +6,7 @@
import seedu.address.logic.commands.DeleteEventCommand;
import seedu.address.logic.commands.DeleteNoteCommand;
import seedu.address.logic.commands.DeletePersonCommand;
+import seedu.address.logic.commands.DeleteTagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -23,6 +24,8 @@ public DeleteCommand parse(String userInput) throws ParseException {
return new DeleteNoteCommandParser().parse(args);
case DeleteEventCommand.SECONDARY_COMMAND_WORD:
return new DeleteEventCommandParser().parse(args);
+ case DeleteTagCommand.SECONDARY_COMMAND_WORD:
+ return new DeleteTagCommandParser().parse(args);
default:
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteTagCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteTagCommandParser.java
new file mode 100644
index 00000000000..b556bae7089
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteTagCommandParser.java
@@ -0,0 +1,42 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+
+import java.util.Set;
+
+import seedu.address.logic.commands.DeleteTagCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new DeleteTagCommand object
+ */
+public class DeleteTagCommandParser implements Parser {
+
+ @Override
+ public DeleteTagCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PERSON_ID,
+ PREFIX_TAG);
+
+ if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PERSON_ID, PREFIX_TAG)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID);
+
+ Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+
+ int contactId = -1;
+ try {
+ contactId = Integer.parseInt(argMultimap.getValue(PREFIX_PERSON_ID).get());
+ } catch (NumberFormatException e) {
+ throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage()));
+ }
+
+ return new DeleteTagCommand(contactId, tagList);
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index e1e64d890ef..4fe54bb9d9d 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -96,6 +96,7 @@ public ObservableList getEvents() {
/**
* Adds a note to this person
+ *
* @param note The note to be added.
*/
public void addNote(Note note) {
@@ -103,10 +104,29 @@ public void addNote(Note note) {
}
/**
- * Remove a note by its user-friendly id
+ * Adds a set of {@code Tag} to this person
+ *
+ * @param tags The tags to be added.
+ */
+ public void addTags(Set tags) {
+ this.tags.addAll(tags);
+ }
+
+ /**
+ * Removes a set of {@code Tag} from this person
+ *
+ * @param tags The tags to be removed.
+ */
+ public void removeTags(Set tags) {
+ tags.forEach(tag -> this.tags.remove(tag));
+ }
+
+ /**
+ * Removes a note by its user-friendly id
+ *
* @param id The id of the note you want to remove
* @return The note object that is just deleted if the operation is successful
- * or {@code null} if the note with this name does not exist
+ * or {@code null} if the note with this name does not exist
*/
public Note removeNoteByUserFriendlyId(NoteID id) {
return this.removeNoteByIndex(id.getId() - 1);
@@ -121,6 +141,7 @@ private Note removeNoteByIndex(int index) {
/**
* Adds an event to this person.
+ *
* @param event The event to be added.
*/
public void addEvent(Event event) {
@@ -128,10 +149,11 @@ public void addEvent(Event event) {
}
/**
- * Remove an event by its user-friendly id
+ * Removes an event by its user-friendly id
+ *
* @param id The id of the event you want to remove
* @return The event object that is just deleted if the operation is successful
- * or {@code null} if the event with this name does not exist
+ * or {@code null} if the event with this name does not exist
*/
public Event removeEventByUserFriendlyId(EventID id) {
return this.removeEventByIndex(id.getId() - 1);
diff --git a/src/test/java/seedu/address/logic/commands/AddTagCommandTest.java b/src/test/java/seedu/address/logic/commands/AddTagCommandTest.java
new file mode 100644
index 00000000000..e4d2a874e85
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddTagCommandTest.java
@@ -0,0 +1,95 @@
+package seedu.address.logic.commands;
+
+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 static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.ThrowingSupplier;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.tag.Tag;
+import seedu.address.testutil.TagBuilder;
+
+public class AddTagCommandTest {
+
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_correctCommand_success() throws CommandException {
+ int personId = 1;
+ final Set tagSet = new TagBuilder().inSet();
+
+ assertCommandSuccessWithFeedback(() -> new AddTagCommand(personId, tagSet)
+ .execute(model), AddTagCommand.MESSAGE_SUCCESS + new TagBuilder().build().toString());
+ }
+
+ @Test
+ public void execute_personNotExist_fails() throws CommandException {
+ int personId = 99999;
+ final Set tagSet = new TagBuilder().inSet();
+
+ assertCommandFailWithFeedback(() -> new AddTagCommand(personId, tagSet)
+ .execute(model), AddTagCommand.MESSAGE_PERSON_NOT_FOUND + personId);
+ }
+
+ private void assertCommandSuccessWithFeedback(ThrowingSupplier function, String result) {
+ try {
+ assertEquals(function.get(), new CommandResult(result));
+ } catch (Throwable e) {
+ throw new AssertionError("Execution of command should not fail.", e);
+ }
+ }
+
+ private void assertCommandFailWithFeedback(ThrowingSupplier function, String errResult) {
+ try {
+ function.get();
+ } catch (Throwable e) {
+ if (!(e instanceof CommandException)) {
+ throw new AssertionError("Execution of command failed but not due to CommandException.");
+ }
+ assertEquals(e.getMessage(), errResult);
+ return;
+ }
+ throw new AssertionError("Execution of command should fail.");
+ }
+
+ @Test
+ public void equals() {
+ final Set tagSetA = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+ final Set tagSetB = new TagBuilder().withTag(VALID_TAG_FRIEND).inSet();
+
+ AddTagCommand commandA = new AddTagCommand(1, tagSetA);
+ AddTagCommand commandB = new AddTagCommand(1, tagSetB);
+
+ // same object -> returns true
+ assertTrue(commandA.equals(commandA));
+
+ // same values -> returns true
+ AddTagCommand commandACopy = new AddTagCommand(1, tagSetA);
+ assertTrue(commandA.equals(commandACopy));
+
+ // different types -> returns false
+ assertFalse(commandA.equals(1));
+
+ // null -> returns false
+ assertFalse(commandA.equals(null));
+
+ // different person -> returns false
+ assertFalse(commandA.equals(commandB));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 5c7f59cefd7..c6f4cfd436f 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -40,6 +40,7 @@ public class CommandTestUtil {
public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3";
public static final String VALID_TAG_HUSBAND = "husband";
public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_PERSON_ID = "1";
public static final String VALID_NOTE_A_PERSON_ID = "1";
public static final String VALID_NOTE_A_TITLE = "Preferred Qualifications";
public static final String VALID_NOTE_A_CONTENT = "Machine Learning Frameworks";
@@ -59,6 +60,7 @@ public class CommandTestUtil {
public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
+ public static final String PERSON_ID_DESC = " " + PREFIX_PERSON_ID + VALID_PERSON_ID;
public static final String NOTE_A_PERSON_ID_DESC = " " + PREFIX_PERSON_ID + VALID_NOTE_A_PERSON_ID;
public static final String NOTE_A_TITLE_DESC = " " + PREFIX_NOTE_TITLE + VALID_NOTE_A_TITLE;
public static final String NOTE_A_CONTENT_DESC = " " + PREFIX_NOTE_CONTENT + VALID_NOTE_A_CONTENT;
@@ -91,7 +93,8 @@ public class CommandTestUtil {
/**
* Executes the given {@code command}, confirms that
- * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ *
* - the {@code actualModel} matches {@code expectedModel}
*/
public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult,
@@ -106,7 +109,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Comm
}
/**
- * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
+ * Convenience wrapper to
+ * {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
* that takes a string {@code expectedMessage}.
*/
public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage,
@@ -119,7 +123,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri
* Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged
+ * - the address book, filtered person list and selected person in
+ * {@code actualModel} remain unchanged
*/
public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) {
// we are unable to defensively copy the model for comparison later, so we can
@@ -131,8 +136,10 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri
assertEquals(expectedAddressBook, actualModel.getAddressBook());
assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
}
+
/**
- * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
+ * Updates {@code model}'s filtered list to show only the person at the given
+ * {@code targetIndex} in the
* {@code model}'s address book.
*/
public static void showPersonAtIndex(Model model, Index targetIndex) {
diff --git a/src/test/java/seedu/address/logic/commands/DeleteTagCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteTagCommandTest.java
new file mode 100644
index 00000000000..26881168ace
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteTagCommandTest.java
@@ -0,0 +1,94 @@
+package seedu.address.logic.commands;
+
+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 static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.ThrowingSupplier;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.tag.Tag;
+import seedu.address.testutil.TagBuilder;
+
+public class DeleteTagCommandTest {
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_correctCommand_success() throws CommandException {
+ int personId = 1;
+ final Set tagSet = new TagBuilder().inSet();
+
+ assertCommandSuccessWithFeedback(() -> new DeleteTagCommand(personId, tagSet)
+ .execute(model), DeleteTagCommand.MESSAGE_SUCCESS + new TagBuilder().build().toString());
+ }
+
+ @Test
+ public void execute_personNotExist_fails() throws CommandException {
+ int personId = 99999;
+ final Set tagSet = new TagBuilder().inSet();
+
+ assertCommandFailWithFeedback(() -> new DeleteTagCommand(personId, tagSet)
+ .execute(model), DeleteTagCommand.MESSAGE_PERSON_NOT_FOUND + personId);
+ }
+
+ private void assertCommandSuccessWithFeedback(ThrowingSupplier function, String result) {
+ try {
+ assertEquals(function.get(), new CommandResult(result));
+ } catch (Throwable e) {
+ throw new AssertionError("Execution of command should not fail.", e);
+ }
+ }
+
+ private void assertCommandFailWithFeedback(ThrowingSupplier function, String errResult) {
+ try {
+ function.get();
+ } catch (Throwable e) {
+ if (!(e instanceof CommandException)) {
+ throw new AssertionError("Execution of command failed but not due to CommandException.");
+ }
+ assertEquals(e.getMessage(), errResult);
+ return;
+ }
+ throw new AssertionError("Execution of command should fail.");
+ }
+
+ @Test
+ public void equals() {
+ final Set tagSetA = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+ final Set tagSetB = new TagBuilder().withTag(VALID_TAG_FRIEND).inSet();
+
+ DeleteTagCommand commandA = new DeleteTagCommand(1, tagSetA);
+ DeleteTagCommand commandB = new DeleteTagCommand(1, tagSetB);
+
+ // same object -> returns true
+ assertTrue(commandA.equals(commandA));
+
+ // same values -> returns true
+ DeleteTagCommand commandACopy = new DeleteTagCommand(1, tagSetA);
+ assertTrue(commandA.equals(commandACopy));
+
+ // different types -> returns false
+ assertFalse(commandA.equals(1));
+
+ // null -> returns false
+ assertFalse(commandA.equals(null));
+
+ // different person -> returns false
+ assertFalse(commandA.equals(commandB));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java
index 54289b27d4e..94302f6bad9 100644
--- a/src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddEventCommandParserTest.java
@@ -4,11 +4,14 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.Messages.MESSAGE_START_TIME_AFTER_END_TIME;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.address.logic.commands.CommandTestUtil.PERSON_ID_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.ThrowingSupplier;
import seedu.address.logic.commands.AddEventCommand;
+import seedu.address.logic.commands.AddTagCommand;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
@@ -21,11 +24,12 @@ public class AddEventCommandParserTest {
private AddCommandParser parser = new AddCommandParser();
-
@Test
public void execute_correctCommand_success() throws CommandException {
assertParseSuccessWithCommand(() -> parser.parse(" "
+ AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en aa -st 00:01"), AddEventCommand.class.getName());
+ assertParseSuccessWithCommand(() -> parser.parse(" " + AddTagCommand.SECONDARY_COMMAND_WORD + " "
+ + PERSON_ID_DESC + TAG_DESC_HUSBAND), AddTagCommand.class.getName());
}
@Test
@@ -39,24 +43,27 @@ public void execute_commandFormatError_fails() throws CommandException {
assertParseFailedWithError(() -> parser.parse(" "
+ AddEventCommand.SECONDARY_COMMAND_WORD + " -...."),
String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEventCommand.MESSAGE_USAGE));
+ assertParseFailedWithError(() -> parser.parse(" "
+ + AddTagCommand.SECONDARY_COMMAND_WORD + " -**"),
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE));
}
@Test
public void execute_emptyStringArguments_fails() {
assertParseFailedWithError(() -> parser.parse(" "
- + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en -st 12:00"),
+ + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en -st 12:00"),
EventName.MESSAGE_CONSTRAINTS);
assertParseFailedWithError(() -> parser.parse(" "
- + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st"),
+ + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st"),
EventTime.MESSAGE_NON_EMPTY);
assertParseFailedWithError(() -> parser.parse(" "
- + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -et"),
+ + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -et"),
EventTime.MESSAGE_NON_EMPTY);
assertParseFailedWithError(() -> parser.parse(" "
- + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -loc"),
+ + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -loc"),
EventLocation.MESSAGE_CONSTRAINTS);
assertParseFailedWithError(() -> parser.parse(" "
- + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -info"),
+ + AddEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -en Sample -st 12:00 -info"),
EventInformation.MESSAGE_CONSTRAINTS);
}
diff --git a/src/test/java/seedu/address/logic/parser/AddTagParserTest.java b/src/test/java/seedu/address/logic/parser/AddTagParserTest.java
new file mode 100644
index 00000000000..0de568d44d8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddTagParserTest.java
@@ -0,0 +1,71 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.PERSON_ID_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PERSON_ID;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddTagCommand;
+import seedu.address.model.tag.Tag;
+import seedu.address.testutil.TagBuilder;
+
+public class AddTagParserTest {
+ private AddTagCommandParser parser = new AddTagCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Set expectedTagSet = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + TAG_DESC_HUSBAND,
+ new AddTagCommand(Integer.parseInt(VALID_PERSON_ID), expectedTagSet));
+ }
+
+ @Test
+ public void parse_multipleTags_success() {
+ Set expectedTagSet = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+ expectedTagSet.add(new TagBuilder().withTag(VALID_TAG_FRIEND).build());
+
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ new AddTagCommand(Integer.parseInt(VALID_PERSON_ID), expectedTagSet));
+ }
+
+ @Test
+ public void parse_duplicateTags_success() {
+ Set expectedTagSet = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + TAG_DESC_HUSBAND + TAG_DESC_HUSBAND,
+ new AddTagCommand(Integer.parseInt(VALID_PERSON_ID), expectedTagSet));
+ }
+
+ @Test
+ public void parse_invlaidTag_success() {
+ String expectedMessage = Tag.MESSAGE_CONSTRAINTS;
+
+ assertParseFailure(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + INVALID_TAG_DESC,
+ expectedMessage);
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE);
+
+ // missing person id prefix
+ assertParseFailure(parser, PREAMBLE_WHITESPACE + TAG_DESC_HUSBAND,
+ expectedMessage);
+
+ // missing tag prefix
+ assertParseFailure(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC,
+ expectedMessage);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java
index d7cdeb107c9..db33a047344 100644
--- a/src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/DeleteEventCommandParserTest.java
@@ -3,12 +3,15 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.address.logic.commands.CommandTestUtil.PERSON_ID_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.ThrowingSupplier;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.DeleteEventCommand;
+import seedu.address.logic.commands.DeleteTagCommand;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
@@ -16,11 +19,12 @@ public class DeleteEventCommandParserTest {
private DeleteCommandParser parser = new DeleteCommandParser();
-
@Test
public void execute_correctCommand_success() throws CommandException {
assertParseSuccessWithCommand(() -> parser.parse(" "
+ DeleteEventCommand.SECONDARY_COMMAND_WORD + " -id 1 -eid 1"), DeleteEventCommand.class.getName());
+ assertParseSuccessWithCommand(() -> parser.parse(" " + DeleteTagCommand.SECONDARY_COMMAND_WORD + " "
+ + PERSON_ID_DESC + TAG_DESC_HUSBAND), DeleteTagCommand.class.getName());
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/DeleteTagParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteTagParserTest.java
new file mode 100644
index 00000000000..d2f1d5f475b
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteTagParserTest.java
@@ -0,0 +1,71 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.PERSON_ID_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PERSON_ID;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteTagCommand;
+import seedu.address.model.tag.Tag;
+import seedu.address.testutil.TagBuilder;
+
+public class DeleteTagParserTest {
+ private DeleteTagCommandParser parser = new DeleteTagCommandParser();
+
+ @Test
+ public void parse_allFieldsPresent_success() {
+ Set expectedTagSet = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + TAG_DESC_HUSBAND,
+ new DeleteTagCommand(Integer.parseInt(VALID_PERSON_ID), expectedTagSet));
+ }
+
+ @Test
+ public void parse_multipleTags_success() {
+ Set expectedTagSet = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+ expectedTagSet.add(new TagBuilder().withTag(VALID_TAG_FRIEND).build());
+
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ new DeleteTagCommand(Integer.parseInt(VALID_PERSON_ID), expectedTagSet));
+ }
+
+ @Test
+ public void parse_duplicateTags_success() {
+ Set expectedTagSet = new TagBuilder().withTag(VALID_TAG_HUSBAND).inSet();
+
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + TAG_DESC_HUSBAND + TAG_DESC_HUSBAND,
+ new DeleteTagCommand(Integer.parseInt(VALID_PERSON_ID), expectedTagSet));
+ }
+
+ @Test
+ public void parse_invlaidTag_success() {
+ String expectedMessage = Tag.MESSAGE_CONSTRAINTS;
+
+ assertParseFailure(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC + INVALID_TAG_DESC,
+ expectedMessage);
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_USAGE);
+
+ // missing person id prefix
+ assertParseFailure(parser, PREAMBLE_WHITESPACE + TAG_DESC_HUSBAND,
+ expectedMessage);
+
+ // missing tag prefix
+ assertParseFailure(parser, PREAMBLE_WHITESPACE + PERSON_ID_DESC,
+ expectedMessage);
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
index 31a10d156c9..5a45cec8ccb 100644
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ b/src/test/java/seedu/address/model/person/PersonTest.java
@@ -15,6 +15,7 @@
import org.junit.jupiter.api.Test;
import seedu.address.testutil.PersonBuilder;
+import seedu.address.testutil.TagBuilder;
public class PersonTest {
@@ -90,6 +91,19 @@ public void equals() {
assertFalse(ALICE.equals(editedAlice));
}
+ @Test
+ public void addTag_validSet_success() {
+ ALICE.addTags(new TagBuilder().inSet());
+ assertTrue(ALICE.getTags().contains(new TagBuilder().build()));
+ }
+
+ @Test
+ public void deleteTag_validSet_success() {
+ ALICE.addTags(new TagBuilder().inSet());
+ ALICE.removeTags(new TagBuilder().inSet());
+ assertFalse(ALICE.getTags().contains(new TagBuilder().build()));
+ }
+
@Test
public void toStringMethod() {
String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
diff --git a/src/test/java/seedu/address/testutil/TagBuilder.java b/src/test/java/seedu/address/testutil/TagBuilder.java
new file mode 100644
index 00000000000..bc61774313e
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TagBuilder.java
@@ -0,0 +1,53 @@
+package seedu.address.testutil;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.address.model.tag.Tag;
+
+/**
+ * A utility class to help with building Tag objects.
+ */
+public class TagBuilder {
+ public static final String DEFAULT_TAG = "Frontend";
+
+ private String tag;
+
+ /**
+ * Creates a {@code TagBuilder} with the default details.
+ */
+ public TagBuilder() {
+ this.tag = DEFAULT_TAG;
+ }
+
+ /**
+ * Initializes the TagBuilder with the data of {@code tagToCopy}.
+ */
+ public TagBuilder(Tag tagToCopy) {
+ this.tag = tagToCopy.tagName;
+ }
+
+ /**
+ * Sets the tag that we are building.
+ */
+ public TagBuilder withTag(String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ /**
+ * Returns the built {@code Tag} in a {@code Set}.
+ */
+ public Set inSet() {
+ final Set tagSet = new HashSet<>();
+ tagSet.add(this.build());
+ return tagSet;
+ }
+
+ /**
+ * Returns the built {@code Tag}.
+ */
+ public Tag build() {
+ return new Tag(this.tag);
+ }
+}