diff --git a/out/production/infrastructureLogicielleClient/opinion/Book.class b/out/production/infrastructureLogicielleClient/opinion/Book.class index 8dfdf984bf75a2ed66860015a00782fa24b141a7..dbdc2cd776a1d7fb9146f3653746c2aa23291368 100644 Binary files a/out/production/infrastructureLogicielleClient/opinion/Book.class and b/out/production/infrastructureLogicielleClient/opinion/Book.class differ diff --git a/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class b/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class index 63caec3114c6cff4ec8a5d44055e4672e8b70dec..f72caf53502252cc68a317e97eb5f24e42b6d421 100644 Binary files a/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class and b/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class differ diff --git a/src/opinion/Book.java b/src/opinion/Book.java index 36e2095fc2059336b39df360bbfdb4485f114e83..bc3c4a6902bae07ca41408087c16e64934753158 100644 --- a/src/opinion/Book.java +++ b/src/opinion/Book.java @@ -1,5 +1,7 @@ package opinion; +import java.util.LinkedList; + /** * <h3>A class that allows us to create books and to register new books</h3> * @@ -16,6 +18,7 @@ public class Book { private String title, kind, author, comment; private int nbPages; + private LinkedList<Review> reviews = new LinkedList<>(); /** * <b>Initialization of private variables with constructor</b> @@ -79,13 +82,37 @@ public class Book { String formattedTitle2 = title2.toUpperCase().trim(); return formattedTitle.equals(formattedTitle2); } + public void addOrUpdateReview(String login, float mark, String comment) { + for (Review r : reviews) { + if (r.getReviewerLogin().equalsIgnoreCase(login)) { + r.setMark(mark); + r.setComment(comment); + return; + } + } + reviews.add(new Review(login, mark, comment)); + } + public float getAverageMark() { + if (reviews.isEmpty()) return 0f; + float total = 0f; + for (Review r : reviews) { + total += r.getMark(); + } + return total / reviews.size(); + } /** * <b>String function to display the details of a book</b> * * @return the book's details */ public String toString() { - return "Book : " + getTitle() + "\nKind : " + getKind() + "\nAuthor : " + getAuthor() + "\nNumber of pages : " + getNbPages(); + String text = "Book : " + getTitle() + "\nKind : " + getKind() + "\nAuthor : " + getAuthor() + "\nNumber of pages : " + getNbPages()+"\nOverall note: "+getAverageMark() +"\nReviews"; + int counter = 1; + for (Review r : reviews) { + text += counter + ". " + r.getReviewerLogin() + "\t" + r.getMark() + "\t" + r.getComment(); + counter++; + } + return text; } } diff --git a/src/opinion/Review.java b/src/opinion/Review.java new file mode 100644 index 0000000000000000000000000000000000000000..e66dcd2fb1de9b8d8d37c90be426389448c07ac8 --- /dev/null +++ b/src/opinion/Review.java @@ -0,0 +1,33 @@ +package opinion; + +public class Review { + private String reviewerLogin; + private float mark; + private String comment; + + public Review(String reviewerLogin, float mark, String comment) { + this.reviewerLogin = reviewerLogin; + this.mark = mark; + this.comment = comment; + } + + public String getReviewerLogin() { + return reviewerLogin; + } + + public float getMark() { + return mark; + } + + public void setMark(float mark) { + this.mark = mark; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } +} \ No newline at end of file diff --git a/src/opinion/SocialNetwork.java b/src/opinion/SocialNetwork.java index 3a0fd692baf7c37d462bd30faba8f1c34ea5e554..7a5ba3fce88bd6c3c704944a38e57b1f5e2895fb 100644 --- a/src/opinion/SocialNetwork.java +++ b/src/opinion/SocialNetwork.java @@ -1,13 +1,8 @@ package opinion; -import java.util.LinkedList; +import exceptions.*; -import exceptions.BadEntryException; -import exceptions.ItemBookAlreadyExistsException; -import exceptions.ItemFilmAlreadyExistsException; -import exceptions.MemberAlreadyExistsException; -import exceptions.NotItemException; -import exceptions.NotMemberException; +import java.util.LinkedList; /** * Skeleton for the SocialNetwork @@ -43,7 +38,6 @@ public class SocialNetwork implements ISocialNetwork { */ @Override public int nbMembers() { - // TODO Auto-generated method stub return nbmembers; } @@ -65,8 +59,7 @@ public class SocialNetwork implements ISocialNetwork { */ @Override public int nbBooks() { - // TODO Auto-generated method stub - return listBook.size(); + return nbbooks; } /** @@ -87,18 +80,18 @@ public class SocialNetwork implements ISocialNetwork { /** * Validates the format of a user's login and password (not empty or too short) * - * @param login the login to be validated + * @param login the login to be validated * @param password the password to be validated * @return true if login and password are valid * @throws BadEntryException if either login or password is invalid */ - public boolean validateuser(String login, String password) throws BadEntryException { + public boolean validateUser(String login, String password) throws BadEntryException { boolean result = false; - if (login == null || login.length() <= 1) { - throw new BadEntryException("login is null or empty"); + if (login == null || login.length() <= 1) { + throw new BadEntryException("Error: Login is null or empty"); } if (password == null || password.length() < 4) { - throw new BadEntryException("password is null or empty"); + throw new BadEntryException("Error: Password is null or empty"); } return true; } @@ -106,7 +99,7 @@ public class SocialNetwork implements ISocialNetwork { /** * Authenticates a user's login and password * - * @param login the login of the user + * @param login the login of the user * @param password the password of the user * @return true if password corresponds to registered login, false if wrong password */ @@ -127,10 +120,9 @@ public class SocialNetwork implements ISocialNetwork { * @return true if book does not exist yet in system, false if already exists * @throws BadEntryException if title is empty or too short */ - public boolean validatebook(String title) throws BadEntryException { - boolean result = false; - if (title == null || title.length() <= 1) { - throw new BadEntryException("title is null or empty"); + public boolean validateBook(String title) throws BadEntryException { + if (title == null || title.trim().length() <= 1) { + throw new BadEntryException("Error: Title is null or too short"); } for (Book b : listBook) { if (b.compareTitle(title)) { @@ -143,18 +135,18 @@ public class SocialNetwork implements ISocialNetwork { /** * Adds a new member to the social network. * - * @param login the new member's login + * @param login the new member's login * @param password the new member's password - * @param profile a free String describing the new member's profile - * @throws BadEntryException if parameter is null or too short + * @param profile a free String describing the new member's profile + * @throws BadEntryException if parameter is null or too short * @throws MemberAlreadyExistsException if login already exists */ @Override public void addMember(String login, String password, String profile) throws BadEntryException, MemberAlreadyExistsException { if (profile == null) { - throw new BadEntryException("profile is null or empty"); + throw new BadEntryException("Error: Profile is null or empty"); } - validateuser(login, password); + validateUser(login, password); if (memberRegister(login)) { throw new MemberAlreadyExistsException(); } @@ -163,32 +155,37 @@ public class SocialNetwork implements ISocialNetwork { nbmembers++; } + @Override + public void addItemFilm(String login, String password, String title, String kind, String director, String scriptwriter, int duration) + throws BadEntryException, NotMemberException, ItemFilmAlreadyExistsException { + } + /** * Adds a new book to the social network. * - * @param login login of the member adding the book + * @param login login of the member adding the book * @param password password of the member adding the book - * @param title the new book's title - * @param kind the new book's kind - * @param author the new book's author - * @param nbPages number of pages of the new book's - * @throws BadEntryException if any input is invalid (empty or number pages equal or less than 0) - * @throws NotMemberException if login or password is wrong + * @param title the new book's title + * @param kind the new book's kind + * @param author the new book's author + * @param nbPages number of pages of the new book's + * @throws BadEntryException if any input is invalid (empty or number pages equal or less than 0) + * @throws NotMemberException if login or password is wrong * @throws ItemBookAlreadyExistsException if book already exists in system */ @Override public void addItemBook(String login, String password, String title, String kind, String author, int nbPages) throws BadEntryException, NotMemberException, ItemBookAlreadyExistsException { - validateuser(login, password); + validateUser(login, password); if (title == null || title.trim().isEmpty()) - throw new BadEntryException("Error: Invalid title."); + throw new BadEntryException("Error: Title is null or empty"); if (kind == null || kind.trim().isEmpty()) - throw new BadEntryException("Error: Invalid kind."); + throw new BadEntryException("Error: Kind is null or empty"); if (author == null || author.trim().isEmpty()) - throw new BadEntryException("Error: Invalid author."); + throw new BadEntryException("Error: Author is null or empty"); if (nbPages <= 0) - throw new BadEntryException("Error: Invalid number of pages."); + throw new BadEntryException("Error: Page number is null or less"); if (!authenticateUser(login, password)) - throw new NotMemberException("Error: User doesn't exist"); + throw new NotMemberException("Error: Invalid login or password"); for (Book b : listBook) { if (b.compareTitle(title)) { throw new ItemBookAlreadyExistsException(); @@ -200,15 +197,74 @@ public class SocialNetwork implements ISocialNetwork { } @Override - public void addItemFilm(String login, String password, String title, String kind, String director, String scriptwriter, int duration) - throws BadEntryException, NotMemberException, ItemFilmAlreadyExistsException { + public float reviewItemFilm(String login, String password, String title, float mark, String comment) + throws BadEntryException, NotMemberException, NotItemException { + // TODO Auto-generated method stub + return 0; } + /** + * + * @param login login of the member adding the review + * @param password password of the member adding the review + * @param title the reviewed book's title + * @param mark the mark given by the member for this book + * @param comment the comment given by the member for this book + * @return average mark of current book + * @throws BadEntryException if any input is invalid (mark is out of range) + * @throws NotMemberException if login or password is wrong + * @throws NotItemException if book doesn't exist + */ @Override - public float reviewItemFilm(String login, String password, String title, - float mark, String comment) throws BadEntryException, NotMemberException, NotItemException { - // TODO Auto-generated method stub - return 0; + public float reviewItemBook(String login, String password, String title, float mark, String comment) + throws BadEntryException, NotMemberException, NotItemException { + validateUser(login, password); + if (comment == null) { + throw new BadEntryException("Error: Comment is null"); + } + if (mark < 0.0f || mark > 5.0f) { + throw new BadEntryException("Error: Mark must be between 0.0 and 5.0"); + } + boolean bookExists = validateBook(title); + if (!bookExists) { + throw new NotItemException("Error: Book not found"); + } + if (!authenticateUser(login, password)) { + throw new NotMemberException("Error: Invalid login or password"); + } + Book targetBook = null; + for (Book b : listBook) { + if (b.compareTitle(title)) { + targetBook = b; + break; + } + } + if (targetBook == null) { + throw new NotItemException("Error: Book not found"); + } + targetBook.addOrUpdateReview(login.trim(), mark, comment); + return targetBook.getAverageMark(); + } + + /** + * + * @param title title of searched item(s) + * @return the search result of book or film added + * @throws BadEntryException if any input is invalid (title is empty or too short) + */ + @Override + public LinkedList<String> consultItems(String title) throws BadEntryException { + if (title == null || title.trim().length() <= 1) { + throw new BadEntryException("Error: Title is null or too short"); + } + String formattedTitle = title.trim().toLowerCase(); + LinkedList<String> result = new LinkedList<>(); + for (Book b : listBook) { + if (b.getTitle().toLowerCase().contains(formattedTitle)) { + result.add(b.toString()); + } + } + return result; } /** @@ -228,21 +284,7 @@ public class SocialNetwork implements ISocialNetwork { } return ret.trim(); } - - @Override - public float reviewItemBook(String login, String password, String title, - float mark, String comment) throws BadEntryException, - NotMemberException, NotItemException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public LinkedList<String> consultItems(String title) - throws BadEntryException { - return new LinkedList<String>(); - } - + /** * @param args */ @@ -250,5 +292,4 @@ public class SocialNetwork implements ISocialNetwork { // TODO Auto-generated method stub } - -} \ No newline at end of file +} diff --git a/src/tests/ConsultItemBookTest.java b/src/tests/ConsultItemBookTest.java new file mode 100644 index 0000000000000000000000000000000000000000..825e35d3503eb61ea47381630ebc82f3027620db --- /dev/null +++ b/src/tests/ConsultItemBookTest.java @@ -0,0 +1,132 @@ +package tests; + +import exceptions.*; +import opinion.ISocialNetwork; +import opinion.SocialNetwork; + +import java.util.LinkedList; + +/** + * Tests for the <i>consultItems()</i> method. + */ +public class ConsultItemBookTest { + + /** + * Checks that consultItems() throws the expected exception. + * + * @param sn the social network + * @param title the title to consult + * @param expectedException the expected exception class (with any type of class) + * @param testId the test identifier + * @return 0 if test is OK, 1 if not OK + */ + private static int checkConsultItemBookException(ISocialNetwork sn, String title, Class<?> expectedException, String testId) { + try { + sn.consultItems(title); + System.out.println("Err " + testId + " : Expected exception " + expectedException.getSimpleName() + " was not thrown"); + return 1; + } catch (Exception e) { + if (e.getClass().equals(expectedException)) { + return 0; + } else { + System.out.println("Err " + testId + " : Unexpected exception " + e); + e.printStackTrace(); + return 1; + } + } + } + + /** + * Checks that consultItems() returns the expected number of results. + * + * @param sn the social network + * @param title the title to consult + * @param expectedSize the expected number of returned results + * @param testId the test identifier + * @return 0 if test is OK, 1 if not OK + */ + private static int checkConsultItemBookResult(ISocialNetwork sn, String title, int expectedSize, String testId) { + try { + LinkedList<String> results = sn.consultItems(title); + if (results.size() == expectedSize) { + return 0; + } else { + System.out.println("Err " + testId + " : Expected " + expectedSize + " result(s) but got " + results.size()); + return 1; + } + } catch (Exception e) { + System.out.println("Err " + testId + " : Unexpected exception " + e); + e.printStackTrace(); + return 1; + } + } + + /** + * Main test for consultItems: + * Checks if the method throws BadEntryException in non-nominal conditions + * Checks if the method works correctly in nominal conditions + * + * @return a summary of the performed tests + */ + public static TestReport test() { + ISocialNetwork sn = new SocialNetwork(); + int nbTests = 0; + int nbErrors = 0; + + try { + sn.addMember("User1", "pass1234", "profile"); + sn.addItemBook("User1", "pass1234", "Don Quijote", "Novela", "Cervantes", 800); + sn.addItemBook("User1", "pass1234", "El Principito", "Fábula", "Saint-Exupéry", 100); + sn.addItemBook("User1", "pass1234", "Principios de Física", "Ciencia", "Tipler", 1200); + } catch (BadEntryException | MemberAlreadyExistsException | NotMemberException | + ItemBookAlreadyExistsException e) { + System.out.println("Setup failed: " + e); + e.printStackTrace(); + return null; + } + + System.out.println("Testing consultItems()"); + + // Non-nominal scenarios + + // Test 3.11 : Uninstantiated title + nbTests++; + nbErrors += checkConsultItemBookException(sn, null, BadEntryException.class, "3.11"); + + // Test 3.12 : Invalid title (only containing a space) + nbTests++; + nbErrors += checkConsultItemBookException(sn, " ", BadEntryException.class, "3.12"); + + // Test 3.13 : Invalid title (too short) + nbTests++; + nbErrors += checkConsultItemBookException(sn, "a", BadEntryException.class, "3.13"); + + // Nominal scenarios + + // Test 3.14 : Matches "El Principito" and "Principios de Física" + nbTests++; + nbErrors += checkConsultItemBookResult(sn, "princip", 2, "3.14"); + + // Test 3.15 : Matches "Don Quijote" case-insensitively + nbTests++; + nbErrors += checkConsultItemBookResult(sn, "DON", 1, "3.15"); + + // Test 3.16 : No matches + nbTests++; + nbErrors += checkConsultItemBookResult(sn, "XYZ", 0, "3.16"); + + // Print a summary of the tests and return test results + try { + TestReport tr = new TestReport(nbTests, nbErrors); + System.out.println("ConsultItemBookTest : " + tr); + return tr; + } catch (NotTestReportException e) { + System.out.println("Unexpected error in ConsultItemBookTest test code - Can't return valuable test results"); + return null; + } + } + + public static void main(String[] args) { + test(); + } +} diff --git a/src/tests/ReviewItemBookTest.java b/src/tests/ReviewItemBookTest.java new file mode 100644 index 0000000000000000000000000000000000000000..327b417d8241a061f92dcf11733231a18af95fef --- /dev/null +++ b/src/tests/ReviewItemBookTest.java @@ -0,0 +1,154 @@ +package tests; + +import exceptions.BadEntryException; +import exceptions.NotItemException; +import exceptions.NotMemberException; +import exceptions.NotTestReportException; +import opinion.ISocialNetwork; +import opinion.SocialNetwork; + +/** + * Tests for the SocialNetwork.<i>reviewItemBook()</i> method. + */ +public class ReviewItemBookTest { + + /** + * Checks that reviewItemBook() returns the expected average after adding a review. + * + * @param sn the social network + * @param login the login of the reviewing member + * @param password the password of the reviewing member + * @param title the title of the book + * @param mark the rating given + * @param comment the comment provided + * @param expectedAverage the expected average rating + * @param testId the test identifier + * @return 0 if test is OK, 1 if not OK + */ + private static int checkReviewItemBookAverage(ISocialNetwork sn, String login, String password, String title, float mark, String comment, float expectedAverage, String testId) { + try { + float result = sn.reviewItemBook(login, password, title, mark, comment); + if (Math.abs(result - expectedAverage) < 0.01f) { + return 0; + } else { + System.out.println("Err " + testId + " : Expected average " + expectedAverage + " but got " + result); + return 1; + } + } catch (Exception e) { + System.out.println("Err " + testId + " : Unexpected exception " + e); + e.printStackTrace(); + return 1; + } + } + + /** + * Checks that reviewItemBook() throws the expected exception in invalid situations. + * + * @param sn the social network + * @param login the login of the reviewer + * @param password the reviewer's password + * @param title the book's title + * @param mark the rating + * @param comment the review comment + * @param expectedException the expected exception class + * @param testId the test identifier + * @return 0 if test is OK, 1 if not OK + */ + private static int checkReviewItemBookException(ISocialNetwork sn, String login, String password, String title, float mark, String comment, Class<?> expectedException, String testId) { + try { + sn.reviewItemBook(login, password, title, mark, comment); + System.out.println("Err " + testId + " : Expected exception " + expectedException.getSimpleName() + " was not thrown"); + return 1; + } catch (Exception e) { + if (e.getClass().equals(expectedException)) { + return 0; + } else { + System.out.println("Err " + testId + " : Unexpected exception " + e); + e.printStackTrace(); + return 1; + } + } + } + + /** + * Main test method for reviewItemBook: + * Tests both nominal and non-nominal scenarios. + * + * @return a summary of the performed tests + */ + public static TestReport test() { + ISocialNetwork sn = new SocialNetwork(); + int nbTests = 0; + int nbErrors = 0; + + try { + sn.addMember("Neva", "pass123", "casual reader"); + sn.addMember("Maryam", "1234pass", "fantasy reader"); + sn.addItemBook("Neva", "pass123", "El Hobbit", "Fantasía", "J.R.R. Tolkien", 300); + } catch (Exception e) { + System.out.println("Setup failed: " + e); + e.printStackTrace(); + return null; + } + + System.out.println("Testing reviewItemBook()"); + + // Non-nominal cases + + // Test 3.1 : Member doesn't exist + nbTests++; + nbErrors += checkReviewItemBookException(sn, "Mario", "nopass", "El Hobbit", 4.0f, "Hola", NotMemberException.class, "3.1"); + + // Test 3.2 : Book title doesn't exist + nbTests++; + nbErrors += checkReviewItemBookException(sn, "Neva", "pass123", "Book X", 3.0f, "Does not exist", NotItemException.class, "3.2"); + + // Test 3.3 : Invalid mark (out of range) + nbTests++; + nbErrors += checkReviewItemBookException(sn, "Neva", "pass123", "El Hobbit", 6.0f, "Out of range", BadEntryException.class, "3.3"); + + // Test 3.4 : Invalid login (empty) + nbTests++; + nbErrors += checkReviewItemBookException(sn, "", "pass123", "El Hobbit", 4.0f, "Fantastic book", BadEntryException.class, "3.4"); + + // Test 3.5 : Invalid password (too short) + nbTests++; + nbErrors += checkReviewItemBookException(sn, "Neva", "a", "El Hobbit", 4.0f, "Fantastic book", BadEntryException.class, "3.5"); + + // Test 3.6 : Invalid book title (empty) + nbTests++; + nbErrors += checkReviewItemBookException(sn, "Neva", "pass123", "", 4.0f, "Fantastic book", BadEntryException.class, "3.6"); + + // Test 3.7 : Uninstantiated comment + nbTests++; + nbErrors += checkReviewItemBookException(sn, "Neva", "pass123", "El Hobbit", 4.0f, null, BadEntryException.class, "3.7"); + + // Nominal cases + + // Test 3.1 : First review for "El Hobbit", expects average mark of 4.0 + nbTests++; + nbErrors += checkReviewItemBookAverage(sn, "Neva", "pass123", "El Hobbit", 4.0f, "So good", 4.0f, "3.8"); + + // Test 3.2 : Second review from another user, expects average of (4.0 + 5.0) ÷ 2 = 4.5 + nbTests++; + nbErrors += checkReviewItemBookAverage(sn, "Maryam", "1234pass", "El Hobbit", 5.0f, "Perfect", 4.5f, "3.9"); + + // Test 3.3: Update Neva's review to 3.0, expects average of (3.0 + 5.0) ÷ 2 = 4.0 + nbTests++; + nbErrors += checkReviewItemBookAverage(sn, "Neva", "pass123", "El Hobbit", 3.0f, "Good but too long", 4.0f, "3.11"); + + // Print a summary of the tests and return test results + try { + TestReport report = new TestReport(nbTests, nbErrors); + System.out.println("ReviewItemBookTest : " + report); + return report; + } catch (NotTestReportException e) { + System.out.println("Failed to create TestReport"); + return null; + } + } + + public static void main(String[] args) { + test(); + } +} \ No newline at end of file