diff --git a/InventoryManagementSystem/Dockerfile b/InventoryManagementSystem/Dockerfile new file mode 100644 index 0000000..e722909 --- /dev/null +++ b/InventoryManagementSystem/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:21-jdk-slim +RUN groupadd -r appuser && useradd -r -g appuser appuser +WORKDIR /app +COPY build/libs/simple-inventory-1.0.0.jar ./app.jar +RUN chown appuser:appuser ./app.jar +USER appuser +CMD ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"] \ No newline at end of file diff --git a/InventoryManagementSystem/build.gradle b/InventoryManagementSystem/build.gradle new file mode 100644 index 0000000..843705d --- /dev/null +++ b/InventoryManagementSystem/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'java' +apply plugin: 'application' + +version = "1.0.0" + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +application { + mainClass = "inventory.Main" +} + +jar { + archiveBaseName = 'simple-inventory' + manifest { + attributes( + 'Main-Class': application.mainClass + ) + } +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/main/java/inventory/DiscountCalculator.java b/InventoryManagementSystem/src/main/java/inventory/DiscountCalculator.java new file mode 100644 index 0000000..431ef92 --- /dev/null +++ b/InventoryManagementSystem/src/main/java/inventory/DiscountCalculator.java @@ -0,0 +1,93 @@ +package inventory; + +import java.util.Objects; + +import static inventory.ProductFactory.BOOK_TYPE; + +/** + * Strategy pattern implementation for calculating different types of discounts. + * Requirements: + * - Student discount: 10% off books only + * - Bulk discount: 15% off when buying 5+ items + * - No discount option + * - Return discount amount and description + */ +public class DiscountCalculator { + + /** + * Inner class to hold discount calculation results. + */ + public static class DiscountResult { + private final double discountAmount; + private final String description; + + public DiscountResult(double discountAmount, String description) { + if (discountAmount < 0) { + throw new IllegalArgumentException("Discount amount must be greater than 0"); + } + this.discountAmount = discountAmount; + this.description = Objects.requireNonNullElse(description, ""); + } + + public double getDiscountAmount() { + return discountAmount; + } + + public String getDescription() { + return description; + } + } + + /** + * @param product - the product being purchased + * @param quantity - quantity being purchased + * @param discountType - type of discount to apply (STUDENT, BULK, NONE) + * @return DiscountResult with amount and description + */ + public static DiscountResult calculateDiscount(Product product, int quantity, String discountType) { + + double discountAmount = 0.0; + String description = "No discount applied"; + + switch (discountType.toUpperCase()) { + + case "STUDENT": + if (isEligibleForStudentDiscount(product)) { + discountAmount = product.getPrice() * quantity * 0.10; + description = "Student discount: 10% off books"; + } else { + description = "Student discount only applies to books"; + } + break; + + case "BULK": + if (isEligibleForBulkDiscount(quantity)) { + discountAmount = product.getPrice() * quantity * 0.15; + description = "Bulk discount: 15% off for 5+ items"; + } else { + description = "Bulk discount requires 5+ items"; + } + break; + + case "EMPLOYEE": + discountAmount = product.getPrice() * quantity * 0.20; + description = "Employee discount: 20% off"; + break; + + case "NONE": + default: + // No discount + break; + } + + return new DiscountResult(discountAmount, description); + } + + private static boolean isEligibleForStudentDiscount(Product product) { + return BOOK_TYPE.equals(product.getType()); + } + + private static boolean isEligibleForBulkDiscount(int quantity) { + return quantity >= 5; + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/main/java/inventory/InventoryManager.java b/InventoryManagementSystem/src/main/java/inventory/InventoryManager.java new file mode 100644 index 0000000..c99d649 --- /dev/null +++ b/InventoryManagementSystem/src/main/java/inventory/InventoryManager.java @@ -0,0 +1,165 @@ +package inventory; + +import inventory.DiscountCalculator.DiscountResult; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Main business logic class that manages inventory operations. + * Requirements: + * - Use ProductFactory for creating products + * - Use DiscountCalculator for sales with discounts + * - Maintain product collection + * - Provide inventory operations and statistics + */ +public class InventoryManager { + + private final Map products; + + public InventoryManager() { + products = new HashMap<>(); + } + + /** + * Add product to inventory using Factory pattern + * @param id - product identifier + * @param name - product name + * @param type - product type + * @param price - product price + * @param quantity - initial quantity + * @return True if product was added, false otherwise + */ + public boolean addProduct(String id, String name, String type, double price, int quantity) { + try { + Product product = ProductFactory.createProduct(id, name, type, price, quantity); + products.put(id, product); + System.out.println("Product added successfully: " + product); + return true; + } catch (IllegalArgumentException e) { + System.out.println("Error adding product: " + e.getMessage()); + return false; + } + } + + /** + * @param id - product identifier + * @param quantity - quantity to sell + * @param discountType - type of discount to apply + * @return True if book was sold, False if there was an error + */ + public boolean sellProduct(String id, int quantity, String discountType) { + if (!productExists(id)) { + System.out.println("Product not found: " + id); + return false; + } + + Product product = products.get(id); + + if (!product.isInStock() || product.getQuantity() < quantity) { + System.out.println("Insufficient stock for product: " + id); + return false; + } + + // Calculate discount using Strategy pattern + DiscountResult discount = + DiscountCalculator.calculateDiscount(product, quantity, discountType); + + // Calculate total and apply discount + double totalPrice = product.getPrice() * quantity; + double finalPrice = totalPrice - discount.getDiscountAmount(); + + // Update inventory + product.sell(quantity); + + // Display sale information + displaySalesSummary(product, quantity, totalPrice, finalPrice, discount); + + return true; + } + + /** + * Add stock to existing product + * @param id - product identifier + * @param quantity - quantity to add + * @return True if stock was added, False if there was an error + */ + public boolean addStock(String id, int quantity) { + Product product = products.get(id); + if (product == null) { + System.out.println("Product not found: " + id); + return false; + } + + product.addStock(quantity); + + System.out.printf("Added %d items to product %s%n", quantity, product); + + return true; + } + + /** + * Display all products in inventory + */ + public void viewInventory() { + System.out.println("Inventory:"); + if (products.isEmpty()) { + System.out.println("Inventory is empty."); + return; + } + + for (Product product : products.values().stream().sorted(Comparator.comparing(Product::getId)).toList()) { + System.out.printf("\t%s%n",product); + } + } + + /** + * Calculate total inventory value + * @return total value of all products + */ + public double getInventoryValue() { + return products.values().stream().mapToDouble(p -> p.getQuantity() * p.getPrice()).sum(); + } + + /** + * Get products with low stock + * @param threshold - minimum stock level + * @return list of products below threshold + */ + public List getLowStockProducts(int threshold) { + return products.values().stream().filter(p -> p.getQuantity() < threshold).toList(); + } + + /** + * Display inventory statistics + */ + public void viewStatistics() { + System.out.printf("Total products: %d%n", products.size()); + System.out.printf("Total inventory value: %f%n", getInventoryValue()); + + List lowStock = getLowStockProducts(5); + if (lowStock.isEmpty()) { + System.out.println("No products low on stock."); + } else { + System.out.println("Products low on stock (<5):"); + for (Product product : lowStock) { + System.out.printf("\tProduct: %s (quantity: %d, price: %f)%n", product.getName(), product.getQuantity(), product.getPrice()); + } + } + } + + public boolean productExists(String id) { + return id != null && products.containsKey(id); + } + + private void displaySalesSummary(Product product, int quantity, double originalPrice, double finalPrice, DiscountResult discountInfo) { + System.out.printf("Sale completed:%n"); + System.out.printf("Product: %s%n", product.getName()); + System.out.printf("Quantity: %d%n", quantity); + System.out.printf("Original Price: $%.2f%n", originalPrice); + System.out.printf("Discount: $%.2f (%s)%n", discountInfo.getDiscountAmount(), discountInfo.getDescription()); + System.out.printf("Final Price: $%.2f%n", finalPrice); + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/main/java/inventory/Main.java b/InventoryManagementSystem/src/main/java/inventory/Main.java new file mode 100644 index 0000000..2721415 --- /dev/null +++ b/InventoryManagementSystem/src/main/java/inventory/Main.java @@ -0,0 +1,164 @@ +package inventory; + +import java.util.Scanner; + +/** + * Main class providing command-line interface for the inventory system. + * + * Requirements: + * - Menu with 6 options: Add, View, Sell, Stock, Statistics, Exit + * - Input validation and error handling + * - Sample data for testing + * - User-friendly interface + */ +public class Main { + + private static InventoryManager manager; + private static Scanner scanner; + + public static void main(String[] args) { + System.out.println("Welcome to Inventory Management System!"); + System.out.println(); + + manager = new InventoryManager(); + loadSampleData(); + + try (Scanner scanner = new Scanner(System.in)) { + Main.scanner = scanner; + while (true) { + showMenu(); + int choice = getChoice(); + if (!handleChoice(choice)) { + break; + } + } + } catch (RuntimeException e) { + System.err.printf("Unexpected error occurred: %s", e.getMessage()); + + } finally { + scanner = null; + } + } + + /** + * Load sample data for testing + */ + private static void loadSampleData() { + manager.addProduct("B001", "Java Programming", "BOOK", 29.99, 10); + manager.addProduct("B002", "Design Patterns", "BOOK", 35.50, 8); + manager.addProduct("E001", "Laptop", "ELECTRONICS", 999.99, 5); + manager.addProduct("E002", "Mouse", "ELECTRONICS", 25.99, 15); + } + + /** + * Display main menu options + */ + private static void showMenu() { + System.out.println(); + System.out.println("Choices:"); + System.out.println("\t1. Add Product"); + System.out.println("\t2. View Inventory"); + System.out.println("\t3. Sell Product"); + System.out.println("\t4. Add Stock"); + System.out.println("\t5. View Statistics"); + System.out.println("\t6. Exit"); + System.out.println(); + } + + /** + * Get user choice with input validation + * @return user's menu choice + */ + private static int getChoice() { + return getIntInput("Choice: ", 1); + } + + /** + * Handle user menu choice + * @param choice - user's selected option + * @return should application continue + */ + private static boolean handleChoice(int choice) { + switch (choice) { + case 1: addProduct(); break; + case 2: manager.viewInventory(); break; + case 3: sellProduct(); break; + case 4: addStock(); break; + case 5: manager.viewStatistics(); break; + case 6: return false; + default: { + System.out.println("Invalid choice."); + return true; + } + } + return true; + } + + /** + * Handle adding new product + */ + private static void addProduct() { + String id = getStringInput("Product id: "); + String name = getStringInput("Product name: "); + + String type; + do { + type = getStringInput("Product type (BOOK,ELECTRONICS): ").toUpperCase(); + } while (!ProductFactory.isValidProductType(type)); + + int quantity = getIntInput("Product quantity: ", 1); + double price = getDoubleInput("Product price: ", 5.0); + + manager.addProduct(id, name, type, price, quantity); + } + + /** + * Handle selling product + */ + private static void sellProduct() { + String id = getStringInput("Product id: "); + int quantity = getIntInput("Product quantity: ", 1); + String discountType = getStringInput("Discount type (STUDENT,BULK,NONE)[NONE]: ").toUpperCase(); + + manager.sellProduct(id,quantity,discountType); + } + + /** + * Handle adding stock + */ + private static void addStock() { + String id = getStringInput("Product id: "); + int quantity = getIntInput("Product quantity: ", 1); + + manager.addStock(id, quantity); + } + + private static String getStringInput(String prompt) { + System.out.print(prompt); + return scanner.nextLine(); + } + + private static int getIntInput(String prompt, int min) { + int input = min - 1; + do { + try { + input = Integer.parseInt(getStringInput(prompt)); + } catch (NumberFormatException nfe) { + System.err.println("Invalid input."); + } + } while (input < min); + return input; + } + + private static double getDoubleInput(String prompt, double min) { + double input = min - 1.0; + do { + try { + input = Double.parseDouble(getStringInput(prompt)); + } catch (NumberFormatException nfe) { + System.err.println("Invalid input."); + } + } while (input < min); + return input; + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/main/java/inventory/Product.java b/InventoryManagementSystem/src/main/java/inventory/Product.java new file mode 100644 index 0000000..a57c9f2 --- /dev/null +++ b/InventoryManagementSystem/src/main/java/inventory/Product.java @@ -0,0 +1,123 @@ +package inventory; + +/** + * Product class representing items in the inventory. + * Requirements: + * - Private fields: id, name, type, price, quantity + * - Constructor with validation + * - Getter and setter methods + * - sell(), addStock(), isInStock() methods + * - toString() method + */ +public class Product { + + private final String id; + private final String name; + private final String type; + private double price; + private int quantity; + + /** + * @param id - unique product identifier + * @param name - product name + * @param type - product category (BOOK or ELECTRONICS) + * @param price - product price (must be non-negative) + * @param quantity - stock quantity (must be non-negative) + * @throws IllegalArgumentException if price or quantity is negative + */ + public Product(String id, String name, String type, double price, int quantity) { + if (price <= 0) { + throw new IllegalArgumentException("Price cannot be negative or zero"); + } + if (quantity <= 0) { + throw new IllegalArgumentException("Quantity cannot be negative or zero"); + } + if (id == null) { + throw new IllegalArgumentException("Id cannot be null"); + } + if (name == null) { + throw new IllegalArgumentException("Name cannot be null"); + } + if (type == null) { + throw new IllegalArgumentException("Type cannot be null"); + } + this.id = id; + this.name = name; + this.type = type; + this.price = price; + this.quantity = quantity; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public double getPrice() { + return price; + } + + public int getQuantity() { + return quantity; + } + + public void setPrice(double price) { + if (price <= 0) { + throw new IllegalArgumentException("Price cannot be negative or zero"); + } + this.price = price; + } + + public void setQuantity(int quantity) { + if (quantity <= 0) { + throw new IllegalArgumentException("Quantity cannot be negative or zero"); + } + this.quantity = quantity; + } + + /** + * @param amount - quantity to sell + * @return true if sale successful, false if insufficient stock + */ + public boolean sell(int amount) { + if (amount <= 0 || amount > quantity) { + return false; + } + + quantity -= amount; + return true; + } + + /** + * @param amount - quantity to add to stock + */ + public void addStock(int amount) { + if (amount <= 0) { + throw new IllegalArgumentException("Amount cannot be negative or zero"); + } + quantity += amount; + } + + /** + * @return true if product has stock available + */ + public boolean isInStock() { + return quantity > 0; + } + + /** + * @return formatted string representation of the product + */ + @Override + public String toString() { + return "Product{id='%s', name='%s', type='%s', price=%f, quantity=%d}" + .formatted(id, name, type, price, quantity); + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/main/java/inventory/ProductFactory.java b/InventoryManagementSystem/src/main/java/inventory/ProductFactory.java new file mode 100644 index 0000000..76735e5 --- /dev/null +++ b/InventoryManagementSystem/src/main/java/inventory/ProductFactory.java @@ -0,0 +1,56 @@ +package inventory; + +/** + * Factory class for creating Product objects with business rules. + * + * Requirements: + * - Constants for product types + * - Business rules: Books minimum $5, Electronics minimum $10 + * - Static factory method for product creation + * - Exception handling for invalid inputs + */ +public class ProductFactory { + + public static final String BOOK_TYPE = "BOOK"; + public static final String ELECTRONICS_TYPE = "ELECTRONICS"; + public static final String FURNITURE_TYPE = "FURNITURE"; + + /** + * Factory method to create products with business rules + * @param id - product identifier + * @param name - product name + * @param type - product type (must be BOOK or ELECTRONICS) + * @param price - product price (must meet minimum requirements) + * @param quantity - initial stock quantity + * @return new Product instance + * @throws IllegalArgumentException if validation fails + */ + public static Product createProduct(String id, String name, String type, double price, int quantity) { + + if (!isValidProductType(type)) { + throw new IllegalArgumentException("Product type '%s' not available.".formatted(type)); + } + + validateBusinessRules(type, price); + + return new Product(id, name, type, price, quantity); + } + + public static boolean isValidProductType(String type) { + return BOOK_TYPE.equals(type) || ELECTRONICS_TYPE.equals(type) || FURNITURE_TYPE.equals(type); + } + + private static void validateBusinessRules(String type, double price) { + if (BOOK_TYPE.equals(type) && price < 5.0) { + throw new IllegalArgumentException("Book price must be at least 5"); + } + + if (ELECTRONICS_TYPE.equals(type) && price < 10.0) { + throw new IllegalArgumentException("Electronics price must be at least 10"); + } + + if (FURNITURE_TYPE.equals(type) && price < 50.0) { + throw new IllegalArgumentException("Furniture price must be at least 50"); + } + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/test/java/inventory/DiscountCalculatorTest.java b/InventoryManagementSystem/src/test/java/inventory/DiscountCalculatorTest.java new file mode 100644 index 0000000..3ade075 --- /dev/null +++ b/InventoryManagementSystem/src/test/java/inventory/DiscountCalculatorTest.java @@ -0,0 +1,142 @@ +package inventory; + +import inventory.DiscountCalculator.DiscountResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Unit tests for DiscountCalculator class. + */ +class DiscountCalculatorTest { + + private Product book; + private Product electronics; + + /** + * Set up test data before each test + */ + @BeforeEach + void setUp() { + book = ProductFactory.createProduct("B001", "Test Book", "BOOK", 20.0, 10); + electronics = ProductFactory.createProduct("E001", "Test Electronics", "ELECTRONICS", 100.0, 10); + } + + /** + * Test student discount on books + */ + @Test + void testStudentDiscountOnBooks() { + DiscountResult discount = DiscountCalculator.calculateDiscount(book, 1, "STUDENT"); + assertNotNull(discount); + assertEquals(1 * book.getPrice() * 0.10, discount.getDiscountAmount()); + assertEquals("Student discount: 10% off books", discount.getDescription()); + } + + /** + * Test student discount on electronics (should not apply) + */ + @Test + void testStudentDiscountOnElectronics() { + DiscountResult discount = DiscountCalculator.calculateDiscount(electronics, 1, "STUDENT"); + assertNotNull(discount); + assertEquals(0, discount.getDiscountAmount()); + assertEquals("Student discount only applies to books", discount.getDescription()); + } + + /** + * test bulk discount when quantity >= 5 + */ + @ParameterizedTest(name = "quantity = {arguments}") + @ValueSource(ints = {5,10}) + void testBulkDiscountValid(int quantity) { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, quantity, "BULK"); + assertNotNull(discountBook); + assertEquals(quantity * book.getPrice() * 0.15, discountBook.getDiscountAmount()); + assertEquals("Bulk discount: 15% off for 5+ items", discountBook.getDescription()); + + DiscountResult discountElectronics = DiscountCalculator.calculateDiscount(electronics, quantity, "BULK"); + assertNotNull(discountElectronics); + assertEquals(quantity * electronics.getPrice() * 0.15, discountElectronics.getDiscountAmount()); + assertEquals("Bulk discount: 15% off for 5+ items", discountElectronics.getDescription()); + } + + /** + * Test bulk discount when quantity < 5 + */ + @Test + void testBulkDiscountInvalid() { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, 2, "BULK"); + assertNotNull(discountBook); + assertEquals(0, discountBook.getDiscountAmount()); + assertEquals("Bulk discount requires 5+ items", discountBook.getDescription()); + } + + /** + * Test no discount option + */ + @Test + void testNoDiscount() { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, 1, "NONE"); + assertNotNull(discountBook); + assertEquals(0, discountBook.getDiscountAmount()); + assertEquals("No discount applied", discountBook.getDescription()); + } + + /** + * Test boundary conditions + */ + @ParameterizedTest(name = "quantity = {0}, discount(%) = {1}") + @CsvSource({"1,0", "5,0.15", "100,0.15", "1000,0.15", "10000,0.15"}) + void testBoundaryConditions(int quantity, double discountPercentage) { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, quantity, "BULK"); + assertNotNull(discountBook); + assertEquals(book.getPrice() * quantity * discountPercentage, discountBook.getDiscountAmount()); + } + + /** + * Test discount calculation accuracy + */ + @Test + void testDiscountCalculationAccuracy() { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, 2, "STUDENT"); + assertNotNull(discountBook); + assertEquals(4, discountBook.getDiscountAmount()); + + DiscountResult discountElectronics = DiscountCalculator.calculateDiscount(electronics, 5, "BULK"); + assertNotNull(discountElectronics); + assertEquals(75, discountElectronics.getDiscountAmount()); + } + + @ParameterizedTest(name = "type = {0}") + @ValueSource(strings = {"STUDENT", "student", "STUdent", "stuDent", "sTudenT"}) + void testCaseSensitivityOfDiscountTypes(String type) { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, 1, type); + assertNotNull(discountBook); + assertEquals(2, discountBook.getDiscountAmount()); + } + + @ParameterizedTest(name = "type = {0}") + @ValueSource(strings = {"", "--", "\n", "09999", "STUDENT1", "BULKSTUDENT"}) + void testInvalidDiscountTypes(String type) { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, 1, type); + assertNotNull(discountBook); + assertEquals(0, discountBook.getDiscountAmount()); + } + + @Test + void testDiscountZeroQuantity() { + DiscountResult discountBook = DiscountCalculator.calculateDiscount(book, 0, "STUDENT"); + assertNotNull(discountBook); + assertEquals(0, discountBook.getDiscountAmount()); + + DiscountResult discountElectronics = DiscountCalculator.calculateDiscount(electronics, 0, "BULK"); + assertNotNull(discountElectronics); + assertEquals(0, discountElectronics.getDiscountAmount()); + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/test/java/inventory/InventoryManagerTest.java b/InventoryManagementSystem/src/test/java/inventory/InventoryManagerTest.java new file mode 100644 index 0000000..c84c119 --- /dev/null +++ b/InventoryManagementSystem/src/test/java/inventory/InventoryManagerTest.java @@ -0,0 +1,142 @@ +package inventory; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import java.util.List; + +/** + * Unit tests for InventoryManager class. + */ +class InventoryManagerTest { + + private InventoryManager manager; + + /** + * Set up fresh instance before each test + */ + @BeforeEach + void setUp() { + manager = new InventoryManager(); + } + + /** + * Test adding products to inventory + */ + @Test + void testAddProduct() { + boolean succeeded = manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 1); + assertTrue(succeeded); + assertTrue(manager.productExists("TEST1")); + assertEquals(10.00, manager.getInventoryValue()); + } + + /** + * Test adding product with invalid parameters + */ + @Test + void testAddInvalidProduct() { + boolean succeeded = manager.addProduct("TEST1", "Test Book", "BOOK", 0, 1); + assertFalse(succeeded); + assertEquals(0, manager.getInventoryValue()); + } + + /** + * Test selling products + */ + @Test + void testSellProduct() { + manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 10); + assertEquals(100.00, manager.getInventoryValue()); + + manager.sellProduct("TEST1", 2, "NONE"); + assertEquals(80.00, manager.getInventoryValue()); + + manager.sellProduct("TEST1", 6, "BULK"); + assertEquals(20.00, manager.getInventoryValue()); + + manager.sellProduct("TEST1", 2, "STUDENT"); + assertEquals(0, manager.getInventoryValue()); + } + + /** + * Test selling more than available stock + */ + @Test + void testSellInsufficientStock() { + manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 10); + assertEquals(100.00, manager.getInventoryValue()); + + manager.sellProduct("TEST1", 12, "NONE"); + assertEquals(100.00, manager.getInventoryValue()); + } + + /** + * Test adding stock to existing product + */ + @Test + void testAddStock() { + manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 1); + assertEquals(10.00, manager.getInventoryValue()); + + manager.addStock("TEST1", 1); + assertEquals(20.00, manager.getInventoryValue()); + } + + /** + * Test inventory value calculation + */ + @Test + void testInventoryValue() { + manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 2); + manager.addProduct("TEST2", "Test Book", "BOOK", 50.0, 1); + assertEquals(2 * 10.00 + 1 * 50.00, manager.getInventoryValue()); + } + + /** + * Test low stock detection + */ + @Test + void testLowStockProducts() { + manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 1); + manager.addProduct("TEST2", "Test Book", "BOOK", 10.0, 2); + manager.addProduct("TEST5", "Test Book", "BOOK", 10.0, 5); + manager.addProduct("TEST10", "Test Book", "BOOK", 10.0, 10); + + List lowStockUnder5 = manager.getLowStockProducts(5); + assertEquals(2, lowStockUnder5.size()); + assertTrue(lowStockUnder5.stream().anyMatch(p -> p.getId().equals("TEST1"))); + assertTrue(lowStockUnder5.stream().anyMatch(p -> p.getId().equals("TEST2"))); + } + + /** + * Test operations on non-existent products + */ + @Test + void testNonExistentProduct() { + boolean sellSucceeded = manager.sellProduct("NONE", 1, "BOOK"); + assertFalse(sellSucceeded); + + boolean addStockSucceeded = manager.addStock("NONE", 1); + assertFalse(addStockSucceeded); + } + + /** + * Test complete workflow + */ + @Test + void testCompleteWorkflow() { + + boolean addSucceeded = manager.addProduct("TEST1", "Test Book", "BOOK", 10.0, 10); + assertTrue(addSucceeded); + assertEquals(100.00, manager.getInventoryValue()); + + boolean addStockSucceeded = manager.addStock("TEST1", 10); + assertTrue(addStockSucceeded); + assertEquals(200.00, manager.getInventoryValue()); + + boolean sellSucceeded = manager.sellProduct("TEST1", 18, "NONE"); + assertTrue(sellSucceeded); + assertEquals(20.00, manager.getInventoryValue()); + } +} \ No newline at end of file diff --git a/InventoryManagementSystem/src/test/java/inventory/ProductFactoryTest.java b/InventoryManagementSystem/src/test/java/inventory/ProductFactoryTest.java new file mode 100644 index 0000000..e5af3ee --- /dev/null +++ b/InventoryManagementSystem/src/test/java/inventory/ProductFactoryTest.java @@ -0,0 +1,97 @@ +package inventory; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for ProductFactory class. + */ +class ProductFactoryTest { + + /** + * Test creating a valid book product + */ + @Test + void testCreateValidBook() { + Product book = ProductFactory.createProduct("B001", "Java Book", "BOOK", 15.99, 5); + assertNotNull(book); + assertEquals("B001", book.getId()); + assertEquals("BOOK", book.getType()); + assertEquals(15.99, book.getPrice()); + } + + /** + * Test creating a valid electronics product + */ + @Test + void testCreateValidElectronics() { + Product electronics = ProductFactory.createProduct("E001", "Java Book", "ELECTRONICS", 15.99, 5); + assertNotNull(electronics); + assertEquals("E001", electronics.getId()); + assertEquals("ELECTRONICS", electronics.getType()); + assertEquals(15.99, electronics.getPrice()); + } + + /** + * Test book minimum price validation + */ + @Test + void testBookMinimumPriceValidation() { + assertThrows(IllegalArgumentException.class, () -> { + ProductFactory.createProduct("B001", "Cheap Book", "BOOK", 3.99, 5); + }); + } + + /** + * Test electronics minimum price validation + */ + @Test + void testElectronicsMinimumPriceValidation() { + assertThrows(IllegalArgumentException.class, () -> { + ProductFactory.createProduct("E001", "Cheap Electronics", "ELECTRONICS", 5.99, 5); + }); + } + + /** + * Test invalid product type + */ + @Test + void testInvalidProductType() { + assertThrows(IllegalArgumentException.class, () -> { + ProductFactory.createProduct("S001", "Sheep", "SHEEP", 1.99, 5); + }); + } + + /** + * Test boundary conditions + */ + @Test + void testBoundaryConditions() { + Product book = ProductFactory.createProduct("B001", "Java Book", "BOOK", 5.00, 5); + assertNotNull(book); + Product electronics = ProductFactory.createProduct("B001", "Java Book", "ELECTRONICS", 10.00, 5); + assertNotNull(electronics); + } + + @Test + void testNullValues() { + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct(null, "Cheap Book", "BOOK", 1.99, 5)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", null, "BOOK", 3.99, 5)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "Cheap Book", null, 3.99, 5)); + } + + @Test + void testEmptyValues() { + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("", "Cheap Book", "BOOK", 1.99, 5)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "", "BOOK", 3.99, 5)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "Cheap Book", "", 3.99, 5)); + } + + @Test + void testNegativeAndZeroValues() { + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "Cheap Book", "BOOK", 0, 5)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "Cheap Book", "BOOK", 3.99, 0)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "Cheap Book", "BOOK", -1, 5)); + assertThrows(IllegalArgumentException.class, () -> ProductFactory.createProduct("B001", "Cheap Book", "BOOK", 3.99, -1)); + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f76f1a4 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +junitVersion=5.9.3 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 312b3de..3cf24c8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,5 @@ include 'PetCareScheduler' include 'Portfolio' include 'OnlineQuiz' include 'RetailManagementSystem:back-end' -include 'RetailManagementSystem:front-end' \ No newline at end of file +include 'RetailManagementSystem:front-end' +include 'InventoryManagementSystem' \ No newline at end of file