commit 3c468a1b59399b940254c76fa81a5cb79ed7c93e Author: Phuriphat Yuanyee Date: Mon May 18 16:58:51 2026 +0200 init diff --git a/source/.gitignore b/source/.gitignore new file mode 100644 index 0000000..2e2966e --- /dev/null +++ b/source/.gitignore @@ -0,0 +1,17 @@ +# Maven build output +target/ + +# Generated log files +*.log + +# IDE files +.idea/ +*.iml +.vscode/ +*.classpath +*.project +.settings/ + +# OS files +.DS_Store +Thumbs.db diff --git a/source/pom.xml b/source/pom.xml new file mode 100644 index 0000000..07d721a --- /dev/null +++ b/source/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + se.kth.iv1350 + repairbike + 1.0-SNAPSHOT + jar + + + 11 + 11 + UTF-8 + + + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + + + src/main + src/test + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + startup.Main + + + + + + + diff --git a/source/src/main/controller/Controller.java b/source/src/main/controller/Controller.java new file mode 100644 index 0000000..bb2509f --- /dev/null +++ b/source/src/main/controller/Controller.java @@ -0,0 +1,88 @@ +package controller; + +import integration.BikeRegistry; +import integration.RepairTaskCatalog; +import model.Amount; +import model.BikeDTO; +import model.RepairOrder; +import model.RepairShop; +import model.TaskDTO; + +/** + * Coordinates use-case execution between the view and the model and integration layers. + * All calls from the view pass through this class. + */ +public class Controller { + private final RepairShop repairShop; + private final BikeRegistry bikeRegistry; + private final RepairTaskCatalog taskCatalog; + + /** + * Creates a Controller wired to the given dependencies. + * + * @param repairShop The repair shop facade (model layer). + * @param bikeRegistry The bike registry (integration layer). + * @param taskCatalog The repair task catalog (integration layer). + */ + public Controller(RepairShop repairShop, BikeRegistry bikeRegistry, + RepairTaskCatalog taskCatalog) { + this.repairShop = repairShop; + this.bikeRegistry = bikeRegistry; + this.taskCatalog = taskCatalog; + } + + /** + * Starts a new repair session. + */ + public void startNewRepair() { + repairShop.startRepair(); + } + + /** + * Registers the bike with the given ID for the current repair session. + * + * @param bikeID The unique identifier of the bike to register. + * @return The {@link BikeDTO} of the registered bike, or {@code null} if the + * bike ID does not exist in the registry. + */ + public BikeDTO enterBikeID(String bikeID) { + BikeDTO bike = bikeRegistry.findBike(bikeID); + if (bike != null) { + repairShop.registerBike(bike); + } + return bike; + } + + /** + * Adds the repair task with the given name to the current repair session. + * + * @param taskName The name of the repair task to add. + * @return The updated running total as an {@link Amount}, or {@code null} if the + * task name does not exist in the catalog. + */ + public Amount addRepairTask(String taskName) { + TaskDTO task = taskCatalog.findTask(taskName); + if (task != null) { + return repairShop.addTask(task); + } + return null; + } + + /** + * Records the mechanic's diagnostic notes for the current repair session. + * + * @param report The diagnostic report text entered by the mechanic. + */ + public void enterDiagnosticReport(String report) { + repairShop.enterDiagnosticReport(report); + } + + /** + * Ends the current repair session and returns the completed repair order. + * + * @return The {@link RepairOrder} summarising all repair details. + */ + public RepairOrder endRepair() { + return repairShop.endRepair(); + } +} diff --git a/source/src/main/integration/BikeRegistry.java b/source/src/main/integration/BikeRegistry.java new file mode 100644 index 0000000..3eb49da --- /dev/null +++ b/source/src/main/integration/BikeRegistry.java @@ -0,0 +1,34 @@ +package integration; + +import model.BikeDTO; + +import java.util.HashMap; +import java.util.Map; + +/** + * Handles retrieval of bike information from persistent storage. + * In this implementation, bikes are stored in memory as sample data. + */ +public class BikeRegistry { + private final Map bikes = new HashMap<>(); + + /** + * Creates a BikeRegistry pre-loaded with sample bike records. + */ + public BikeRegistry() { + bikes.put("BIKE-001", new BikeDTO("BIKE-001", "Alice Svensson")); + bikes.put("BIKE-002", new BikeDTO("BIKE-002", "Bob Lindqvist")); + bikes.put("BIKE-003", new BikeDTO("BIKE-003", "Carl Johansson")); + } + + /** + * Looks up and returns the bike with the given ID. + * + * @param bikeID The unique identifier of the bike to look up. + * @return The {@link BikeDTO} for the found bike, or {@code null} if no bike + * with the given ID exists in the registry. + */ + public BikeDTO findBike(String bikeID) { + return bikes.get(bikeID); + } +} diff --git a/source/src/main/integration/RepairTaskCatalog.java b/source/src/main/integration/RepairTaskCatalog.java new file mode 100644 index 0000000..ab59d67 --- /dev/null +++ b/source/src/main/integration/RepairTaskCatalog.java @@ -0,0 +1,40 @@ +package integration; + +import model.Amount; +import model.TaskDTO; + +import java.util.HashMap; +import java.util.Map; + +/** + * Handles retrieval of repair task information from persistent storage. + * In this implementation, tasks are stored in memory as sample data. + */ +public class RepairTaskCatalog { + private final Map tasks = new HashMap<>(); + + /** + * Creates a RepairTaskCatalog pre-loaded with sample repair task records. + */ + public RepairTaskCatalog() { + tasks.put("Brake Pad Replacement", + new TaskDTO("Brake Pad Replacement", new Amount(350.00))); + tasks.put("Tire Replacement", + new TaskDTO("Tire Replacement", new Amount(500.00))); + tasks.put("Battery Check", + new TaskDTO("Battery Check", new Amount(200.00))); + tasks.put("Chain Lubrication", + new TaskDTO("Chain Lubrication", new Amount(150.00))); + } + + /** + * Looks up and returns the repair task with the given name. + * + * @param taskName The name of the repair task to look up. + * @return The {@link TaskDTO} for the found task, or {@code null} if no task + * with the given name exists in the catalog. + */ + public TaskDTO findTask(String taskName) { + return tasks.get(taskName); + } +} diff --git a/source/src/main/model/ActiveRepair.java b/source/src/main/model/ActiveRepair.java new file mode 100644 index 0000000..ed3568b --- /dev/null +++ b/source/src/main/model/ActiveRepair.java @@ -0,0 +1,64 @@ +package model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents one ongoing repair session for a single bike. + * Only accessible within the model package via {@link RepairShop}. + */ +class ActiveRepair { + private BikeDTO bike; + private final List tasks = new ArrayList<>(); + private String diagnosticReport = ""; + private Amount total = new Amount(0); + + /** + * Registers the bike that is being repaired in this session. + * + * @param bike The bike to register. + */ + void registerBike(BikeDTO bike) { + this.bike = bike; + } + + /** + * Adds a repair task to this session and returns the updated running total. + * + * @param task The repair task to add. + * @return The new running total after adding the task. + */ + Amount addTask(TaskDTO task) { + tasks.add(task); + total = calculateTotal(); + return total; + } + + /** + * Records the mechanic's diagnostic notes for this repair session. + * + * @param report The diagnostic report text entered by the mechanic. + */ + void enterDiagnosticReport(String report) { + this.diagnosticReport = report; + } + + /** + * Ends the repair session and returns a completed repair order. + * + * @return The {@link RepairOrder} summarising all repair details. + */ + RepairOrder endRepair() { + List immutableTasks = Collections.unmodifiableList(new ArrayList<>(tasks)); + return new RepairOrder(bike, immutableTasks, diagnosticReport, total); + } + + private Amount calculateTotal() { + Amount sum = new Amount(0); + for (TaskDTO task : tasks) { + sum = sum.add(task.getCost()); + } + return sum; + } +} diff --git a/source/src/main/model/Amount.java b/source/src/main/model/Amount.java new file mode 100644 index 0000000..dc98b4f --- /dev/null +++ b/source/src/main/model/Amount.java @@ -0,0 +1,56 @@ +package model; + +/** + * Represents a monetary amount. Immutable; all arithmetic returns new instances. + */ +public class Amount { + private final double value; + + /** + * Creates an Amount with the specified value. + * + * @param value The monetary value. + */ + public Amount(double value) { + this.value = value; + } + + /** + * Returns a new Amount that is the sum of this amount and the given amount. + * + * @param other The amount to add. + * @return A new Amount representing the sum. + */ + public Amount add(Amount other) { + return new Amount(this.value + other.value); + } + + /** + * Returns a new Amount that is the difference of this amount minus the given amount. + * + * @param other The amount to subtract. + * @return A new Amount representing the difference. + */ + public Amount subtract(Amount other) { + return new Amount(this.value - other.value); + } + + /** + * Returns the raw numeric value of this amount. + * + * @return The monetary value as a double. + */ + public double getValue() { + return value; + } + + /** + * Returns a formatted string representation of this amount. + * + * @return A string in the form {@code "123.45 SEK"}. + */ + @Override + public String toString() { + return String.format("%.2f SEK", value); + } +} diff --git a/source/src/main/model/BikeDTO.java b/source/src/main/model/BikeDTO.java new file mode 100644 index 0000000..d5dcafc --- /dev/null +++ b/source/src/main/model/BikeDTO.java @@ -0,0 +1,39 @@ +package model; + +/** + * Data Transfer Object that carries bike information across layer boundaries. + * Instances are immutable. + */ +public class BikeDTO { + private final String bikeID; + private final String ownerName; + + /** + * Creates a BikeDTO with the specified bike ID and owner name. + * + * @param bikeID The unique identifier of the bike. + * @param ownerName The full name of the bike's owner. + */ + public BikeDTO(String bikeID, String ownerName) { + this.bikeID = bikeID; + this.ownerName = ownerName; + } + + /** + * Returns the bike's unique identifier. + * + * @return The bike ID string. + */ + public String getBikeID() { + return bikeID; + } + + /** + * Returns the full name of the bike's owner. + * + * @return The owner's name. + */ + public String getOwnerName() { + return ownerName; + } +} diff --git a/source/src/main/model/RepairOrder.java b/source/src/main/model/RepairOrder.java new file mode 100644 index 0000000..88d2e06 --- /dev/null +++ b/source/src/main/model/RepairOrder.java @@ -0,0 +1,65 @@ +package model; + +import java.util.List; + +/** + * Represents a completed repair order issued at the end of a repair session. + * Instances are immutable. + */ +public class RepairOrder { + private final BikeDTO bike; + private final List tasks; + private final String diagnosticReport; + private final Amount total; + + /** + * Creates a RepairOrder with all details of the completed repair. + * + * @param bike The bike that was repaired. + * @param tasks The list of repair tasks performed. + * @param diagnosticReport The mechanic's diagnostic notes. + * @param total The total cost of all repair tasks. + */ + RepairOrder(BikeDTO bike, List tasks, String diagnosticReport, Amount total) { + this.bike = bike; + this.tasks = tasks; + this.diagnosticReport = diagnosticReport; + this.total = total; + } + + /** + * Returns the bike information for this repair order. + * + * @return The {@link BikeDTO} of the repaired bike. + */ + public BikeDTO getBike() { + return bike; + } + + /** + * Returns the list of repair tasks included in this order. + * + * @return An unmodifiable list of {@link TaskDTO} objects. + */ + public List getTasks() { + return tasks; + } + + /** + * Returns the mechanic's diagnostic report for this repair. + * + * @return The diagnostic report string. + */ + public String getDiagnosticReport() { + return diagnosticReport; + } + + /** + * Returns the total cost of all repair tasks in this order. + * + * @return The total as an {@link Amount}. + */ + public Amount getTotal() { + return total; + } +} diff --git a/source/src/main/model/RepairShop.java b/source/src/main/model/RepairShop.java new file mode 100644 index 0000000..5d7cc9d --- /dev/null +++ b/source/src/main/model/RepairShop.java @@ -0,0 +1,60 @@ +package model; + +/** + * Facade for the model layer. Manages the lifecycle of one repair session at a time + * and delegates all operations to the current {@link ActiveRepair}. + */ +public class RepairShop { + + private ActiveRepair currentRepair; + + /** + * Creates a new RepairShop ready to accept repair sessions. + */ + public RepairShop() { + } + + /** + * Starts a new repair session, discarding any previously active session. + */ + public void startRepair() { + currentRepair = new ActiveRepair(); + } + + /** + * Registers the bike that will be worked on during the current repair session. + * + * @param bike The {@link BikeDTO} of the bike to register. + */ + public void registerBike(BikeDTO bike) { + currentRepair.registerBike(bike); + } + + /** + * Adds a repair task to the current session and returns the updated running total. + * + * @param task The {@link TaskDTO} of the task to add. + * @return The running total after the task has been added. + */ + public Amount addTask(TaskDTO task) { + return currentRepair.addTask(task); + } + + /** + * Records the mechanic's diagnostic notes for the current repair session. + * + * @param report The diagnostic report text entered by the mechanic. + */ + public void enterDiagnosticReport(String report) { + currentRepair.enterDiagnosticReport(report); + } + + /** + * Ends the current repair session and returns the completed repair order. + * + * @return The {@link RepairOrder} for the finished repair. + */ + public RepairOrder endRepair() { + return currentRepair.endRepair(); + } +} diff --git a/source/src/main/model/TaskDTO.java b/source/src/main/model/TaskDTO.java new file mode 100644 index 0000000..e44e457 --- /dev/null +++ b/source/src/main/model/TaskDTO.java @@ -0,0 +1,39 @@ +package model; + +/** + * Data Transfer Object that carries repair task information across layer boundaries. + * Instances are immutable. + */ +public class TaskDTO { + private final String name; + private final Amount cost; + + /** + * Creates a TaskDTO with the specified task name and cost. + * + * @param name The name of the repair task. + * @param cost The cost of the repair task. + */ + public TaskDTO(String name, Amount cost) { + this.name = name; + this.cost = cost; + } + + /** + * Returns the repair task name. + * + * @return The task name string. + */ + public String getName() { + return name; + } + + /** + * Returns the cost of this repair task. + * + * @return The cost as an {@link Amount}. + */ + public Amount getCost() { + return cost; + } +} diff --git a/source/src/main/startup/Main.java b/source/src/main/startup/Main.java new file mode 100644 index 0000000..c0872a1 --- /dev/null +++ b/source/src/main/startup/Main.java @@ -0,0 +1,28 @@ +package startup; + +import controller.Controller; +import integration.BikeRegistry; +import integration.RepairTaskCatalog; +import model.RepairShop; +import view.View; + +/** + * Contains the application entry point. Responsible for creating and wiring + * all top-level objects in dependency order. + */ +public class Main { + + /** + * Starts the Repair Electric Bike application. + * + * @param args Command-line arguments (not used). + */ + public static void main(String[] args) { + BikeRegistry bikeRegistry = new BikeRegistry(); + RepairTaskCatalog taskCatalog = new RepairTaskCatalog(); + RepairShop repairShop = new RepairShop(); + Controller controller = new Controller(repairShop, bikeRegistry, taskCatalog); + View view = new View(controller); + view.runFakeExecution(); + } +} diff --git a/source/src/main/view/View.java b/source/src/main/view/View.java new file mode 100644 index 0000000..6adb284 --- /dev/null +++ b/source/src/main/view/View.java @@ -0,0 +1,83 @@ +package view; + +import controller.Controller; +import model.Amount; +import model.BikeDTO; +import model.RepairOrder; +import model.TaskDTO; + +/** + * Placeholder view that simulates mechanic interactions using hard-coded method calls. + * Everything returned by the controller is printed to standard output. + */ +public class View { + private final Controller controller; + + /** + * Creates a View connected to the given controller. + * + * @param controller The controller to use for all system operations. + */ + public View(Controller controller) { + this.controller = controller; + } + + /** + * Simulates a complete repair session using hard-coded inputs, printing all + * output returned by the controller to standard output. + */ + public void runFakeExecution() { + System.out.println("=== Repair Electric Bike System ==="); + System.out.println(); + + controller.startNewRepair(); + System.out.println("[Mechanic] Started new repair session."); + + BikeDTO bike = controller.enterBikeID("BIKE-001"); + if (bike != null) { + System.out.println("[System] Bike registered: ID=" + bike.getBikeID() + + ", Owner=" + bike.getOwnerName()); + } else { + System.out.println("[System] Bike not found."); + } + + String[] taskNames = {"Brake Pad Replacement", "Tire Replacement"}; + for (String taskName : taskNames) { + Amount runningTotal = controller.addRepairTask(taskName); + if (runningTotal != null) { + System.out.println("[System] Added task: \"" + taskName + + "\" | Running total: " + runningTotal); + } else { + System.out.println("[System] Task not found: \"" + taskName + "\""); + } + } + + String diagnosticNotes = "Front brake pads fully worn. Front tire flat (puncture). " + + "Both parts replaced. Bike tested and approved."; + controller.enterDiagnosticReport(diagnosticNotes); + System.out.println("[Mechanic] Diagnostic report entered."); + + RepairOrder repairOrder = controller.endRepair(); + printRepairOrder(repairOrder); + } + + private void printRepairOrder(RepairOrder repairOrder) { + System.out.println(); + System.out.println("========================================"); + System.out.println(" REPAIR ORDER "); + System.out.println("========================================"); + System.out.println("Bike ID : " + repairOrder.getBike().getBikeID()); + System.out.println("Owner : " + repairOrder.getBike().getOwnerName()); + System.out.println("----------------------------------------"); + System.out.println("Repair Tasks:"); + for (TaskDTO task : repairOrder.getTasks()) { + System.out.printf(" %-28s %s%n", task.getName(), task.getCost()); + } + System.out.println("----------------------------------------"); + System.out.println("Total : " + repairOrder.getTotal()); + System.out.println("----------------------------------------"); + System.out.println("Diagnostic Report:"); + System.out.println(" " + repairOrder.getDiagnosticReport()); + System.out.println("========================================"); + } +} diff --git a/source/src/test/controller/ControllerTest.java b/source/src/test/controller/ControllerTest.java new file mode 100644 index 0000000..eaa3192 --- /dev/null +++ b/source/src/test/controller/ControllerTest.java @@ -0,0 +1,105 @@ +package controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import integration.BikeRegistry; +import integration.RepairTaskCatalog; +import model.Amount; +import model.BikeDTO; +import model.RepairOrder; +import model.RepairShop; + +import static org.junit.jupiter.api.Assertions.*; + +class ControllerTest { + + private Controller controller; + + @BeforeEach + void setUp() { + BikeRegistry bikeRegistry = new BikeRegistry(); + RepairTaskCatalog taskCatalog = new RepairTaskCatalog(); + RepairShop repairShop = new RepairShop(); + controller = new Controller(repairShop, bikeRegistry, taskCatalog); + controller.startNewRepair(); + } + + @Test + void enterBikeIdWithValidIdReturnsNonNullBikeDTO() { + BikeDTO bike = controller.enterBikeID("BIKE-001"); + assertNotNull(bike); + } + + @Test + void enterBikeIdWithValidIdReturnsCorrectBikeId() { + BikeDTO bike = controller.enterBikeID("BIKE-001"); + assertEquals("BIKE-001", bike.getBikeID()); + } + + @Test + void enterBikeIdWithUnknownIdReturnsNull() { + BikeDTO bike = controller.enterBikeID("BIKE-999"); + assertNull(bike); + } + + @Test + void addRepairTaskWithValidNameReturnsNonNullAmount() { + controller.enterBikeID("BIKE-001"); + Amount total = controller.addRepairTask("Brake Pad Replacement"); + assertNotNull(total); + } + + @Test + void addRepairTaskWithValidNameReturnsCorrectRunningTotal() { + controller.enterBikeID("BIKE-001"); + Amount total = controller.addRepairTask("Brake Pad Replacement"); + assertEquals(350, total.getValue(), 0.001); + } + + @Test + void addTwoRepairTasksReturnsCumulativeTotal() { + controller.enterBikeID("BIKE-001"); + controller.addRepairTask("Brake Pad Replacement"); + Amount total = controller.addRepairTask("Tire Replacement"); + assertEquals(850, total.getValue(), 0.001); + } + + @Test + void addRepairTaskWithUnknownNameReturnsNull() { + controller.enterBikeID("BIKE-001"); + Amount total = controller.addRepairTask("Non-existing Task"); + assertNull(total); + } + + @Test + void endRepairReturnsNonNullRepairOrder() { + controller.enterBikeID("BIKE-001"); + controller.addRepairTask("Battery Check"); + RepairOrder repairOrder = controller.endRepair(); + assertNotNull(repairOrder); + } + + @Test + void endRepairReturnsRepairOrderWithCorrectBikeId() { + controller.enterBikeID("BIKE-002"); + RepairOrder repairOrder = controller.endRepair(); + assertEquals("BIKE-002", repairOrder.getBike().getBikeID()); + } + + @Test + void endRepairReturnsRepairOrderWithCorrectTotalAfterTasks() { + controller.enterBikeID("BIKE-001"); + controller.addRepairTask("Battery Check"); + controller.addRepairTask("Chain Lubrication"); + RepairOrder repairOrder = controller.endRepair(); + assertEquals(350, repairOrder.getTotal().getValue(), 0.001); + } + + @Test + void enterDiagnosticReportIsReflectedInFinalRepairOrder() { + controller.enterBikeID("BIKE-003"); + controller.enterDiagnosticReport("Chain replaced and tested."); + RepairOrder repairOrder = controller.endRepair(); + assertEquals("Chain replaced and tested.", repairOrder.getDiagnosticReport()); + } +} diff --git a/source/src/test/integration/BikeRegistryTest.java b/source/src/test/integration/BikeRegistryTest.java new file mode 100644 index 0000000..8f1828a --- /dev/null +++ b/source/src/test/integration/BikeRegistryTest.java @@ -0,0 +1,54 @@ +package integration; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import model.BikeDTO; + +import static org.junit.jupiter.api.Assertions.*; + +class BikeRegistryTest { + + private BikeRegistry bikeRegistry; + + @BeforeEach + void setUp() { + bikeRegistry = new BikeRegistry(); + } + + @Test + void findBikeWithExistingIdReturnsBikeDTO() { + BikeDTO bike = bikeRegistry.findBike("BIKE-001"); + assertNotNull(bike); + } + + @Test + void findBikeWithExistingIdReturnsCorrectBikeId() { + BikeDTO bike = bikeRegistry.findBike("BIKE-001"); + assertEquals("BIKE-001", bike.getBikeID()); + } + + @Test + void findBikeWithExistingIdReturnsCorrectOwnerName() { + BikeDTO bike = bikeRegistry.findBike("BIKE-002"); + assertEquals("Bob Lindqvist", bike.getOwnerName()); + } + + @Test + void findBikeWithNonExistingIdReturnsNull() { + BikeDTO bike = bikeRegistry.findBike("BIKE-999"); + assertNull(bike); + } + + @Test + void findBikeWithEmptyStringReturnsNull() { + BikeDTO bike = bikeRegistry.findBike(""); + assertNull(bike); + } + + @Test + void findBikeWithAllThreeSampleBikesReturnsNonNull() { + assertNotNull(bikeRegistry.findBike("BIKE-001")); + assertNotNull(bikeRegistry.findBike("BIKE-002")); + assertNotNull(bikeRegistry.findBike("BIKE-003")); + } +} diff --git a/source/src/test/integration/RepairTaskCatalogTest.java b/source/src/test/integration/RepairTaskCatalogTest.java new file mode 100644 index 0000000..d85c71f --- /dev/null +++ b/source/src/test/integration/RepairTaskCatalogTest.java @@ -0,0 +1,55 @@ +package integration; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import model.TaskDTO; + +import static org.junit.jupiter.api.Assertions.*; + +class RepairTaskCatalogTest { + + private RepairTaskCatalog catalog; + + @BeforeEach + void setUp() { + catalog = new RepairTaskCatalog(); + } + + @Test + void findTaskWithExistingNameReturnsTaskDTO() { + TaskDTO task = catalog.findTask("Brake Pad Replacement"); + assertNotNull(task); + } + + @Test + void findTaskWithExistingNameReturnsCorrectName() { + TaskDTO task = catalog.findTask("Tire Replacement"); + assertEquals("Tire Replacement", task.getName()); + } + + @Test + void findTaskWithExistingNameReturnsCorrectCost() { + TaskDTO task = catalog.findTask("Battery Check"); + assertEquals(200.0, task.getCost().getValue(), 0.001); + } + + @Test + void findTaskWithNonExistingNameReturnsNull() { + TaskDTO task = catalog.findTask("Non-existing Task"); + assertNull(task); + } + + @Test + void findTaskWithEmptyStringReturnsNull() { + TaskDTO task = catalog.findTask(""); + assertNull(task); + } + + @Test + void findTaskWithAllFourSampleTasksReturnsNonNull() { + assertNotNull(catalog.findTask("Brake Pad Replacement")); + assertNotNull(catalog.findTask("Tire Replacement")); + assertNotNull(catalog.findTask("Battery Check")); + assertNotNull(catalog.findTask("Chain Lubrication")); + } +} diff --git a/source/src/test/model/ActiveRepairTest.java b/source/src/test/model/ActiveRepairTest.java new file mode 100644 index 0000000..87ac653 --- /dev/null +++ b/source/src/test/model/ActiveRepairTest.java @@ -0,0 +1,72 @@ +package model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ActiveRepairTest { + + private ActiveRepair activeRepair; + + @BeforeEach + void setUp() { + activeRepair = new ActiveRepair(); + } + + @Test + void addSingleTaskReturnsTaskCostAsRunningTotal() { + TaskDTO task = new TaskDTO("Brake Pad Replacement", new Amount(350)); + Amount runningTotal = activeRepair.addTask(task); + assertEquals(350, runningTotal.getValue(), 0.001); + } + + @Test + void addTwoTasksReturnsCorrectCumulativeTotal() { + activeRepair.addTask(new TaskDTO("Brake Pad Replacement", new Amount(350))); + Amount runningTotal = activeRepair.addTask(new TaskDTO("Tire Replacement", new Amount(500))); + assertEquals(850, runningTotal.getValue(), 0.001); + } + + @Test + void endRepairWithRegisteredBikeReturnsBikeInRepairOrder() { + BikeDTO bike = new BikeDTO("BIKE-001", "Alice Svensson"); + activeRepair.registerBike(bike); + RepairOrder repairOrder = activeRepair.endRepair(); + assertEquals("BIKE-001", repairOrder.getBike().getBikeID()); + } + + @Test + void endRepairAfterAddingTasksReturnsAllTasksInOrder() { + activeRepair.registerBike(new BikeDTO("BIKE-001", "Alice Svensson")); + activeRepair.addTask(new TaskDTO("Brake Pad Replacement", new Amount(350))); + activeRepair.addTask(new TaskDTO("Tire Replacement", new Amount(500))); + RepairOrder repairOrder = activeRepair.endRepair(); + assertEquals(2, repairOrder.getTasks().size()); + } + + @Test + void endRepairReturnsCorrectTotalInRepairOrder() { + activeRepair.registerBike(new BikeDTO("BIKE-001", "Alice Svensson")); + activeRepair.addTask(new TaskDTO("Brake Pad Replacement", new Amount(350))); + activeRepair.addTask(new TaskDTO("Tire Replacement", new Amount(500))); + RepairOrder repairOrder = activeRepair.endRepair(); + assertEquals(850, repairOrder.getTotal().getValue(), 0.001); + } + + @Test + void endRepairWithoutTasksReturnsTotalOfZero() { + activeRepair.registerBike(new BikeDTO("BIKE-001", "Alice Svensson")); + RepairOrder repairOrder = activeRepair.endRepair(); + assertEquals(0, repairOrder.getTotal().getValue(), 0.001); + } + + @Test + void enterDiagnosticReportIsIncludedInRepairOrder() { + activeRepair.registerBike(new BikeDTO("BIKE-001", "Alice Svensson")); + String report = "Front tire replaced."; + activeRepair.enterDiagnosticReport(report); + RepairOrder repairOrder = activeRepair.endRepair(); + assertEquals(report, repairOrder.getDiagnosticReport()); + } +} diff --git a/source/src/test/model/AmountTest.java b/source/src/test/model/AmountTest.java new file mode 100644 index 0000000..1652610 --- /dev/null +++ b/source/src/test/model/AmountTest.java @@ -0,0 +1,61 @@ +package model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AmountTest { + + @Test + void addTwoPositiveAmountsReturnsCorrectSum() { + Amount a = new Amount(200); + Amount b = new Amount(150); + Amount result = a.add(b); + assertEquals(350, result.getValue(), 0.001); + } + + @Test + void addZeroToAmountReturnsUnchangedValue() { + Amount a = new Amount(500); + Amount zero = new Amount(0); + Amount result = a.add(zero); + assertEquals(500, result.getValue(), 0.001); + } + + @Test + void addTwoDecimalAmountsReturnsCorrectSum() { + Amount a = new Amount(99.99); + Amount b = new Amount(0.01); + Amount result = a.add(b); + assertEquals(100.0, result.getValue(), 0.001); + } + + @Test + void subtractSmallerAmountReturnsPositiveDifference() { + Amount a = new Amount(300); + Amount b = new Amount(100); + Amount result = a.subtract(b); + assertEquals(200, result.getValue(), 0.001); + } + + @Test + void subtractEqualAmountReturnsZero() { + Amount a = new Amount(250); + Amount b = new Amount(250); + Amount result = a.subtract(b); + assertEquals(0, result.getValue(), 0.001); + } + + @Test + void getValueReturnsStoredValue() { + Amount a = new Amount(123.45); + assertEquals(123.45, a.getValue(), 0.001); + } + + @Test + void addIsNotMutatingOriginalAmount() { + Amount original = new Amount(100); + original.add(new Amount(50)); + assertEquals(100, original.getValue(), 0.001); + } +} diff --git a/source/src/test/model/RepairShopTest.java b/source/src/test/model/RepairShopTest.java new file mode 100644 index 0000000..1e28737 --- /dev/null +++ b/source/src/test/model/RepairShopTest.java @@ -0,0 +1,67 @@ +package model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RepairShopTest { + + private RepairShop repairShop; + + @BeforeEach + void setUp() { + repairShop = new RepairShop(); + repairShop.startRepair(); + } + + @Test + void addTaskAfterStartReturnsTaskCostAsRunningTotal() { + TaskDTO task = new TaskDTO("Brake Pad Replacement", new Amount(350)); + Amount runningTotal = repairShop.addTask(task); + assertEquals(350, runningTotal.getValue(), 0.001); + } + + @Test + void addTwoTasksReturnsCumulativeTotal() { + repairShop.addTask(new TaskDTO("Brake Pad Replacement", new Amount(350))); + Amount runningTotal = repairShop.addTask(new TaskDTO("Tire Replacement", new Amount(500))); + assertEquals(850, runningTotal.getValue(), 0.001); + } + + @Test + void endRepairAfterRegisterBikeAndTasksReturnsRepairOrderWithBike() { + BikeDTO bike = new BikeDTO("BIKE-002", "Bob Lindqvist"); + repairShop.registerBike(bike); + repairShop.addTask(new TaskDTO("Battery Check", new Amount(200))); + RepairOrder repairOrder = repairShop.endRepair(); + assertEquals("BIKE-002", repairOrder.getBike().getBikeID()); + } + + @Test + void endRepairReturnsRepairOrderWithCorrectTotal() { + repairShop.registerBike(new BikeDTO("BIKE-002", "Bob Lindqvist")); + repairShop.addTask(new TaskDTO("Battery Check", new Amount(200))); + repairShop.addTask(new TaskDTO("Chain Lubrication", new Amount(150))); + RepairOrder repairOrder = repairShop.endRepair(); + assertEquals(350, repairOrder.getTotal().getValue(), 0.001); + } + + @Test + void startRepairTwiceCreatesNewSessionWithZeroTotal() { + repairShop.registerBike(new BikeDTO("BIKE-001", "Alice Svensson")); + repairShop.addTask(new TaskDTO("Brake Pad Replacement", new Amount(350))); + repairShop.startRepair(); + repairShop.registerBike(new BikeDTO("BIKE-002", "Bob Lindqvist")); + RepairOrder repairOrder = repairShop.endRepair(); + assertEquals(0, repairOrder.getTotal().getValue(), 0.001); + } + + @Test + void enterDiagnosticReportIsReflectedInRepairOrder() { + repairShop.registerBike(new BikeDTO("BIKE-003", "Carl Johansson")); + repairShop.enterDiagnosticReport("Tire replaced successfully."); + RepairOrder repairOrder = repairShop.endRepair(); + assertEquals("Tire replaced successfully.", repairOrder.getDiagnosticReport()); + } +}