Add InventoryManagementSystem app

This commit is contained in:
2025-11-03 17:32:20 +01:00
parent 8577d69eb1
commit f49be0c3d3
12 changed files with 1028 additions and 1 deletions

View File

@@ -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"]

View File

@@ -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()
}

View File

@@ -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;
}
}

View File

@@ -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<String, Product> 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<Product> 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<Product> 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}
}

View File

@@ -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());
}
}

View File

@@ -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<Product> 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());
}
}

View File

@@ -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));
}
}

1
gradle.properties Normal file
View File

@@ -0,0 +1 @@
junitVersion=5.9.3

View File

@@ -4,3 +4,4 @@ include 'Portfolio'
include 'OnlineQuiz' include 'OnlineQuiz'
include 'RetailManagementSystem:back-end' include 'RetailManagementSystem:back-end'
include 'RetailManagementSystem:front-end' include 'RetailManagementSystem:front-end'
include 'InventoryManagementSystem'