diff --git a/.github/workflows/automationtest.yml b/.github/workflows/automationtest.yml
new file mode 100644
index 00000000..4fdd2383
--- /dev/null
+++ b/.github/workflows/automationtest.yml
@@ -0,0 +1,17 @@
+on:
+ pull_request:
+ branches:
+ - main
+ - develop
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ cache: maven
+ - name: Build with Maven
+ run: mvn package -Dmaven.test.skip=true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..7ddfa42d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# Maven
+log/
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### VS Code ###
+.vscode
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# Log file
+*.log
+
+
+# MacOS
+.DS_Store
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..8445f3fa
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,75 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.3
+
+
+
+ ironlibrary
+ 0.0.1-SNAPSHOT
+
+
+ 21
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.10.2
+ test
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/ironhack/IronLibrary.java b/src/main/java/com/ironhack/IronLibrary.java
new file mode 100644
index 00000000..5622c3a4
--- /dev/null
+++ b/src/main/java/com/ironhack/IronLibrary.java
@@ -0,0 +1,30 @@
+package com.ironhack;
+
+import com.ironhack.viewer.LibraryMenu;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.core.env.Environment;
+
+import java.util.Arrays;
+
+@SpringBootApplication
+public class IronLibrary implements CommandLineRunner {
+ @Autowired
+ private LibraryMenu libraryMenu;
+
+ @Autowired
+ private Environment environment;
+
+ public static void main(String[] args) {
+ SpringApplication.run(IronLibrary.class, args);
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ if(!Arrays.asList(environment.getActiveProfiles()).contains("test")){
+ libraryMenu.displayMenu();
+ }
+ }
+}
diff --git a/src/main/java/com/ironhack/model/Author.java b/src/main/java/com/ironhack/model/Author.java
new file mode 100644
index 00000000..ad890128
--- /dev/null
+++ b/src/main/java/com/ironhack/model/Author.java
@@ -0,0 +1,39 @@
+package com.ironhack.model;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Objects;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "author")
+public class Author {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int authorId;
+ private String name;
+ private String email;
+
+ public Author(String name, String email) {
+ setName(name);
+ setEmail(email);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Author author = (Author) o;
+ return authorId == author.authorId && Objects.equals(name, author.name) && Objects.equals(email, author.email);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(authorId, name, email);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ironhack/model/Book.java b/src/main/java/com/ironhack/model/Book.java
new file mode 100644
index 00000000..d3e27ce0
--- /dev/null
+++ b/src/main/java/com/ironhack/model/Book.java
@@ -0,0 +1,65 @@
+package com.ironhack.model;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Objects;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "book")
+public class Book {
+ @Id
+ @Column(name = "isbn")
+ private String isbn;
+
+ private String title;
+
+ @Enumerated(EnumType.STRING)
+ private Categories category;
+
+ private int quantity;
+
+ @ManyToOne(cascade = CascadeType.PERSIST)
+ @JoinColumn(name = "author_id")
+ private Author authorBook;
+
+ public Book(String isbn, String title, Categories category, int quantity, Author author) {
+ this.isbn = isbn;
+ this.title = title;
+ this.category = category;
+ this.quantity = quantity;
+ this.authorBook = author;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Book book)) return false;
+ return quantity == book.quantity && Objects.equals(isbn, book.isbn) &&
+ Objects.equals(title, book.title) && category == book.category &&
+ authorBook.getAuthorId() == book.authorBook.getAuthorId() && Objects.equals(authorBook.getName(), book.authorBook.getName()) &&
+ book.authorBook.getEmail().equals(authorBook.getEmail());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(isbn, title, category, quantity, authorBook);
+ }
+
+ public void updateQuantity(int newQuantity) {
+ this.quantity += newQuantity;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%nISBN: %s%n" +
+ "Title: %s%n" +
+ "Category: %s%n" +
+ "No of Books: %d%n", isbn, title, category, quantity);
+ }
+}
diff --git a/src/main/java/com/ironhack/model/Categories.java b/src/main/java/com/ironhack/model/Categories.java
new file mode 100644
index 00000000..45ed7bb4
--- /dev/null
+++ b/src/main/java/com/ironhack/model/Categories.java
@@ -0,0 +1,14 @@
+package com.ironhack.model;
+
+public enum Categories {
+ HORROR,
+ SCIENCE,
+ ROMANCE,
+ FICTION,
+ FANTASY,
+ ADVENTURE,
+ BIOGRAPHY,
+ MISTERY,
+ OTHERS
+
+}
diff --git a/src/main/java/com/ironhack/model/Issue.java b/src/main/java/com/ironhack/model/Issue.java
new file mode 100644
index 00000000..6381f4be
--- /dev/null
+++ b/src/main/java/com/ironhack/model/Issue.java
@@ -0,0 +1,31 @@
+package com.ironhack.model;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Entity
+@Getter
+@Setter
+@NoArgsConstructor // Generates a no-args constructor for JPA
+public class Issue {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int issueId;
+ private String issueDate;
+ private String returnDate;
+ @ManyToOne
+ @JoinColumn(name="issue_student")
+ private Student issueStudent;
+ @OneToOne
+ @JoinColumn(name="issue_book")
+ private Book issueBook;
+
+ public Issue(String issueDate, String returnDate) {
+ this.issueDate = issueDate;
+ this.returnDate = returnDate;
+ }
+}
+
+
+
diff --git a/src/main/java/com/ironhack/model/Student.java b/src/main/java/com/ironhack/model/Student.java
new file mode 100644
index 00000000..1eba5f47
--- /dev/null
+++ b/src/main/java/com/ironhack/model/Student.java
@@ -0,0 +1,55 @@
+package com.ironhack.model;
+
+import jakarta.persistence.*;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Getter
+@Setter
+@Entity
+@Table(name = "student")
+@Data
+public class Student {
+ @Id
+ private String usn;
+ private String name;
+
+ @OneToMany(mappedBy = "issueStudent", cascade = CascadeType.ALL)
+ private List issues;
+
+ public Student() {
+ }
+
+ public Student(String usn, String name) {
+ this.usn = usn;
+ this.name = name;
+ }
+
+ public void addIssue(Issue issue) {
+ if (issues == null) {
+ issues = new ArrayList<>();
+ }
+ issues.add(issue);
+ issue.setIssueStudent(this);
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Student student = (Student) o;
+ return Objects.equals(usn, student.usn) && Objects.equals(name, student.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(usn, name);
+ }
+
+}
diff --git a/src/main/java/com/ironhack/repository/AuthorRepository.java b/src/main/java/com/ironhack/repository/AuthorRepository.java
new file mode 100644
index 00000000..e1bfaafe
--- /dev/null
+++ b/src/main/java/com/ironhack/repository/AuthorRepository.java
@@ -0,0 +1,12 @@
+package com.ironhack.repository;
+
+import com.ironhack.model.Author;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface AuthorRepository extends JpaRepository {
+ Optional findByName(String authorName);
+}
diff --git a/src/main/java/com/ironhack/repository/BookRepository.java b/src/main/java/com/ironhack/repository/BookRepository.java
new file mode 100644
index 00000000..bed56705
--- /dev/null
+++ b/src/main/java/com/ironhack/repository/BookRepository.java
@@ -0,0 +1,25 @@
+package com.ironhack.repository;
+
+
+import com.ironhack.model.Book;
+import com.ironhack.model.Categories;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface BookRepository extends JpaRepository {
+ Optional findBookByTitle(String title);
+
+ List findBookByCategory(Categories category);
+
+ @Query("Select b FROM Book b JOIN b.authorBook a WHERE a.authorId = :authorId")
+ List findBookByAuthorId(int authorId);
+
+ Optional findByIsbn(String isbn);
+
+ @Query("SELECT b FROM Book b JOIN FETCH b.authorBook")
+ List findAllBooksWithAuthor();
+
+}
diff --git a/src/main/java/com/ironhack/repository/IssueRepository.java b/src/main/java/com/ironhack/repository/IssueRepository.java
new file mode 100644
index 00000000..afab4859
--- /dev/null
+++ b/src/main/java/com/ironhack/repository/IssueRepository.java
@@ -0,0 +1,20 @@
+package com.ironhack.repository;
+
+import com.ironhack.model.Issue;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface IssueRepository extends JpaRepository {
+ Issue findByIssueId(int issueId);
+
+ @Query("SELECT i FROM Issue i JOIN i.issueBook b WHERE b.isbn = ?1")
+ List findByIsbn(String isbn);
+
+ @Query("SELECT i FROM Issue i JOIN i.issueStudent s WHERE s.usn = ?1")
+ List findByUsn(String usn);
+
+}
diff --git a/src/main/java/com/ironhack/repository/StudentRepository.java b/src/main/java/com/ironhack/repository/StudentRepository.java
new file mode 100644
index 00000000..51c05fef
--- /dev/null
+++ b/src/main/java/com/ironhack/repository/StudentRepository.java
@@ -0,0 +1,20 @@
+package com.ironhack.repository;
+
+
+import com.ironhack.model.Issue;
+import com.ironhack.model.Student;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface StudentRepository extends JpaRepository {
+
+ Optional findByName(String name);
+
+ Optional findByUsn(String usn);
+
+ @Query("SELECT i FROM Issue i JOIN i.issueStudent s WHERE s.usn = ?1")
+ List searchBooksByStudent(String usn);
+}
diff --git a/src/main/java/com/ironhack/service/LibraryService.java b/src/main/java/com/ironhack/service/LibraryService.java
new file mode 100644
index 00000000..65959bfa
--- /dev/null
+++ b/src/main/java/com/ironhack/service/LibraryService.java
@@ -0,0 +1,225 @@
+package com.ironhack.service;
+
+import com.ironhack.model.*;
+import com.ironhack.repository.AuthorRepository;
+import com.ironhack.repository.BookRepository;
+import com.ironhack.repository.IssueRepository;
+import com.ironhack.repository.StudentRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ironhack.utils.Utils;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+import java.util.Scanner;
+
+@Service
+public class LibraryService {
+
+ @Autowired
+ BookRepository bookRepository;
+
+ @Autowired
+ AuthorRepository authorRepository;
+
+ @Autowired
+ IssueRepository issueRepository;
+
+ @Autowired
+ StudentRepository studentRepository;
+
+ public void addNewBook(Book book) {
+ Optional optionalBook = bookRepository.findByIsbn(book.getIsbn());
+ if (optionalBook.isPresent()) {
+ optionalBook.get().updateQuantity(book.getQuantity());
+ bookRepository.save(optionalBook.get());
+ } else {
+ bookRepository.save(book);
+ }
+ }
+
+ public Optional searchBookByTitle(String title) {
+ return bookRepository.findBookByTitle(title);
+ }
+
+ public List searchBookByCategory(Categories category) {
+ return bookRepository.findBookByCategory(category);
+ }
+
+ public List searchBookByAuthor(int author_id) {
+ return bookRepository.findBookByAuthorId(author_id);
+ }
+
+ public List searchAllBooks() {
+ return bookRepository.findAll();
+ }
+
+ public String issueBook(String usn, String name, String isbn) {
+ LocalDateTime issueDate = LocalDateTime.now();
+ LocalDateTime returnDate = issueDate.plusDays(7);
+ String issueDateString = Utils.formatter.format(issueDate);
+ String returnDateString = Utils.formatter.format(returnDate);
+ Issue issue = new Issue(issueDateString, returnDateString);
+ Optional student = studentRepository.findByUsn(usn);
+ //check if book is already issued
+ if (!isBookIssued(isbn)) {
+ issue.setIssueStudent(student.get());
+ Optional book = bookRepository.findByIsbn(isbn);
+ issue.setIssueBook(book.get());
+ issueRepository.save(issue);
+ //restar un ejemplar a libro
+ book.get().updateQuantity(-1);
+ bookRepository.save(book.get());
+ return returnDateString;
+ } else {
+ return null;
+ }
+ }
+
+ public boolean isBookIssued(String isbn) {
+ Optional book = bookRepository.findByIsbn(isbn);
+ return book.get().getQuantity() == 0;
+ }
+
+ public List searchBooksByStudentString(String usn) {
+ return studentRepository.searchBooksByStudent(usn);
+ }
+
+ public List findIssueByIsbn(String isbn) {
+ return issueRepository.findByIsbn(isbn);
+ }
+
+ public List findIssueByUsn(String usn) {
+ return issueRepository.findByUsn(usn);
+ }
+
+ public Optional findBookByIsbn(String isbn) {
+ return bookRepository.findByIsbn(isbn);
+ }
+
+ public Optional findStudentByUsn(String usn) {
+ return studentRepository.findByUsn(usn);
+ }
+
+ public List findAllBooksWithAuthors() {
+ return bookRepository.findAllBooksWithAuthor();
+ }
+
+ //añadido hoy
+ public Optional findAuthorByName(String name) {
+ return authorRepository.findByName(name);
+ }
+
+ public void printBooks(List books) {
+ if (books.isEmpty()) {
+ System.out.println("No books found.");
+ } else {
+ System.out.println("Book ISBN Book Title Category No of Books Author name Author mail ");
+ for (Book book : books) {
+ System.out.printf("%-20s %-15s %-12s %-15s %-20s %s%n",
+ book.getIsbn(),
+ book.getTitle(),
+ book.getCategory(),
+ book.getQuantity(),
+ book.getAuthorBook().getName(),
+ book.getAuthorBook().getEmail());
+ }
+ }
+ }
+
+ public void printBooksByCategoryOrAuthor(List books) {
+ if (books.isEmpty()) {
+ System.out.println("No books found.");
+ } else {
+ System.out.println("Book ISBN Book Title Category No of Books");
+ for (Book book : books) {
+ System.out.printf("%-20s %-15s %-12s %-15s%n",
+ book.getIsbn(),
+ book.getTitle(),
+ book.getCategory(),
+ book.getQuantity());
+ }
+ }
+ }
+
+ public String getIsbn(Scanner scanner) {
+ String isbn;
+ do {
+ System.out.print("Enter isbn: ");
+ isbn = scanner.nextLine();
+ if (!Utils.isbnValidator(isbn)) {
+ System.out.println("Invalid ISBN. Please try again.");
+ }
+ } while (!Utils.isbnValidator(isbn));
+ return isbn;
+ }
+
+ public String getAuthorEmail(Scanner scanner) {
+ String email;
+ do {
+ System.out.print("Enter author email: ");
+ email = scanner.nextLine();
+ if (!Utils.emailValidator(email)) {
+ System.out.println("Invalid author email. Please try again.");
+ }
+ } while (!Utils.emailValidator(email));
+ return email;
+ }
+
+ public String getString(Scanner scanner, String promptMessage, String errorMessage) {
+ String input;
+ do {
+ System.out.print(promptMessage);
+ input = scanner.nextLine();
+ if (!Utils.stringValidator(input)) {
+ System.out.println(errorMessage);
+ }
+ } while (!Utils.stringValidator(input));
+ return input;
+ }
+
+ public Categories getCategory(Scanner scanner) {
+ Categories category = null;
+ while (true) {
+ System.out.print("Enter category (Available categories: HORROR, SCIENCE, ROMANCE, FICTION, FANTASY, ADVENTURE, BIOGRAPHY, MISTERY, OTHERS): ");
+ String categoryInput = scanner.nextLine().toUpperCase();
+ for (Categories c : Categories.values()) {
+ if (c.name().equals(categoryInput)) {
+ category = c;
+ break;
+ }
+ }
+ if (category != null) {
+ break;
+ } else {
+ System.out.println("Invalid category. Please enter a valid category.");
+ }
+ }
+ return category;
+ }
+
+ public String getUsn(Scanner scanner) {
+ String usn;
+ do {
+ System.out.print("Enter USN: ");
+ usn = scanner.nextLine();
+ if (!Utils.usnValidator(usn)) {
+ System.out.println("Invalid USN. Please try again.");
+ }
+ } while (!Utils.usnValidator(usn));
+ return usn;
+ }
+
+ public int getNumber(Scanner scanner, String promptMessage, String errorMessage) {
+ String input;
+ do {
+ System.out.print(promptMessage);
+ input = scanner.nextLine();
+ if (!Utils.numericValidator(input)) {
+ System.out.println(errorMessage);
+ }
+ } while (!Utils.numericValidator(input));
+ return Integer.parseInt(input);
+ }
+}
diff --git a/src/main/java/com/ironhack/utils/Utils.java b/src/main/java/com/ironhack/utils/Utils.java
new file mode 100644
index 00000000..7c006fa4
--- /dev/null
+++ b/src/main/java/com/ironhack/utils/Utils.java
@@ -0,0 +1,139 @@
+package com.ironhack.utils;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Utils {
+
+ /**
+ * Validates if the provided string is not null and not empty.
+ */
+ public static boolean stringValidator(String input) {
+ return input != null && !input.trim().isEmpty();
+ }
+
+ /**
+ * Validates if the given string is a valid email address.
+ */
+ public static boolean emailValidator(String email) {
+ if (email == null) return false;
+ String emailRegex = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
+ Pattern emailPattern = Pattern.compile(emailRegex);
+ Matcher matcher = emailPattern.matcher(email);
+ return matcher.matches();
+ }
+
+ /**
+ * Validates ISBNs (ISBN-10 and ISBN-13).
+ * ISBN-10 Examples: "0-306-40615-2", "0306406152"
+ * ISBN-13 Examples: "978-0-306-40615-7", "9780306406157"
+ * Note:
+ * - ISBN-10 is a 10-digit number with or without optional hyphens and might end with an 'X' representing 10.
+ * - ISBN-13 is a 13-digit number, prefixed by '978' or '979', with or without optional hyphens.
+ *
+ * @param isbn the ISBN string to validate.
+ * @return true if the isbn is valid according to ISBN-10 or ISBN-13 standards, false otherwise.
+ */
+ public static boolean isbnValidator(String isbn) {
+ if (isbn == null) return false;
+
+ // ISBN Regex that matches ISBN-10 and ISBN-13 formats, taking into account optional hyphens
+ String isbnRegex = "^(?:ISBN(?:-13)?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})[- 0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$";
+
+ Pattern isbnPattern = Pattern.compile(isbnRegex);
+ Matcher matcher = isbnPattern.matcher(isbn);
+
+ // Matches ISBN against the regex pattern
+ return matcher.matches();
+ }
+
+ /**
+ * Updates the quantity of an item, ensuring it cannot go below zero.
+ */
+ public static int addQuantityUpdate(int currentQuantity, int change) {
+ if (currentQuantity < 0 || change < 0) {
+ System.out.println("Both currentQuantity and change must be non-negative.");
+ return currentQuantity; // Return the original quantity unchanged as an error signal
+ }
+ return currentQuantity + change;
+ }
+
+ public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
+
+ /**
+ * Validates if the provided date string follows the "dd/MM/yyyy" format.
+ *
+ * @param date the date string to validate.
+ * @return true if the date is in the correct format, false otherwise.
+ */
+ public static boolean validateDateFormat(String date) {
+ try {
+ LocalDate.parse(date, formatter);
+ return true; // Date was successfully parsed, indicating valid format
+ } catch (DateTimeParseException e) {
+ return false; // Parsing failed, indicating invalid format
+ }
+ }
+
+ /**
+ * Calculates the difference in days between two dates provided in "dd/MM/yyyy" format.
+ * Assumes both dates have been validated and are in the correct format.
+ *
+ * @param startDate the start date in "dd/MM/yyyy" format.
+ * @param endDate the end date in "dd/MM/yyyy" format.
+ * @return an Optional containing the difference in days between the start and end dates,
+ * or an empty Optional if either date is in an invalid format.
+ */
+ public static Optional dateDifferenceCalculator(String startDate, String endDate) {
+ if (!validateDateFormat(startDate) || !validateDateFormat(endDate)) {
+ // If either date is in invalid format, return an empty Optional
+ return Optional.empty();
+ }
+
+ LocalDate start = LocalDate.parse(startDate, formatter);
+ LocalDate end = LocalDate.parse(endDate, formatter);
+ long daysBetween = ChronoUnit.DAYS.between(start, end);
+
+ return Optional.of(daysBetween);
+ }
+
+
+ /**
+ * Generates a unique identifier, for example, for use with entities that don't have an auto-generated ID.
+ */
+ public static String uniqueIdGenerator() {
+ return UUID.randomUUID().toString();
+ }
+
+ public static boolean usnValidator(String usn) {
+ if (usn == null) return false;
+
+ // USN Regex that matches exactly 13 numeric characters
+ String usnRegex = "^[0-9]{11}$";
+
+ Pattern usnPattern = Pattern.compile(usnRegex);
+ Matcher matcher = usnPattern.matcher(usn);
+
+ // Matches USN against the regex pattern
+ return matcher.matches();
+ }
+
+ public static boolean numericValidator(String input) {
+ if (input == null) return false;
+
+ // Numeric Regex that matches integer numbers
+ String numericRegex = "^[1-9]\\d*$";
+
+ Pattern numericPattern = Pattern.compile(numericRegex);
+ Matcher matcher = numericPattern.matcher(input);
+
+ // Matches input against the regex pattern
+ return matcher.matches();
+ }
+}
diff --git a/src/main/java/com/ironhack/viewer/LibraryMenu.java b/src/main/java/com/ironhack/viewer/LibraryMenu.java
new file mode 100644
index 00000000..f3d266ae
--- /dev/null
+++ b/src/main/java/com/ironhack/viewer/LibraryMenu.java
@@ -0,0 +1,161 @@
+package com.ironhack.viewer;
+
+import com.ironhack.model.*;
+import com.ironhack.repository.AuthorRepository;
+import com.ironhack.service.LibraryService;
+import org.springframework.stereotype.Component;
+import java.util.InputMismatchException;
+import java.util.List;
+import java.util.Optional;
+import java.util.Scanner;
+
+@Component
+public class LibraryMenu {
+ private final LibraryService libraryService;
+ private final AuthorRepository authorRepository;
+ public LibraryMenu(LibraryService libraryService, AuthorRepository authorRepository) {
+ this.libraryService = libraryService;
+ this.authorRepository = authorRepository;
+ }
+ public void displayMenu() {
+ Scanner scanner = new Scanner(System.in);
+ int choice;
+
+ do {
+ System.out.println("Library Management System");
+ System.out.println("1. Add Book");
+ System.out.println("2. Search Book By Title");
+ System.out.println("3. Search Book By Category");
+ System.out.println("4. Search Book By Author");
+ System.out.println("5. Show All Books");
+ System.out.println("6. Issue Book");
+ System.out.println("7. Return Book");
+ System.out.println("8. Search Books By Student");
+ System.out.println("0. Exit");
+ System.out.print("Enter your choice: ");
+ try {
+ choice = scanner.nextInt();
+ scanner.nextLine(); // Consume newline
+ } catch (InputMismatchException ime) {
+ choice = 9;
+ scanner.nextLine();
+ }
+ switch (choice) {
+ case 1:
+ // Add Book
+ String isbnBook = libraryService.getIsbn(scanner);
+ String titleBook = libraryService.getString(scanner, "Enter title: ", "Title can not be empty. Please try again.");
+ Categories categoryBook = libraryService.getCategory(scanner);
+ String authorName = libraryService.getString(scanner, "Enter author name: ", "Author name can not be empty. Please try again.");
+ String authorMail = libraryService.getAuthorEmail(scanner);
+ int quantity = libraryService.getNumber(scanner, "Enter number of books: ", "Invalid input. Please enter a positive quantity.");
+ Author author;
+ Optional optionalAuthor = libraryService.findAuthorByName(authorName);
+ if (optionalAuthor.isPresent()) {
+ author = optionalAuthor.get();
+ } else {
+ author = new Author(authorName, authorMail);
+ authorRepository.save(author);
+ }
+ Book newBook = new Book(isbnBook, titleBook, categoryBook, quantity, author);
+ try {
+ libraryService.addNewBook(newBook);
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Something went wrong" + iae.getMessage());
+ }
+ System.out.println("Book was successfully added to library");
+ break;
+ case 2:
+ String title = libraryService.getString(scanner, "Enter title: ", "Title can not be empty. Please try again.");
+ Optional requestedBook = libraryService.searchBookByTitle(title);
+ if (requestedBook.isPresent()) {
+ System.out.println(requestedBook.get());
+ } else {
+ System.out.println("Book not found");
+ }
+ break;
+ case 3:
+ String category = libraryService.getCategory(scanner).name();
+ try {
+ List books = libraryService.searchBookByCategory(Categories.valueOf(category));
+ libraryService.printBooksByCategoryOrAuthor(books);
+ } catch (InputMismatchException imm) {
+ System.out.print("Wrong Category ");
+ }
+// catch (IllegalArgumentException iae) {
+// System.out.println("Wrong Category");
+// }
+
+ break;
+ case 4:
+ int author_id = libraryService.getNumber(scanner, "Enter Author ID: ", "Invalid input. Please enter a positive numeric value.");
+ List books = libraryService.searchBookByAuthor(author_id);
+ libraryService.printBooksByCategoryOrAuthor(books);
+ break;
+ case 5:
+ libraryService.printBooks(libraryService.searchAllBooks());
+ break;
+ case 6:
+ String usn = libraryService.getUsn(scanner);
+ String name = libraryService.getString(scanner, "Enter student name: ", "Student name can not be empty. Please try again.");
+ String isbn = libraryService.getIsbn(scanner);
+ try {
+ //check if student and book exist.
+ Optional studentOptional = libraryService.findStudentByUsn(usn);
+ Optional bookOptional = libraryService.findBookByIsbn(isbn);
+ if (studentOptional.isPresent() && bookOptional.isPresent()) {
+ Book book = bookOptional.get();
+ if (book.getQuantity() > 0) {
+ String returnDate = libraryService.issueBook(usn, name, isbn);
+ System.out.println("\n");
+ System.out.println("Book issued. Return date : " + returnDate);
+ } else {
+ System.out.println("There aren't any copies left.");
+ }
+ } else {
+ System.out.println("Student or book does not exist.");
+ }
+ } catch (IllegalArgumentException iae) {
+ System.out.println("An exception occurred: " + iae.getMessage());
+ }
+ break;
+ case 7:
+ // Return Book
+ break;
+ case 8:
+ // Search Books By Student
+ String usnSearch = libraryService.getUsn(scanner);
+ try {
+ Optional studentOptional = libraryService.findStudentByUsn(usnSearch);
+ if (studentOptional.isPresent()) {
+ List issueList = libraryService.searchBooksByStudentString(usnSearch);
+ if (issueList.isEmpty()) {
+ System.out.println("No books found for the specified student.");
+ break;
+ }
+ System.out.println("Book Title Student Name Return date");
+ for (Issue issue : issueList) {
+ String bookTitle = issue.getIssueBook().getTitle();
+ String studentName = issue.getIssueStudent().getName();
+ String returnDate = issue.getReturnDate();
+ System.out.printf("%-20s %-15s %s%n", bookTitle, studentName, returnDate);
+ }
+ } else {
+ System.out.println("Student does not exist");
+ }
+ } catch (IllegalArgumentException iae) {
+ System.out.println("An exception occurred: " + iae.getMessage());
+ }
+ break;
+ case 0:
+ System.out.println("Exiting...");
+ break;
+ default:
+ System.out.println("Invalid choice. Please try again.");
+ break;
+ }
+ } while (choice != 0);
+
+ scanner.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 00000000..a096c8ec
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,8 @@
+spring.application.name=ironlibrary
+spring.datasource.url=jdbc:mysql://localhost:3306/library?serverTimezone=UTC
+spring.datasource.username=ironhack
+spring.datasource.password=ironhack2024
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+spring.jpa.hibernate.ddl-auto=validate
+spring.jpa.show-sql=true
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
new file mode 100644
index 00000000..a2a97074
--- /dev/null
+++ b/src/main/resources/schema.sql
@@ -0,0 +1,47 @@
+drop schema library;
+create schema library;
+use library;
+
+
+create table author(
+ author_id int AUTO_INCREMENT not null,
+ name varchar(255) not null,
+ email varchar(100) not null,
+ primary key(author_id)
+);
+
+create table book(
+ isbn varchar(17) not null,
+ title varchar(255) not null,
+ category enum('HORROR',
+ 'SCIENCE',
+ 'ROMANCE',
+ 'FICTION',
+ 'FANTASY',
+ 'ADVENTURE',
+ 'BIOGRAPHY',
+ 'MISTERY',
+ 'OTHERS'),
+ quantity int not null,
+ author_id int not null,
+ foreign key(author_id) references author(author_id),
+ primary key(isbn)
+);
+
+
+CREATE TABLE student(
+ usn varchar(11) not null,
+ name varchar(255) not null,
+ primary key(usn)
+);
+
+CREATE TABLE issue(
+ issue_id INT AUTO_INCREMENT not null,
+ issue_date varchar(255) not null,
+ return_date varchar(255) not null,
+ issue_student varchar(255) not null,
+ issue_book varchar(17) not null,
+ primary key(issue_id),
+ foreign key(issue_student) references student(usn),
+ foreign key(issue_book) references book(isbn)
+);
diff --git a/src/test/java/com/ironhack/model/AuthorTests.java b/src/test/java/com/ironhack/model/AuthorTests.java
new file mode 100644
index 00000000..0312b00f
--- /dev/null
+++ b/src/test/java/com/ironhack/model/AuthorTests.java
@@ -0,0 +1,32 @@
+package com.ironhack.model;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class AuthorTests {
+
+
+ @BeforeEach
+ void setUp() {
+ }
+
+ @AfterEach
+ void tearDown() {
+ }
+
+ @Test
+ public void testAuthorCreation() {
+
+ Author author = new Author("Xavi", "Xavi@mail.com");
+
+ assertNotNull(author);
+
+ assertEquals("Xavi", author.getName());
+
+ assertEquals("Xavi@mail.com", author.getEmail());
+ }
+}
diff --git a/src/test/java/com/ironhack/model/BookTests.java b/src/test/java/com/ironhack/model/BookTests.java
new file mode 100644
index 00000000..7a5b0140
--- /dev/null
+++ b/src/test/java/com/ironhack/model/BookTests.java
@@ -0,0 +1,29 @@
+package com.ironhack.model;
+
+import com.ironhack.repository.BookRepository;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BookTests {
+
+
+ @Test
+ public void testBookCreation(){
+ Author sample_author = new Author("Jose Martinez","josep@gmail.com");
+ Book book = new Book("123456789","Best Book Ever" ,Categories.ADVENTURE, 2, sample_author);
+ assertEquals(book.getAuthorBook(),sample_author);
+ assertEquals(book.getCategory(),Categories.ADVENTURE);
+ assertEquals(book.getIsbn(),"123456789");
+ assertEquals(book.getTitle(),"Best Book Ever");
+ assertEquals(book.getQuantity(),2);
+
+ }
+
+}
diff --git a/src/test/java/com/ironhack/model/IssueTest.java b/src/test/java/com/ironhack/model/IssueTest.java
new file mode 100644
index 00000000..45b91e7a
--- /dev/null
+++ b/src/test/java/com/ironhack/model/IssueTest.java
@@ -0,0 +1,36 @@
+package com.ironhack.model;
+
+import com.ironhack.model.Issue;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class IssueTest {
+
+ @Test
+ void testConstructorAssignsValuesCorrectly() {
+ // Given
+ String expectedIssueDate = "2023-01-01";
+ String expectedReturnDate = "2023-01-10";
+
+ // When
+ Issue issue = new Issue(expectedIssueDate, expectedReturnDate);
+
+ // Then
+ assertEquals(expectedIssueDate, issue.getIssueDate(), "IssueDate should match the constructor input");
+ assertEquals(expectedReturnDate, issue.getReturnDate(), "ReturnDate should match the constructor input");
+ }
+
+ @Test
+ void testInputValidation() {
+ // Given
+ Issue issue = new Issue("2023-01-01", "2023-01-10");
+
+ // When
+ boolean isValid = issue.input_validation();
+
+ // Then
+ assertTrue(isValid, "input_validation should return true");
+ }
+}
diff --git a/src/test/java/com/ironhack/model/StudentTest.java b/src/test/java/com/ironhack/model/StudentTest.java
new file mode 100644
index 00000000..866ee09a
--- /dev/null
+++ b/src/test/java/com/ironhack/model/StudentTest.java
@@ -0,0 +1,38 @@
+package com.ironhack.model;
+import com.ironhack.model.Student;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class StudentTest {
+
+ @BeforeEach
+ void setUp() {
+ }
+
+ @AfterEach
+ void tearDown() {
+ }
+
+ @Test
+ public void testStudentCreation() {
+
+ Student student = new Student("12345678901", "Juan");
+
+ assertNotNull(student);
+
+ assertEquals("12345678901", student.getUsn());
+
+ assertEquals("Juan", student.getName());
+ }
+ @Test
+ void testAddIssue() {
+ Student student = new Student("12345678901", "John Doe");
+ Issue issue = new Issue("2022-01-01", "2022-02-01");
+ student.addIssue(issue);
+ assertTrue(student.getIssues().contains(issue));
+ assertEquals(student, issue.getIssueStudent());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/ironhack/repository/AuthorRepositoryTest.java b/src/test/java/com/ironhack/repository/AuthorRepositoryTest.java
new file mode 100644
index 00000000..3bd92311
--- /dev/null
+++ b/src/test/java/com/ironhack/repository/AuthorRepositoryTest.java
@@ -0,0 +1,37 @@
+package com.ironhack.repository;
+
+import com.ironhack.model.Author;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+@SpringBootTest
+class AuthorRepositoryTest {
+
+ @Autowired
+ private AuthorRepository authorRepository;
+ Author author;
+
+ @AfterEach
+ void tearDown() {
+ authorRepository.deleteAll();
+ }
+
+ @Test
+ public void findAuthorByIdTest() {
+ author = new Author("Xavi", "xavi@mail.com");
+ authorRepository.save(author);
+
+ Optional authorFound = authorRepository.findById(author.getAuthorId());
+
+ assertTrue(authorFound.isPresent());
+ assertEquals(author.getAuthorId(), authorFound.get().getAuthorId());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/ironhack/repository/BookRepositoryTest.java b/src/test/java/com/ironhack/repository/BookRepositoryTest.java
new file mode 100644
index 00000000..5c7601ac
--- /dev/null
+++ b/src/test/java/com/ironhack/repository/BookRepositoryTest.java
@@ -0,0 +1,60 @@
+package com.ironhack.repository;
+
+import com.ironhack.model.Author;
+import com.ironhack.model.Book;
+import com.ironhack.model.Categories;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@SpringBootTest
+public class BookRepositoryTest {
+
+ @Autowired
+ private BookRepository bookRepository;
+
+ @Autowired
+ private AuthorRepository authorRepository;
+ Author author;
+ Book sample_book;
+
+ @BeforeEach
+ void setUp() {
+ author = new Author("Xavi", "xavi@mail.com");
+ sample_book = new Book("1234", "my awesome book", Categories.ADVENTURE, 3, author);
+ authorRepository.save(author);
+ bookRepository.save(sample_book);
+ }
+
+ @AfterEach
+ void tearDown() {
+ bookRepository.deleteAll();
+ }
+
+ @Test
+ public void findBookByTitleTest() {
+ Optional db_book = bookRepository.findBookByTitle("my awesome book");
+ assertTrue(db_book.isPresent());
+ assertEquals(sample_book, db_book.get());
+ }
+ @Test
+ void findAllBooksWithAuthor() {
+ Author author1 = new Author("JK Rowling", "jkrowling@mail.com");
+ Author author2 = new Author("George Orwell", "georgeorwell@mail.com");
+ authorRepository.save(author1);
+ authorRepository.save(author2);
+ Book book1 = new Book("978-3-16-148410-0", "Harry Potter", Categories.FANTASY, 1, author1);
+ Book book2 = new Book("978-3-16-148434-0", "1984", Categories.FICTION, 2, author2);
+ bookRepository.save(book1);
+ bookRepository.save(book2);
+ assertEquals(author1, book1.getAuthorBook());
+ }
+
+}
diff --git a/src/test/java/com/ironhack/repository/IssueRepositoryTest.java b/src/test/java/com/ironhack/repository/IssueRepositoryTest.java
new file mode 100644
index 00000000..fe434a50
--- /dev/null
+++ b/src/test/java/com/ironhack/repository/IssueRepositoryTest.java
@@ -0,0 +1,43 @@
+package com.ironhack.repository;
+
+import com.ironhack.model.Issue;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+public class IssueRepositoryTest {
+
+ @Autowired
+ private IssueRepository issueRepository;
+
+ @Test
+ void testIssueCreationAndRetrieval() {
+ // Create and save an Issue
+ Issue savedIssue = issueRepository.save(new Issue("2023-01-01", "2023-01-10"));
+
+ // Retrieve the saved Issue by ID
+ Optional retrievedIssue = issueRepository.findById(savedIssue.getIssueId());
+
+ // Verify the retrieval
+ assertTrue(retrievedIssue.isPresent(), "Issue should be found by ID");
+ assertEquals("2023-01-01", retrievedIssue.get().getIssueDate(), "Issue dates should match");
+ }
+
+ @Test
+ void testFindByIssueId() {
+ // Create and save an Issue
+ Issue savedIssue = issueRepository.save(new Issue("2023-02-01", "2023-02-10"));
+
+ // Use the custom findByIssueId method
+ Issue foundIssue = issueRepository.findByIssueId(savedIssue.getIssueId());
+
+ // Verify the result
+ assertNotNull(foundIssue, "Issue should be found with custom query");
+ assertEquals("2023-02-01", foundIssue.getIssueDate(), "Issue dates should match in custom query");
+ }
+}
diff --git a/src/test/java/com/ironhack/repository/StudentRepositoryTest.java b/src/test/java/com/ironhack/repository/StudentRepositoryTest.java
new file mode 100644
index 00000000..2e556d37
--- /dev/null
+++ b/src/test/java/com/ironhack/repository/StudentRepositoryTest.java
@@ -0,0 +1,76 @@
+package com.ironhack.repository;
+
+
+import com.ironhack.model.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+class StudentRepositoryTest {
+
+ @Autowired
+ private StudentRepository studentRepository;
+
+ @Autowired
+ private IssueRepository issueRepository;
+
+ @Autowired
+ private BookRepository bookRepository;
+
+ @Autowired
+ private AuthorRepository authorRepository;
+
+ private Student student;
+
+ @BeforeEach
+ void setUp() {
+ student = new Student("09003688800", "Juan");
+ student = studentRepository.save(student);
+ }
+
+ @AfterEach
+ void tearDown() {
+ studentRepository.deleteAll();
+ studentRepository.flush();
+ }
+
+ @Test
+ void searchBooksByStudentString() {
+ Author author1 = new Author("JK Rowling", "jkrowling@mail.com");
+ Author author2 = new Author("George Orwell", "georgeorwell@mail.com");
+ authorRepository.save(author1);
+ authorRepository.save(author2);
+
+ Book book1 = new Book("978-3-16-148410-0", "Harry Potter", Categories.FANTASY, 1, author1);
+ Book book2 = new Book("978-3-16-148434-0", "1984", Categories.FICTION, 2, author2);
+ bookRepository.save(book1);
+ bookRepository.save(book2);
+
+ Issue issue1 = new Issue("2022-08-01 17:09:38", "2022-08-07 17:09:38");
+ issue1.setIssueBook(book1);
+ issue1.setIssueStudent(student);
+ student.addIssue(issue1);
+ issueRepository.save(issue1);
+
+ Issue issue2 = new Issue("2022-10-01 17:09:38", "2022-10-07 17:09:38");
+ issue2.setIssueBook(book2);
+ issue2.setIssueStudent(student);
+ student.addIssue(issue2);
+ issueRepository.save(issue2);
+
+ List results = studentRepository.searchBooksByStudent("09003688800");
+
+ assertNotNull(results);
+ assertEquals(2, results.size());
+ assertEquals("Harry Potter", results.getFirst().getIssueBook().getTitle());
+ assertEquals("2022-08-07 17:09:38", results.getFirst().getReturnDate());
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/ironhack/service/LibraryServiceTest.java b/src/test/java/com/ironhack/service/LibraryServiceTest.java
new file mode 100644
index 00000000..0ebc798b
--- /dev/null
+++ b/src/test/java/com/ironhack/service/LibraryServiceTest.java
@@ -0,0 +1,202 @@
+package com.ironhack.service;
+
+import com.ironhack.model.*;
+import com.ironhack.repository.AuthorRepository;
+import com.ironhack.repository.BookRepository;
+import com.ironhack.repository.IssueRepository;
+import com.ironhack.repository.StudentRepository;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.time.LocalDate;
+import java.util.List;
+import static org.junit.jupiter.api.Assertions.*;
+import java.util.Optional;
+
+@SpringBootTest
+class LibraryServiceTest {
+
+ @Autowired
+ private LibraryService libraryService;
+
+ @Autowired
+ private BookRepository bookRepository;
+
+ @Autowired
+ private AuthorRepository authorRepository;
+
+ @Autowired
+ private IssueRepository issueRepository;
+
+ @Autowired
+ private StudentRepository studentRepository;
+ Author author1;
+ Author author2;
+ Book book1;
+ Book book2;
+ Student student1;
+ Student student2;
+ private Issue issue1;
+ private Issue issue2;
+
+ @BeforeEach
+ void setUp() {
+ author1 = new Author("Corey Schafer","corey@mail.com");
+ author2 = new Author("Antonio Gámez","antonio@mail.com");
+ book1 = new Book("978-1-123456-12-7","Java is awesome", Categories.HORROR,8,author1);
+ book2 = new Book("978-1-123456-12-6","SLA Driven Governance of RESTful systems", Categories.SCIENCE,5,author2);
+ authorRepository.saveAll(List.of(author1, author2));
+ bookRepository.saveAll(List.of(book1, book2));
+ student1 = new Student("12345678901", "Mosh");
+ student2 = new Student("12345678902", "John");
+ studentRepository.saveAll(List.of(student1, student2));
+ issue1 = new Issue(LocalDate.now().toString(), LocalDate.now().plusMonths(1).toString());
+ issue1.setIssueBook(book1);
+ issue1.setIssueStudent(student1);
+
+ issue2 = new Issue(LocalDate.now().toString(), LocalDate.now().plusMonths(1).toString());
+ issue2.setIssueBook(book2);
+ issue2.setIssueStudent(student2);
+
+ issueRepository.saveAll(List.of(issue1, issue2));
+ }
+ @AfterEach
+ void tearDown() {
+ issueRepository.deleteAll();
+ bookRepository.deleteAll();
+ studentRepository.deleteAll();
+ authorRepository.deleteAll();
+ }
+
+ @Test
+ void addNewBook() {
+ libraryService.addNewBook(book1);
+ Optional optionalBook = bookRepository.findByIsbn(book1.getIsbn());
+ assertTrue(optionalBook.isPresent());
+ assertEquals(book1.getIsbn(), optionalBook.get().getIsbn());
+ }
+
+ @Test
+ void searchBookByTitle() {
+ Optional optionalBook = libraryService.searchBookByTitle("Java is awesome");
+
+ assertTrue(optionalBook.isPresent());
+ assertEquals("Java is awesome", optionalBook.get().getTitle());
+ }
+
+ @Test
+ void searchBookByCategory() {
+ // Use the searchBookByCategory method to retrieve the books
+ List books = libraryService.searchBookByCategory(Categories.HORROR);
+
+ // Assert that the books returned by the method are the same as the books you saved
+ assertEquals(1, books.size());
+ assertTrue(books.contains(book1));
+ }
+
+ @Test
+ void searchBookByAuthor() {
+ // Use the searchBookByAuthor method to retrieve the books
+ List booksByAuthor1 = libraryService.searchBookByAuthor(author1.getAuthorId());
+ List booksByAuthor2 = libraryService.searchBookByAuthor(author2.getAuthorId());
+
+ // Assert that the books returned by the method are the same as the books you saved
+ assertEquals(1, booksByAuthor1.size());
+ assertTrue(booksByAuthor1.contains(book1));
+
+ assertEquals(1, booksByAuthor2.size());
+ assertTrue(booksByAuthor2.contains(book2));
+ }
+
+ @Test
+ void searchBooksByStudentStringTest() {
+ List foundIssues = libraryService.searchBooksByStudentString(student1.getUsn());
+
+ // Verify the correct book issues are retrieved
+ assertNotNull(foundIssues, "The returned list of issues should not be null");
+ assertEquals(1, foundIssues.size(), "Expected one issue to be found for student1");
+
+ // Use getters in Issue for issueBook
+ assertEquals(book1.getIsbn(), foundIssues.get(0).getIssueBook().getIsbn(),
+ "The ISBN of the book in the found issue should match book1's ISBN");
+ }
+
+ @Test
+ void isBookIssuedTest(){
+ Author author3 = new Author("original author", "newemail@mail.com");
+ authorRepository.save(author3);
+ Book bookZeroQuantity = new Book("123-5-567890-00-0", "No books lefts", Categories.ADVENTURE, 0, author3);
+ bookRepository.save(bookZeroQuantity);
+ assertTrue(libraryService.isBookIssued(bookZeroQuantity.getIsbn()));
+ assertFalse(libraryService.isBookIssued(book1.getIsbn()));
+ }
+
+ @Test
+ void issueBookTestValid(){
+ Author author4 = new Author("another author", "newermail@mail.com");
+ authorRepository.save(author4);
+ Book bookToIssue = new Book("123-5-567890-12-3", "Book to issue", Categories.ADVENTURE, 5, author4);
+ bookRepository.save(bookToIssue);
+ String returnDate = libraryService.issueBook("12345678901", "Book to issue", "123-5-567890-12-3");
+ List results = libraryService.findIssueByIsbn("978-1-123456-12-7");
+ assertNotNull(results);
+ assertEquals("Java is awesome", results.getFirst().getIssueBook().getTitle());
+ assertEquals("12345678901", results.getFirst().getIssueStudent().getUsn());
+ assertNotNull(returnDate);
+ }
+
+ @Test
+ void issueBookTest_OutOfStock(){
+ Author author5 = new Author("nuevo", "email@mail.com");
+ authorRepository.save(author5);
+ Book outOfStockBook = new Book("978-1-123456-10-5","Libro agotado2", Categories.FANTASY,0,author5);
+ bookRepository.save(outOfStockBook);
+ String returnDate = libraryService.issueBook("12345678901", "Libro agotado2", "978-1-123456-10-5");
+ assertNull(returnDate);
+ }
+
+ @Test
+ void testFindBookByIsbn() {
+ // Use the findBookByIsbn method to retrieve the book
+ Optional optionalBook1 = libraryService.findBookByIsbn(book1.getIsbn());
+ Optional optionalBook2 = libraryService.findBookByIsbn(book2.getIsbn());
+
+ // Assert that the book returned by the method is the same as the book you saved
+ assertTrue(optionalBook1.isPresent());
+ assertEquals(book1.getIsbn(), optionalBook1.get().getIsbn());
+
+ assertTrue(optionalBook2.isPresent());
+ assertEquals(book2.getIsbn(), optionalBook2.get().getIsbn());
+ }
+
+ @Test
+ void testFindStudentByUsn() {
+ // Use the findStudentByUsn method to retrieve the students
+ Optional optionalStudent1 = libraryService.findStudentByUsn(student1.getUsn());
+ Optional optionalStudent2 = libraryService.findStudentByUsn(student2.getUsn());
+
+ // Assert that the student returned by the method is the same as the student you saved
+ assertTrue(optionalStudent1.isPresent());
+ assertEquals(student1.getUsn(), optionalStudent1.get().getUsn());
+
+ assertTrue(optionalStudent2.isPresent());
+ assertEquals(student2.getUsn(), optionalStudent2.get().getUsn());
+ }
+
+ @Test
+ void testFindAuthorByName() {
+ // Use the findAuthorByName method to retrieve the authors
+ Optional optionalAuthor1 = libraryService.findAuthorByName(author1.getName());
+ Optional optionalAuthor2 = libraryService.findAuthorByName(author2.getName());
+
+ // Assert that the author returned by the method is the same as the author you saved
+ assertTrue(optionalAuthor1.isPresent());
+ assertEquals(author1.getName(), optionalAuthor1.get().getName());
+
+ assertTrue(optionalAuthor2.isPresent());
+ assertEquals(author2.getName(), optionalAuthor2.get().getName());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/ironhack/utils/UtilsTest.java b/src/test/java/com/ironhack/utils/UtilsTest.java
new file mode 100644
index 00000000..58514e5c
--- /dev/null
+++ b/src/test/java/com/ironhack/utils/UtilsTest.java
@@ -0,0 +1,97 @@
+package com.ironhack.utils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class UtilsTest {
+
+ @Test
+ void testStringValidator() {
+ assertTrue(Utils.stringValidator("Hello World"));
+ assertFalse(Utils.stringValidator(""));
+ assertFalse(Utils.stringValidator(null));
+ }
+ @Test
+ void testEmailValidator() {
+ assertTrue(Utils.emailValidator("example@example.com"));
+ assertFalse(Utils.emailValidator("example@"));
+ assertFalse(Utils.emailValidator("example.com"));
+ assertFalse(Utils.emailValidator(null));
+ }
+
+ @Test
+ void testIsbnValidator() {
+ assertTrue(Utils.isbnValidator("978-3-16-148410-0"));
+ assertTrue(Utils.isbnValidator("0-306-40615-2"));
+
+ assertFalse(Utils.isbnValidator("1234567890123"));
+ assertFalse(Utils.isbnValidator(null));
+ }
+
+ @Test
+ void testQuantityUpdate() {
+ // Test for successful quantity update
+ assertEquals(10, Utils.addQuantityUpdate(8, 2), "The sum should be 10 for valid inputs.");
+
+ // Test for unsuccessful quantity update
+ assertEquals(5, Utils.addQuantityUpdate(5, -10), "Negative changes should result in no update.");
+ assertEquals(-5, Utils.addQuantityUpdate(-5, 10), "Initial negative quantities should behave normally.");
+ }
+
+ @Test
+ void testValidateDateFormat() {
+ // Test valid date formats
+ assertTrue(Utils.validateDateFormat("01/01/2023"), "Date should be considered valid");
+ assertTrue(Utils.validateDateFormat("31/12/2023"), "Date should be considered valid");
+
+ // Test invalid date formats
+ assertFalse(Utils.validateDateFormat("2023-01-01"), "Date should be considered invalid");
+ assertFalse(Utils.validateDateFormat("01/2023"), "Date should be considered invalid");
+ assertFalse(Utils.validateDateFormat("test"), "Date should be considered invalid");
+ }
+
+ @Test
+ void testDateDifferenceCalculator() {
+ // Test valid date inputs
+ assertEquals(Optional.of(1L), Utils.dateDifferenceCalculator("01/01/2023", "02/01/2023"),
+ "The difference should be 1 day for valid date inputs");
+ assertEquals(Optional.of(-1L), Utils.dateDifferenceCalculator("02/01/2023", "01/01/2023"),
+ "The difference should be -1 days for valid date inputs");
+
+ // Test invalid date inputs
+ assertEquals(Optional.empty(), Utils.dateDifferenceCalculator("2023-01-01", "01/01/2023"),
+ "Should return Optional.empty() for invalid date format");
+ assertEquals(Optional.empty(), Utils.dateDifferenceCalculator("01/01/2023", "2023-01-02"),
+ "Should return Optional.empty() for invalid date format");
+ }
+
+
+ @Test
+ void testUniqueIdGenerator() {
+ String uniqueId1 = Utils.uniqueIdGenerator();
+ String uniqueId2 = Utils.uniqueIdGenerator();
+ Assertions.assertNotEquals(uniqueId1, uniqueId2);
+ }
+
+ @Test
+ void usnValidator(){
+ assertTrue(Utils.usnValidator("12345678901"));
+ assertTrue(Utils.usnValidator("98765432101"));
+ assertFalse(Utils.usnValidator("123"));
+ assertFalse(Utils.usnValidator(null));
+ }
+
+ @Test
+ void numericValidator(){
+ assertTrue(Utils.numericValidator("123"));
+ assertTrue(Utils.numericValidator("54"));
+ assertFalse(Utils.numericValidator("0"));
+ assertFalse(Utils.numericValidator(null));
+ assertFalse(Utils.numericValidator("-2"));
+ assertFalse(Utils.numericValidator("2.5"));
+ }
+}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
new file mode 100644
index 00000000..30f6c7b9
--- /dev/null
+++ b/src/test/resources/application.properties
@@ -0,0 +1,8 @@
+spring.datasource.url=jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC&createDatabaseIfNotExist=true
+spring.datasource.username=ironhack
+spring.datasource.password=ironhack2024
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.profiles.active=test
+spring.jpa.hibernate.ddl-auto=create-drop
+
+spring.jpa.show-sql=true
\ No newline at end of file