From e1fa5c7d60d17f2c7d5329d3e5ad5cd1f508146d Mon Sep 17 00:00:00 2001 From: maze508 Date: Wed, 3 Apr 2024 20:26:04 +0800 Subject: [PATCH 1/4] Add company search to find command and update UserGuide --- docs/UserGuide.md | 29 +++-- .../java/seedu/address/logic/Messages.java | 4 +- .../address/logic/commands/FindCommand.java | 18 +-- .../logic/parser/ArgumentMultimap.java | 32 +++-- .../logic/parser/FindCommandParser.java | 17 +-- ...rdsPredicate.java => SearchPredicate.java} | 25 ++-- .../logic/commands/FindCommandTest.java | 68 +++++----- .../logic/parser/AddressBookParserTest.java | 6 +- .../logic/parser/FindCommandParserTest.java | 63 +++++++--- ...meAndTagContainsKeywordsPredicateTest.java | 116 ------------------ .../model/person/SearchPredicateTest.java | 90 ++++++++++++++ 11 files changed, 249 insertions(+), 219 deletions(-) rename src/main/java/seedu/address/model/person/{NameAndTagContainsKeywordsPredicate.java => SearchPredicate.java} (62%) delete mode 100644 src/test/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicateTest.java create mode 100644 src/test/java/seedu/address/model/person/SearchPredicateTest.java diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3077c871d4e..6496e94a84b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -216,19 +216,22 @@ Examples: ### Search Contact `find` -- Search feature supports search by name and/or tags **ONLY**. -- Finds all contacts whose names or tags matches the substring keyword provided. +- Search feature supports substring search by name and/or tags and/or company **ONLY**. +- Finds all contacts whose names, tags or company matches the substring keyword provided. General Format: `find FIELD/ KEYWORD FIELD/ KEYWORD ...` -- Where `FIELD` is either `n/` for name or `t/` for tag. -- `KEYWORD` is the keyword (**alphabets only**) to search for. +- Where `FIELD` is either `n/` for name or `t/` for tag or `c/` for company. +- `KEYWORD` is the keyword to search for, here are some guidelines: + - Name and Company should contain alphanumeric characters, spaces, hyphens and/or apostrophes only. + - Tags should contain alphanumeric characters only. +- The search is case-insensitive. +- Teh search will find contacts containing the provided keyword as a substring within the specified field(s) +- Multiple Search Fields are treated as a **Logical AND (&&)**. Therefore, a contact must match all specified keywords across any mentioned fields to appear in the search results. #### Search Guidelines -* 'KEYWORD' can **ONLY** be alphabets and **CANNOT** contain spaces or be empty. - * e.g. `find n/John Doe` will **NOT** work. Try `find n/John n/Doe` instead to represent finding John and Doe +* 'KEYWORD' cannot be empty. * e.g. `find n/` will **NOT** work as 'KEYWORD' cannot be empty. - * e.g. `find n/John123` will **NOT** work as 'KEYWORD' cannot contain non-alphabetic characters. * 'KEYWORD' and next 'FIELD' should be separated by a space. @@ -237,10 +240,11 @@ General Format: `find FIELD/ KEYWORD FIELD/ KEYWORD ...` * and there should not be non-alphabetic characters in the 'KEYWORD' field. -* Multiple of the same 'FIELDs' will be treated as a **Logical AND (&&)**. +* Multiple Search 'FIELD's will be treated as a **Logical AND (&&)**. * e.g. `find n/John n/Doe` will return all instances of John and Doe. - * e.g. `find n/Ale n/le` will still return the following example instances ["Alex Liew", "Alexis Lebrun", "Alec"] - + * e.g. `find n/John t/friends c/ Meat` will return all instances of John that are tagged as friends and have Meat in their company name. This means if there exists a contact with the name John that is tagged as friends but has a company Mat, it will not be returned. + * e.g. `find n/Ale n/le` can return contacts such as ["Alex Lew", "Alexis Lebrun", "Alec"] + * 'KEYWORD' should **NOT** be empty and there should be at least one 'FIELD' and 'KEYWORD' pair. * e.g. `find n/ t/` and `find ` will **NOT** work. @@ -249,7 +253,6 @@ General Format: `find FIELD/ KEYWORD FIELD/ KEYWORD ...` * There should not be prefixes before the first 'FIELD' and 'KEYWORD' pair. * e.g. `find testing123 n/John` will **NOT** work. - * The search is case-insensitive. * e.g. `find n/hans` will match `Hans Niemann` and `Hans Zimmer` @@ -257,7 +260,7 @@ General Format: `find FIELD/ KEYWORD FIELD/ KEYWORD ...` * e.g. Results of `find n/Hans n/Bo` will match the results of`find n/Bo n/Hans` * You can have multiple of the same 'FIELD's. - * e.g. `find n/J n/Do` will match names with `J` AND `Do`, like `John Doe` + * e.g. `find n/J n/Do` will match names with `J` AND `Do`, like `John Doe` or `Dohnut Jibs` Examples: @@ -267,7 +270,7 @@ Examples: * `find n/Alex t/friends` returns `Alex Yeoh` who is tagged as a `friend` -* `find n////` returns an error message as the 'KEYWORD' field must consist of alphabets only +* `find n////` returns an error message as the 'KEYWORD' field must consist of alphanumeric characters, spaces, hyphens and/or apostrophes only. * `find n/` or `find t/` or `find n/ t/` returns an error message as the 'KEYWORD' field cannot be empty diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index bb27f0ac514..38e64f8655c 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -19,7 +19,9 @@ public class Messages { public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; public static final String MESSAGE_DUPLICATE_FIELDS = "Multiple values specified for the following single-valued field(s): "; - public static final String MESSAGE_ALPHABET_ONLY = "Value for %1$s must consist of alphabets only."; + public static final String MESSAGE_ALPHANUMERIC_ONLY = "Value for %1$s must consist of alphabets or numbers only."; + public static final String MESSAGE_NAME_COMPANY_CONSTRAINTS = "Value for %1$s must consist of " + + "alphanumeric characters, spaces, hyphens and/or apostrophes only"; public static final String MESSAGE_CANNOT_BE_EMPTY = "Keyword Value of FIELD %1$s cannot be empty."; diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index 52c531c651b..a6e1fde8c94 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -5,26 +5,26 @@ import seedu.address.commons.util.ToStringBuilder; import seedu.address.logic.Messages; import seedu.address.model.Model; -import seedu.address.model.person.NameAndTagContainsKeywordsPredicate; +import seedu.address.model.person.SearchPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all persons in address book whose name, tags or company name contains any of the argument keywords. * Keyword matching is case-insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all persons whose names, tags or company names contain any of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: TYPE/KEYWORD TYPE/KEYWORD ... \n" - + "Where TYPE can be n/ or t/ to specify name or tag respectively.\n" - + "Example: " + COMMAND_WORD + " n/alice n/bob t/friends t/owesMoney \n" - + "Please Refer to User Guide for more details."; + + "Parameters: n/KEYWORD [MORE_KEYWORDS]... t/KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " n/alice t/friends c/Meat \n" + + "Note: Multiple keywords of the same type (name or tag) are treated with a logical AND."; - private final NameAndTagContainsKeywordsPredicate predicate; + private final SearchPredicate predicate; - public FindCommand(NameAndTagContainsKeywordsPredicate predicate) { + public FindCommand(SearchPredicate predicate) { this.predicate = predicate; } diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java index 1e26b55d38a..9244afa70bb 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java @@ -89,22 +89,38 @@ public void verifyNonEmptyKeywordValues(Prefix... prefixes) throws ParseExceptio /** * Throws a {@code ParseException} if any of the keywords in the prefixes given in {@code prefixes} - * are not alphabets. + * are not alphanumeric. */ - public void verifyAllValuesAlpha(Prefix... prefixes) throws ParseException { + public void verifyAllValuesAlphanumeric(Prefix... prefixes) throws ParseException { for (Prefix prefix : prefixes) { List values = getAllValues(prefix); - checkValuesAlpha(values, prefix); + checkValuesAlphanumeric(values, prefix); } } /** - * Throws a {@code ParseException} if any of the characters in the prefixes given in {@code prefixes} - * are not alphabets. + * Throws a {@code ParseException} if any of the characters in the given values + * are not alphanumeric. */ - private void checkValuesAlpha(List values, Prefix prefix) throws ParseException { - if (values.stream().anyMatch(value -> !value.matches("^[a-zA-Z]+$"))) { - throw new ParseException(String.format(Messages.MESSAGE_ALPHABET_ONLY, prefix.getPrefix())); + private void checkValuesAlphanumeric(List values, Prefix prefix) throws ParseException { + if (values.stream().anyMatch(value -> !value.matches("^[a-zA-Z0-9]+$"))) { + throw new ParseException(String.format(Messages.MESSAGE_ALPHANUMERIC_ONLY, prefix.getPrefix())); + } + } + + /** + * Throws a {@code ParseException} if any of the keywords in the prefixes given in {@code prefixes} + * do not adhere to the validation regex for names and company names. + */ + public void verifyValuesNameCompany(Prefix... prefixes) throws ParseException { + String validationRegex = "[\\p{Alnum}'\\-][\\p{Alnum}'\\- ]*"; + for (Prefix prefix : prefixes) { + if (argMultimap.containsKey(prefix)) { + List values = getAllValues(prefix); + if (values.stream().anyMatch(value -> !value.matches(validationRegex))) { + throw new ParseException(String.format(Messages.MESSAGE_NAME_COMPANY_CONSTRAINTS, prefix)); + } + } } } diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index 1db01a1730f..13a675bf675 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -2,6 +2,7 @@ import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; @@ -9,7 +10,7 @@ import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameAndTagContainsKeywordsPredicate; +import seedu.address.model.person.SearchPredicate; /** * Parses input arguments and creates a new FindCommand object @@ -24,7 +25,7 @@ public class FindCommandParser implements Parser { @Override public FindCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_TAG, PREFIX_COMPANY); // Ensure no preamble exists. if (!argMultimap.getPreamble().isEmpty()) { @@ -32,20 +33,22 @@ public FindCommand parse(String args) throws ParseException { } // Check for non-empty Keywords after all Prefixes - argMultimap.verifyNonEmptyKeywordValues(PREFIX_NAME, PREFIX_TAG); + argMultimap.verifyNonEmptyKeywordValues(PREFIX_NAME, PREFIX_TAG, PREFIX_COMPANY); - // Ensure all keywords are alphabets - argMultimap.verifyAllValuesAlpha(PREFIX_NAME, PREFIX_TAG); + // Ensure all keywords are alphanumeric ONLY for tag and alphanumeric + hyphen + apostrophe for name and company + argMultimap.verifyAllValuesAlphanumeric(PREFIX_TAG); + argMultimap.verifyValuesNameCompany(PREFIX_NAME, PREFIX_COMPANY); List nameKeywords = argMultimap.getAllValues(PREFIX_NAME); + List companyKeywords = argMultimap.getAllValues(PREFIX_COMPANY); List tagKeywords = argMultimap.getAllValues(PREFIX_TAG); // Ensure that at least one name or tag keyword is provided - if (nameKeywords.isEmpty() && tagKeywords.isEmpty()) { + if (nameKeywords.isEmpty() && tagKeywords.isEmpty() && companyKeywords.isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } - return new FindCommand(new NameAndTagContainsKeywordsPredicate(nameKeywords, tagKeywords)); + return new FindCommand(new SearchPredicate(nameKeywords, tagKeywords, companyKeywords)); } } diff --git a/src/main/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/SearchPredicate.java similarity index 62% rename from src/main/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/person/SearchPredicate.java index 8030c8aae5e..e2234fdb6df 100644 --- a/src/main/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/person/SearchPredicate.java @@ -10,18 +10,23 @@ /** * Tests that a {@code Person}'s {@code Name} and {@code Tag} matches any of the keywords given. */ -public class NameAndTagContainsKeywordsPredicate implements Predicate { +public class SearchPredicate implements Predicate { private final List nameKeywords; private final List tagKeywords; + private final List companyKeywords; /** - * Constructor for NameAndTagContainsKeywordsPredicate. + * Constructor for SearchPredicate. * @param nameKeywords List of name keywords to search for. * @param tagKeywords List of tag keywords to search for. + * @param companyKeywords List of company keywords to search for. */ - public NameAndTagContainsKeywordsPredicate(List nameKeywords, List tagKeywords) { + public SearchPredicate(List nameKeywords, List tagKeywords, + List companyKeywords) { this.nameKeywords = nameKeywords; this.tagKeywords = tagKeywords; + this.companyKeywords = companyKeywords; + } @Override @@ -31,7 +36,9 @@ public boolean test(Person person) { boolean matchesTags = tagKeywords.stream() .allMatch(keyword -> person.getTags().stream() .anyMatch(tag -> StringUtil.containsSubstringIgnoreCase(tag.tagName, keyword))); - return matchesName && matchesTags; + boolean matchesCompany = companyKeywords.stream() + .allMatch(keyword -> StringUtil.containsSubstringIgnoreCase(person.getCompany().companyName, keyword)); + return matchesName && matchesTags && matchesCompany; } @Override @@ -41,20 +48,22 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof NameAndTagContainsKeywordsPredicate)) { + if (!(other instanceof SearchPredicate)) { return false; } - NameAndTagContainsKeywordsPredicate that = (NameAndTagContainsKeywordsPredicate) other; + SearchPredicate that = (SearchPredicate) other; return Objects.equals(nameKeywords, that.nameKeywords) - && Objects.equals(tagKeywords, that.tagKeywords); + && Objects.equals(tagKeywords, that.tagKeywords) + && Objects.equals(companyKeywords, that.companyKeywords); } @Override public String toString() { - return "NameAndTagContainsKeywordsPredicate{" + return "SearchPredicate{" + "nameKeywords=" + nameKeywords + ", tagKeywords=" + tagKeywords + + ", companyKeywords=" + companyKeywords + '}'; } } diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java index 3d10cb0d777..91a920aaf76 100644 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java @@ -19,7 +19,7 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameAndTagContainsKeywordsPredicate; +import seedu.address.model.person.SearchPredicate; /** * Contains integration tests (interaction with the Model) for {@code FindCommand}. @@ -30,40 +30,28 @@ public class FindCommandTest { @Test public void equals() { - - List firstKeywordsList = Collections.singletonList("first"); - List secondKeywordsList = Collections.singletonList("second"); - List emptyList = Collections.emptyList(); - - NameAndTagContainsKeywordsPredicate firstPredicate = - new NameAndTagContainsKeywordsPredicate(firstKeywordsList, emptyList); - NameAndTagContainsKeywordsPredicate secondPredicate = - new NameAndTagContainsKeywordsPredicate(secondKeywordsList, emptyList); + List nameKeywordsList = Collections.singletonList("first"); + List tagKeywordsList = Collections.singletonList("second"); + SearchPredicate firstPredicate = new SearchPredicate(nameKeywordsList, tagKeywordsList, + Collections.emptyList()); + SearchPredicate secondPredicate = new SearchPredicate(Collections.emptyList(), + Collections.emptyList(), Collections.singletonList("company")); FindCommand findFirstCommand = new FindCommand(firstPredicate); FindCommand findSecondCommand = new FindCommand(secondPredicate); - // same object -> returns true + // standard equality tests assertTrue(findFirstCommand.equals(findFirstCommand)); - - // same values -> returns true - FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); - assertTrue(findFirstCommand.equals(findFirstCommandCopy)); - - // different types -> returns false - assertFalse(findFirstCommand.equals(1)); - - // null -> returns false - assertFalse(findFirstCommand.equals(null)); - - // different person -> returns false + assertTrue(findFirstCommand.equals(new FindCommand(firstPredicate))); assertFalse(findFirstCommand.equals(findSecondCommand)); + assertFalse(findFirstCommand.equals(null)); + assertFalse(findFirstCommand.equals(1)); } @Test public void execute_tagMatches_multiplePersonsFound() { String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); - NameAndTagContainsKeywordsPredicate predicate = preparePredicate("", "friends"); + SearchPredicate predicate = preparePredicate("", "friends", ""); FindCommand command = new FindCommand(predicate); expectedModel.updateFilteredPersonList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); @@ -71,10 +59,9 @@ public void execute_tagMatches_multiplePersonsFound() { } @Test - public void execute_nameAndTagMatches_singlePersonFound() { - // Assuming only ELLE has a "colleague" tag + public void execute_nameTagCompanyMatches_singlePersonFound() { String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 1); - NameAndTagContainsKeywordsPredicate predicate = preparePredicate("Alice", "friends"); + SearchPredicate predicate = preparePredicate("Alice", "friends", ""); FindCommand command = new FindCommand(predicate); expectedModel.updateFilteredPersonList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); @@ -84,8 +71,8 @@ public void execute_nameAndTagMatches_singlePersonFound() { @Test public void execute_noMatches_noPersonFound() { String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); - NameAndTagContainsKeywordsPredicate predicate = preparePredicate("Nonexistent", - "NonexistentTag"); + SearchPredicate predicate = preparePredicate("Nonexistent", "NonexistentTag", + "NonexistentCompany"); FindCommand command = new FindCommand(predicate); expectedModel.updateFilteredPersonList(predicate); assertCommandSuccess(command, model, expectedMessage, expectedModel); @@ -94,21 +81,24 @@ public void execute_noMatches_noPersonFound() { @Test public void toStringMethod() { - NameAndTagContainsKeywordsPredicate predicate = preparePredicate("Alice", "friend"); + SearchPredicate predicate = preparePredicate("Alice", "friend", ""); FindCommand findCommand = new FindCommand(predicate); - String expectedString = "seedu.address.logic.commands.FindCommand{predicate=" - + "NameAndTagContainsKeywordsPredicate{nameKeywords=[Alice], tagKeywords=[friend]}}"; + String expectedString = + "seedu.address.logic.commands.FindCommand{predicate=SearchPredicate{nameKeywords=[Alice], " + + "tagKeywords=[friend], companyKeywords=[]}}"; assertEquals(expectedString, findCommand.toString()); } /** - * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}. + * Parses {@code userInput} into a {@code SearchPredicate}. */ - private NameAndTagContainsKeywordsPredicate preparePredicate(String nameInput, String tagInput) { - List nameKeywords = nameInput.isEmpty() ? Collections.emptyList() - : Arrays.asList(nameInput.split("\\s+")); - List tagKeywords = tagInput.isEmpty() ? Collections.emptyList() - : Arrays.asList(tagInput.split("\\s+")); - return new NameAndTagContainsKeywordsPredicate(nameKeywords, tagKeywords); + private SearchPredicate preparePredicate(String nameInput, String tagInput, String companyInput) { + List nameKeywords = nameInput.isEmpty() ? Collections.emptyList() : Arrays.asList( + nameInput.trim().split("\\s+")); + List tagKeywords = tagInput.isEmpty() ? Collections.emptyList() : Arrays.asList( + tagInput.trim().split("\\s+")); + List companyKeywords = companyInput.isEmpty() ? Collections.emptyList() : Arrays.asList( + companyInput.trim().split("\\s+")); + return new SearchPredicate(nameKeywords, tagKeywords, companyKeywords); } } diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java index f6811b17e92..98c9787f4ad 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java @@ -9,6 +9,7 @@ import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -34,8 +35,8 @@ import seedu.address.model.order.Date; import seedu.address.model.order.Order; import seedu.address.model.order.Remark; -import seedu.address.model.person.NameAndTagContainsKeywordsPredicate; import seedu.address.model.person.Person; +import seedu.address.model.person.SearchPredicate; import seedu.address.testutil.EditPersonDescriptorBuilder; import seedu.address.testutil.PersonBuilder; import seedu.address.testutil.PersonUtil; @@ -95,10 +96,11 @@ public void parseCommand_exit() throws Exception { public void parseCommand_find() throws Exception { List nameKeywords = Arrays.asList("foo", "bar"); List tagKeywords = Arrays.asList("friend", "colleague"); + List companyKeywords = Collections.emptyList(); FindCommand command = (FindCommand) parser.parseCommand( FindCommand.COMMAND_WORD + " n/ " + String.join(" n/ ", nameKeywords) + " t/ " + String.join(" t/ ", tagKeywords)); - assertEquals(new FindCommand(new NameAndTagContainsKeywordsPredicate(nameKeywords, tagKeywords)), command); + assertEquals(new FindCommand(new SearchPredicate(nameKeywords, tagKeywords, companyKeywords)), command); } @Test diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java index fa58cb6bb54..ad7eab21fcc 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java @@ -1,8 +1,8 @@ package seedu.address.logic.parser; -import static seedu.address.logic.Messages.MESSAGE_ALPHABET_ONLY; import static seedu.address.logic.Messages.MESSAGE_CANNOT_BE_EMPTY; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_COMPANY; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test; import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameAndTagContainsKeywordsPredicate; +import seedu.address.model.person.SearchPredicate; public class FindCommandParserTest { private FindCommandParser parser = new FindCommandParser(); @@ -28,38 +28,42 @@ public void parse_emptyArg_throwsParseException() { public void parse_validArgs_returnsFindCommand() { // Single name keyword assertParseSuccess(parser, " " + PREFIX_NAME + "Alice", - new FindCommand(new NameAndTagContainsKeywordsPredicate(Collections.singletonList("Alice"), - Collections.emptyList()))); + new FindCommand(new SearchPredicate(Collections.singletonList("Alice"), + Collections.emptyList(), Collections.emptyList()))); // Multiple name keywords assertParseSuccess(parser, " " + PREFIX_NAME + "Alice " + PREFIX_NAME + "Bob", - new FindCommand(new NameAndTagContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"), - Collections.emptyList()))); + new FindCommand(new SearchPredicate(Arrays.asList("Alice", "Bob"), + Collections.emptyList(), Collections.emptyList()))); // Single tag keyword assertParseSuccess(parser, " " + PREFIX_TAG + "friend", - new FindCommand(new NameAndTagContainsKeywordsPredicate(Collections.emptyList(), - Collections.singletonList("friend")))); + new FindCommand(new SearchPredicate(Collections.emptyList(), + Collections.singletonList("friend"), Collections.emptyList()))); // Multiple tag keywords assertParseSuccess(parser, " " + PREFIX_TAG + "friend " + PREFIX_TAG + "colleague", - new FindCommand(new NameAndTagContainsKeywordsPredicate(Collections.emptyList(), - Arrays.asList("friend", "colleague")))); + new FindCommand(new SearchPredicate(Collections.emptyList(), + Arrays.asList("friend", "colleague"), Collections.emptyList()))); // Combined name and tag keywords assertParseSuccess(parser, " " + PREFIX_NAME + "Alice " + PREFIX_TAG + "friend", - new FindCommand(new NameAndTagContainsKeywordsPredicate(Collections.singletonList("Alice"), - Collections.singletonList("friend")))); + new FindCommand(new SearchPredicate(Collections.singletonList("Alice"), + Collections.singletonList("friend"), Collections.emptyList()))); } @Test - public void parse_nonAlphabeticNameKeyword_throwsParseException() { - assertParseFailure(parser, " n/Alice1", String.format(MESSAGE_ALPHABET_ONLY, PREFIX_NAME)); + public void parse_alphanumericNameKeyword_success() { + assertParseSuccess(parser, " " + PREFIX_NAME + "Alice1", + new FindCommand(new SearchPredicate(Collections.singletonList("Alice1"), + Collections.emptyList(), Collections.emptyList()))); } @Test - public void parse_nonAlphabeticTagKeyword_throwsParseException() { - assertParseFailure(parser, " t/friend1", String.format(MESSAGE_ALPHABET_ONLY, PREFIX_TAG)); + public void parse_alphanumericTagKeyword_success() { + assertParseSuccess(parser, " " + PREFIX_TAG + "friend1", + new FindCommand(new SearchPredicate(Collections.emptyList(), + Collections.singletonList("friend1"), Collections.emptyList()))); } @Test @@ -74,6 +78,33 @@ public void parse_emptyTagKeyword_throwsParseException() { String.format(MESSAGE_CANNOT_BE_EMPTY, PREFIX_TAG)); } + @Test + public void parse_singleCompanyKeyword_success() { + assertParseSuccess(parser, " " + PREFIX_COMPANY + "Acme", + new FindCommand(new SearchPredicate(Collections.emptyList(), Collections.emptyList(), + Collections.singletonList("Acme")))); + } + + @Test + public void parse_multipleCompanyKeywords_success() { + assertParseSuccess(parser, " " + PREFIX_COMPANY + "Acme " + PREFIX_COMPANY + "Widgets", + new FindCommand(new SearchPredicate(Collections.emptyList(), Collections.emptyList(), + Arrays.asList("Acme", "Widgets")))); + } + + @Test + public void parse_combinedNameTagCompanyKeywords_success() { + assertParseSuccess(parser, " " + PREFIX_NAME + "Alice " + PREFIX_TAG + "friend " + PREFIX_COMPANY + "Acme", + new FindCommand(new SearchPredicate(Collections.singletonList("Alice"), + Collections.singletonList("friend"), Collections.singletonList("Acme")))); + } + + @Test + public void parse_emptyCompanyKeyword_throwsParseException() { + assertParseFailure(parser, " " + PREFIX_COMPANY + " ", + String.format(MESSAGE_CANNOT_BE_EMPTY, PREFIX_COMPANY)); + } + @Test public void parse_noFieldsProvided_throwsParseException() { // No name or tag provided diff --git a/src/test/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicateTest.java deleted file mode 100644 index af6caa2ab66..00000000000 --- a/src/test/java/seedu/address/model/person/NameAndTagContainsKeywordsPredicateTest.java +++ /dev/null @@ -1,116 +0,0 @@ -package seedu.address.model.person; - -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.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class NameAndTagContainsKeywordsPredicateTest { - - @Test - public void equals() { - List firstPredicateNameKeywordList = Collections.singletonList("first"); - List secondPredicateNameKeywordList = Arrays.asList("first", "second"); - List firstPredicateTagKeywordList = Collections.singletonList("tag1"); - List secondPredicateTagKeywordList = Arrays.asList("tag2", "tag3"); - - NameAndTagContainsKeywordsPredicate firstPredicate = - new NameAndTagContainsKeywordsPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList); - NameAndTagContainsKeywordsPredicate secondPredicate = - new NameAndTagContainsKeywordsPredicate(secondPredicateNameKeywordList, secondPredicateTagKeywordList); - - // same object -> returns true - assertTrue(firstPredicate.equals(firstPredicate)); - - // same values -> returns true - NameAndTagContainsKeywordsPredicate firstPredicateCopy = - new NameAndTagContainsKeywordsPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList); - assertTrue(firstPredicate.equals(firstPredicateCopy)); - - // different types -> returns false - assertFalse(firstPredicate.equals(1)); - - // null -> returns false - assertFalse(firstPredicate.equals(null)); - - // different person -> returns false - assertFalse(firstPredicate.equals(secondPredicate)); - - NameAndTagContainsKeywordsPredicate thirdPredicate = - new NameAndTagContainsKeywordsPredicate(Collections.emptyList(), Arrays.asList("firstTag")); - NameAndTagContainsKeywordsPredicate fourthPredicate = - new NameAndTagContainsKeywordsPredicate(Collections.emptyList(), Arrays.asList("secondTag")); - - // Different tagKeywords should not be equal - assertFalse(thirdPredicate.equals(fourthPredicate)); - - NameAndTagContainsKeywordsPredicate fifthPredicate = - new NameAndTagContainsKeywordsPredicate(Arrays.asList("Jamus"), Collections.emptyList()); - NameAndTagContainsKeywordsPredicate sixthPredicate = - new NameAndTagContainsKeywordsPredicate(Arrays.asList("Jamuses"), Collections.emptyList()); - - // Different tagKeywords should not be equal - assertFalse(fifthPredicate.equals(sixthPredicate)); - } - - @Test - public void test_nameAndTagContainsKeywords_returnsTrue() { - // Both name and tag match - NameAndTagContainsKeywordsPredicate predicate = new NameAndTagContainsKeywordsPredicate(Arrays.asList("Alice"), - Arrays.asList("friend")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").withTags("friend").build())); - - // Only name matches - predicate = new NameAndTagContainsKeywordsPredicate(Arrays.asList("Alice"), Collections.emptyList()); - assertTrue(predicate.test(new PersonBuilder().withName("Alice").build())); - - // Only tag matches - predicate = new NameAndTagContainsKeywordsPredicate(Collections.emptyList(), Arrays.asList("friend")); - assertTrue(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").build())); - } - - @Test - public void test_nameAndTagDoesNotContainKeywords_returnsFalse() { - // Neither name nor tag match - NameAndTagContainsKeywordsPredicate predicate = new NameAndTagContainsKeywordsPredicate( - Arrays.asList("Nonexistent"), Arrays.asList("nonexistentTag")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("friend").build())); - } - - @Test - public void test_nameContainsKeywordsButTagDoesNot_returnsFalse() { - // Name matches, tag does not match - NameAndTagContainsKeywordsPredicate predicate = new NameAndTagContainsKeywordsPredicate(Arrays.asList("Alice"), - Arrays.asList("nonexistentTag")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").withTags("friend").build())); - } - - @Test - public void test_tagContainsKeywordsButNameDoesNot_returnsFalse() { - // Tag matches, name does not match - NameAndTagContainsKeywordsPredicate predicate = new NameAndTagContainsKeywordsPredicate( - Arrays.asList("Nonexistent"), Arrays.asList("friend")); - assertFalse(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").build())); - } - - @Test - public void toStringMethod() { - List nameKeywords = List.of("nameKeyword"); - List tagKeywords = List.of("tagKeyword"); - NameAndTagContainsKeywordsPredicate predicate = new NameAndTagContainsKeywordsPredicate(nameKeywords, - tagKeywords); - - String expected = "NameAndTagContainsKeywordsPredicate{" - + "nameKeywords=" + nameKeywords - + ", tagKeywords=" + tagKeywords - + '}'; - assertEquals(expected, predicate.toString()); - } -} diff --git a/src/test/java/seedu/address/model/person/SearchPredicateTest.java b/src/test/java/seedu/address/model/person/SearchPredicateTest.java new file mode 100644 index 00000000000..e1bbb409ef3 --- /dev/null +++ b/src/test/java/seedu/address/model/person/SearchPredicateTest.java @@ -0,0 +1,90 @@ +package seedu.address.model.person; + +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.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.PersonBuilder; + +public class SearchPredicateTest { + + @Test + public void equals() { + List firstPredicateNameKeywordList = Collections.singletonList("first"); + List secondPredicateNameKeywordList = Arrays.asList("first", "second"); + List firstPredicateTagKeywordList = Collections.singletonList("tag1"); + List secondPredicateTagKeywordList = Arrays.asList("tag2", "tag3"); + List companyKeywords = Collections.emptyList(); // Adding company keywords for equality check + + SearchPredicate firstPredicate = + new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, companyKeywords); + SearchPredicate secondPredicate = + new SearchPredicate(secondPredicateNameKeywordList, secondPredicateTagKeywordList, companyKeywords); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + SearchPredicate firstPredicateCopy = + new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, companyKeywords); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different person -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_nameTagCompanyContainsKeywords_returnsTrue() { + // Name, tag, and company match + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("friend"), + Arrays.asList("Alice Corp")); + assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").withTags("friend").withCompany( + "Alice Corp").build())); + + // Only name matches + predicate = new SearchPredicate(Arrays.asList("Alice"), Collections.emptyList(), Collections.emptyList()); + assertTrue(predicate.test(new PersonBuilder().withName("Alice").build())); + + // Only tag matches + predicate = new SearchPredicate(Collections.emptyList(), Arrays.asList("friend"), Collections.emptyList()); + assertTrue(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").build())); + + // Only company matches + predicate = new SearchPredicate(Collections.emptyList(), Collections.emptyList(), Arrays.asList("Alice Corp")); + assertTrue(predicate.test(new PersonBuilder().withCompany("Alice Corp").build())); + } + + @Test + public void test_nameTagCompanyDoesNotContainKeywords_returnsFalse() { + // Neither name, tag, nor company match + SearchPredicate predicate = new SearchPredicate( + Arrays.asList("Nonexistent"), Arrays.asList("nonexistentTag"), Arrays.asList("Nonexistent Corp")); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("friend").withCompany( + "Alice Corp").build())); + } + + @Test + public void toStringMethod() { + List nameKeywords = List.of("nameKeyword"); + List tagKeywords = List.of("tagKeyword"); + List companyKeywords = List.of("companyKeyword"); // Adding company keywords for toString check + SearchPredicate predicate = new SearchPredicate(nameKeywords, tagKeywords, companyKeywords); + + String expected = "SearchPredicate{nameKeywords=" + nameKeywords + + ", tagKeywords=" + tagKeywords + + ", companyKeywords=" + companyKeywords + '}'; + assertEquals(expected, predicate.toString()); + } +} From b7b7bbba76d9808230e5831ca08610837f0bd9de Mon Sep 17 00:00:00 2001 From: maze508 Date: Wed, 3 Apr 2024 20:54:27 +0800 Subject: [PATCH 2/4] Update test for SearchPredicate and ArgumentMultiMap --- .../logic/parser/ArgumentTokenizerTest.java | 53 +++++++++++-- .../model/person/SearchPredicateTest.java | 76 ++++++++++--------- 2 files changed, 89 insertions(+), 40 deletions(-) diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java index c97308935f5..c787055f138 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java @@ -1,11 +1,9 @@ package seedu.address.logic.parser; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - import org.junit.jupiter.api.Test; +import seedu.address.logic.parser.exceptions.ParseException; + +import static org.junit.jupiter.api.Assertions.*; public class ArgumentTokenizerTest { @@ -136,6 +134,51 @@ public void tokenize_multipleArgumentsJoined() { assertArgumentAbsent(argMultimap, hatQ); } + @Test + public void verifyAllValuesAlphanumeric_validAlphanumericValues_success() { + // Arguments with valid alphanumeric values + String argsString = " n/Alice1 c/Company123 t/Tag1"; + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("n/"), new Prefix("c/"), new Prefix("t/")); + + // Attempt to verify values as alphanumeric without throwing an exception + assertDoesNotThrow(() -> argMultimap.verifyAllValuesAlphanumeric(new Prefix("t/"))); + } + + @Test + public void verifyAllValuesAlphanumeric_invalidAlphanumericValues_throwsParseException() { + // Arguments with invalid alphanumeric values (contains special characters) + String argsString = " t/Tag#1"; + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("t/")); + + // Expect a ParseException when verifying values + Exception exception = assertThrows(ParseException.class, () -> argMultimap.verifyAllValuesAlphanumeric(new Prefix("t/"))); + + assertTrue(exception.getMessage().contains("Value for t/ must consist of alphabets or numbers only.")); + } + + @Test + public void verifyValuesNameCompany_validNameCompanyValues_success() { + // Arguments with valid name and company values (contains spaces, hyphens, apostrophes) + String argsString = " n/Alice-Bob c/O'Reilly Auto Parts"; + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("n/"), new Prefix("c/")); + + // Attempt to verify values as valid for names and company without throwing an exception + assertDoesNotThrow(() -> argMultimap.verifyValuesNameCompany(new Prefix("n/"), new Prefix("c/"))); + } + + @Test + public void verifyValuesNameCompany_invalidNameCompanyValues_throwsParseException() { + // Arguments with invalid name and company values (contains special characters not allowed) + String argsString = " n/Alice_123 c/ACME* Inc."; + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("n/"), new Prefix("c/")); + + // Expect a ParseException when verifying values + Exception exception = assertThrows(ParseException.class, () -> argMultimap.verifyValuesNameCompany(new Prefix("n/"), new Prefix("c/"))); + + assertTrue(exception.getMessage().contains("Value for n/ must consist of alphanumeric characters, spaces, hyphens and/or apostrophes only")); + } + + @Test public void equalsMethod() { Prefix aaa = new Prefix("aaa"); diff --git a/src/test/java/seedu/address/model/person/SearchPredicateTest.java b/src/test/java/seedu/address/model/person/SearchPredicateTest.java index e1bbb409ef3..19b0cf4f2a1 100644 --- a/src/test/java/seedu/address/model/person/SearchPredicateTest.java +++ b/src/test/java/seedu/address/model/person/SearchPredicateTest.java @@ -20,23 +20,24 @@ public void equals() { List secondPredicateNameKeywordList = Arrays.asList("first", "second"); List firstPredicateTagKeywordList = Collections.singletonList("tag1"); List secondPredicateTagKeywordList = Arrays.asList("tag2", "tag3"); - List companyKeywords = Collections.emptyList(); // Adding company keywords for equality check + List firstCompanyKeywords = Collections.singletonList("company1"); + List secondCompanyKeywords = Arrays.asList("company2", "company3"); SearchPredicate firstPredicate = - new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, companyKeywords); + new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, firstCompanyKeywords); SearchPredicate secondPredicate = - new SearchPredicate(secondPredicateNameKeywordList, secondPredicateTagKeywordList, companyKeywords); + new SearchPredicate(secondPredicateNameKeywordList, secondPredicateTagKeywordList, secondCompanyKeywords); // same object -> returns true assertTrue(firstPredicate.equals(firstPredicate)); // same values -> returns true - SearchPredicate firstPredicateCopy = - new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, companyKeywords); + SearchPredicate firstPredicateCopy = new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, firstCompanyKeywords); assertTrue(firstPredicate.equals(firstPredicateCopy)); - // different types -> returns false - assertFalse(firstPredicate.equals(1)); + // different company keywords -> returns false + SearchPredicate differentCompanyPredicate = new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, secondCompanyKeywords); + assertFalse(firstPredicate.equals(differentCompanyPredicate)); // null -> returns false assertFalse(firstPredicate.equals(null)); @@ -46,45 +47,50 @@ public void equals() { } @Test - public void test_nameTagCompanyContainsKeywords_returnsTrue() { - // Name, tag, and company match - SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("friend"), - Arrays.asList("Alice Corp")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").withTags("friend").withCompany( - "Alice Corp").build())); + public void test_combinationOfNameTagCompanyMatches_returnsTrue() { + // Combination of name, tag, and company matches + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("friend"), Arrays.asList("Corp")); + assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").withTags("friend").withCompany("Alice Corp").build())); + } + + @Test + public void test_nameAndCompanyMatch_tagDoesNotExist_returnsFalse() { + // Name and company match, but tag does not match (logical AND condition) + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("nonexistentTag"), Arrays.asList("Corp")); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").withCompany("Alice Corp").build())); + } + + @Test + public void test_tagAndCompanyMatch_nameDoesNotMatch_returnsFalse() { + // Tag and company match, but name does not match (logical AND condition) + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Nonexistent"), Arrays.asList("friend"), Arrays.asList("Corp")); + assertFalse(predicate.test(new PersonBuilder().withTags("friend").withCompany("Alice Corp").build())); + } + @Test + public void test_onlyOneFieldMatches_returnsFalse() { + // Only one of the fields matches, others do not (logical AND condition) // Only name matches - predicate = new SearchPredicate(Arrays.asList("Alice"), Collections.emptyList(), Collections.emptyList()); - assertTrue(predicate.test(new PersonBuilder().withName("Alice").build())); + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("nonexistentTag"), Arrays.asList("Nonexistent Corp")); + assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("friend").withCompany("Alice Corp").build())); // Only tag matches - predicate = new SearchPredicate(Collections.emptyList(), Arrays.asList("friend"), Collections.emptyList()); - assertTrue(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").build())); + predicate = new SearchPredicate(Arrays.asList("Nonexistent"), Arrays.asList("friend"), Arrays.asList("Nonexistent Corp")); + assertFalse(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").withCompany("Alice Corp").build())); // Only company matches - predicate = new SearchPredicate(Collections.emptyList(), Collections.emptyList(), Arrays.asList("Alice Corp")); - assertTrue(predicate.test(new PersonBuilder().withCompany("Alice Corp").build())); - } - - @Test - public void test_nameTagCompanyDoesNotContainKeywords_returnsFalse() { - // Neither name, tag, nor company match - SearchPredicate predicate = new SearchPredicate( - Arrays.asList("Nonexistent"), Arrays.asList("nonexistentTag"), Arrays.asList("Nonexistent Corp")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("friend").withCompany( - "Alice Corp").build())); + predicate = new SearchPredicate(Arrays.asList("Nonexistent"), Arrays.asList("nonexistentTag"), Arrays.asList("Alice Corp")); + assertFalse(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").withCompany("Alice Corp").build())); } @Test public void toStringMethod() { - List nameKeywords = List.of("nameKeyword"); - List tagKeywords = List.of("tagKeyword"); - List companyKeywords = List.of("companyKeyword"); // Adding company keywords for toString check - SearchPredicate predicate = new SearchPredicate(nameKeywords, tagKeywords, companyKeywords); + List nameKeywords = Collections.singletonList("Alice"); + List tagKeywords = Collections.singletonList("friend"); + List companyKeywords = Collections.singletonList("Alice Corp"); - String expected = "SearchPredicate{nameKeywords=" + nameKeywords - + ", tagKeywords=" + tagKeywords - + ", companyKeywords=" + companyKeywords + '}'; + SearchPredicate predicate = new SearchPredicate(nameKeywords, tagKeywords, companyKeywords); + String expected = "SearchPredicate{nameKeywords=[Alice], tagKeywords=[friend], companyKeywords=[Alice Corp]}"; assertEquals(expected, predicate.toString()); } } From 491978aeb2c760eb9b8c2a7a077e67ee019ca917 Mon Sep 17 00:00:00 2001 From: maze508 Date: Wed, 3 Apr 2024 20:59:56 +0800 Subject: [PATCH 3/4] Fix Checkstyle --- .../logic/parser/ArgumentTokenizerTest.java | 23 ++++++--- .../model/person/SearchPredicateTest.java | 49 ++++++++++++------- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java index c787055f138..558d9c198c4 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java @@ -1,9 +1,15 @@ package seedu.address.logic.parser; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.Test; -import seedu.address.logic.parser.exceptions.ParseException; -import static org.junit.jupiter.api.Assertions.*; +import seedu.address.logic.parser.exceptions.ParseException; public class ArgumentTokenizerTest { @@ -138,7 +144,8 @@ public void tokenize_multipleArgumentsJoined() { public void verifyAllValuesAlphanumeric_validAlphanumericValues_success() { // Arguments with valid alphanumeric values String argsString = " n/Alice1 c/Company123 t/Tag1"; - ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("n/"), new Prefix("c/"), new Prefix("t/")); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, + new Prefix("n/"), new Prefix("c/"), new Prefix("t/")); // Attempt to verify values as alphanumeric without throwing an exception assertDoesNotThrow(() -> argMultimap.verifyAllValuesAlphanumeric(new Prefix("t/"))); @@ -151,7 +158,9 @@ public void verifyAllValuesAlphanumeric_invalidAlphanumericValues_throwsParseExc ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("t/")); // Expect a ParseException when verifying values - Exception exception = assertThrows(ParseException.class, () -> argMultimap.verifyAllValuesAlphanumeric(new Prefix("t/"))); + Exception exception = assertThrows( + ParseException.class, () -> argMultimap.verifyAllValuesAlphanumeric(new Prefix("t/"))); + assertTrue(exception.getMessage().contains("Value for t/ must consist of alphabets or numbers only.")); } @@ -173,9 +182,11 @@ public void verifyValuesNameCompany_invalidNameCompanyValues_throwsParseExceptio ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, new Prefix("n/"), new Prefix("c/")); // Expect a ParseException when verifying values - Exception exception = assertThrows(ParseException.class, () -> argMultimap.verifyValuesNameCompany(new Prefix("n/"), new Prefix("c/"))); + Exception exception = assertThrows( + ParseException.class, () -> argMultimap.verifyValuesNameCompany(new Prefix("n/"), new Prefix("c/"))); - assertTrue(exception.getMessage().contains("Value for n/ must consist of alphanumeric characters, spaces, hyphens and/or apostrophes only")); + assertTrue(exception.getMessage().contains("Value for n/ must consist of alphanumeric characters, " + + "spaces, hyphens and/or apostrophes only")); } diff --git a/src/test/java/seedu/address/model/person/SearchPredicateTest.java b/src/test/java/seedu/address/model/person/SearchPredicateTest.java index 19b0cf4f2a1..8389b9ee54d 100644 --- a/src/test/java/seedu/address/model/person/SearchPredicateTest.java +++ b/src/test/java/seedu/address/model/person/SearchPredicateTest.java @@ -26,17 +26,20 @@ public void equals() { SearchPredicate firstPredicate = new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, firstCompanyKeywords); SearchPredicate secondPredicate = - new SearchPredicate(secondPredicateNameKeywordList, secondPredicateTagKeywordList, secondCompanyKeywords); + new SearchPredicate(secondPredicateNameKeywordList, secondPredicateTagKeywordList, + secondCompanyKeywords); // same object -> returns true assertTrue(firstPredicate.equals(firstPredicate)); // same values -> returns true - SearchPredicate firstPredicateCopy = new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, firstCompanyKeywords); + SearchPredicate firstPredicateCopy = new SearchPredicate(firstPredicateNameKeywordList, + firstPredicateTagKeywordList, firstCompanyKeywords); assertTrue(firstPredicate.equals(firstPredicateCopy)); // different company keywords -> returns false - SearchPredicate differentCompanyPredicate = new SearchPredicate(firstPredicateNameKeywordList, firstPredicateTagKeywordList, secondCompanyKeywords); + SearchPredicate differentCompanyPredicate = new SearchPredicate(firstPredicateNameKeywordList, + firstPredicateTagKeywordList, secondCompanyKeywords); assertFalse(firstPredicate.equals(differentCompanyPredicate)); // null -> returns false @@ -49,38 +52,50 @@ public void equals() { @Test public void test_combinationOfNameTagCompanyMatches_returnsTrue() { // Combination of name, tag, and company matches - SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("friend"), Arrays.asList("Corp")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").withTags("friend").withCompany("Alice Corp").build())); + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("friend"), + Arrays.asList("Corp")); + assertTrue(predicate.test( + new PersonBuilder().withName("Alice Bob").withTags("friend").withCompany("Alice Corp").build())); } @Test - public void test_nameAndCompanyMatch_tagDoesNotExist_returnsFalse() { + public void test_nameAndCompanyMatchTagDoesNotExist_returnsFalse() { // Name and company match, but tag does not match (logical AND condition) - SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("nonexistentTag"), Arrays.asList("Corp")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withCompany("Alice Corp").build())); + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), + Arrays.asList("nonexistentTag"), Arrays.asList("Corp")); + assertFalse(predicate.test( + new PersonBuilder().withName("Alice").withCompany("Alice Corp").build())); } @Test - public void test_tagAndCompanyMatch_nameDoesNotMatch_returnsFalse() { + public void test_tagAndCompanyMatchNameDoesNotMatch_returnsFalse() { // Tag and company match, but name does not match (logical AND condition) - SearchPredicate predicate = new SearchPredicate(Arrays.asList("Nonexistent"), Arrays.asList("friend"), Arrays.asList("Corp")); - assertFalse(predicate.test(new PersonBuilder().withTags("friend").withCompany("Alice Corp").build())); + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Nonexistent"), + Arrays.asList("friend"), Arrays.asList("Corp")); + assertFalse(predicate.test( + new PersonBuilder().withTags("friend").withCompany("Alice Corp").build())); } @Test public void test_onlyOneFieldMatches_returnsFalse() { // Only one of the fields matches, others do not (logical AND condition) // Only name matches - SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), Arrays.asList("nonexistentTag"), Arrays.asList("Nonexistent Corp")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withTags("friend").withCompany("Alice Corp").build())); + SearchPredicate predicate = new SearchPredicate(Arrays.asList("Alice"), + Arrays.asList("nonexistentTag"), Arrays.asList("Nonexistent Corp")); + assertFalse(predicate.test( + new PersonBuilder().withName("Alice").withTags("friend").withCompany("Alice Corp").build())); // Only tag matches - predicate = new SearchPredicate(Arrays.asList("Nonexistent"), Arrays.asList("friend"), Arrays.asList("Nonexistent Corp")); - assertFalse(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").withCompany("Alice Corp").build())); + predicate = new SearchPredicate(Arrays.asList("Nonexistent"), + Arrays.asList("friend"), Arrays.asList("Nonexistent Corp")); + assertFalse(predicate.test( + new PersonBuilder().withName("Bob").withTags("friend").withCompany("Alice Corp").build())); // Only company matches - predicate = new SearchPredicate(Arrays.asList("Nonexistent"), Arrays.asList("nonexistentTag"), Arrays.asList("Alice Corp")); - assertFalse(predicate.test(new PersonBuilder().withName("Bob").withTags("friend").withCompany("Alice Corp").build())); + predicate = new SearchPredicate(Arrays.asList("Nonexistent"), + Arrays.asList("nonexistentTag"), Arrays.asList("Alice Corp")); + assertFalse(predicate.test( + new PersonBuilder().withName("Bob").withTags("friend").withCompany("Alice Corp").build())); } @Test From c7a9e1008a0f9c707bb47b46f4f75ed961dc57a6 Mon Sep 17 00:00:00 2001 From: maze508 Date: Thu, 4 Apr 2024 14:47:59 +0800 Subject: [PATCH 4/4] Find Command Message Changes --- src/main/java/seedu/address/logic/commands/FindCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index a6e1fde8c94..33e1242a9d9 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -16,11 +16,11 @@ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Finds all persons whose names, tags or company names contain any of " + + ": Finds all persons whose names, tags or company names contain all of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: n/KEYWORD [MORE_KEYWORDS]... t/KEYWORD [MORE_KEYWORDS]...\n" + + "Parameters: n/KEYWORD [MORE_KEYWORDS]... t/KEYWORD [MORE_KEYWORDS]... c/KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " n/alice t/friends c/Meat \n" - + "Note: Multiple keywords of the same type (name or tag) are treated with a logical AND."; + + "Note: Multiple keywords (name, tag or company) are treated with a logical AND."; private final SearchPredicate predicate;