Produktverwaltung: Unit-Tests für Service erfolgreich hinzugefügt
parent
57028fba23
commit
a29c99d9e8
|
|
@ -0,0 +1,8 @@
|
||||||
|
[ {
|
||||||
|
"produktId" : "P-0001",
|
||||||
|
"bezeichnung" : "M6 Schraube",
|
||||||
|
"einzelpreisNetto" : 10.50,
|
||||||
|
"mwstSatz" : "NORMAL",
|
||||||
|
"beschreibung" : "Beschreibung",
|
||||||
|
"kategorie" : "Werkzeug"
|
||||||
|
} ]
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package de.hsmannheim.faktura.produkt;
|
||||||
|
|
||||||
|
public enum MwstSatz {
|
||||||
|
|
||||||
|
NORMAL,
|
||||||
|
ERMAESSIGT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package de.hsmannheim.faktura.produkt;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class Produkt {
|
||||||
|
|
||||||
|
private String produktId;
|
||||||
|
private String bezeichnung;
|
||||||
|
private BigDecimal einzelpreisNetto;
|
||||||
|
private MwstSatz mwstSatz;
|
||||||
|
private String beschreibung;
|
||||||
|
private String kategorie;
|
||||||
|
|
||||||
|
public Produkt() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Produkt(String produktId, String bezeichnung, BigDecimal einzelpreisNetto,
|
||||||
|
MwstSatz mwstSatz, String beschreibung, String kategorie) {
|
||||||
|
this.produktId = produktId;
|
||||||
|
this.bezeichnung = bezeichnung;
|
||||||
|
this.einzelpreisNetto = einzelpreisNetto;
|
||||||
|
this.mwstSatz = mwstSatz;
|
||||||
|
this.beschreibung = beschreibung;
|
||||||
|
this.kategorie = kategorie;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Getter und Setter
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
public String getProduktId() {
|
||||||
|
return produktId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProduktId(String produktId) {
|
||||||
|
this.produktId = produktId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBezeichnung() {
|
||||||
|
return bezeichnung;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBezeichnung(String bezeichnung) {
|
||||||
|
this.bezeichnung = bezeichnung;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getEinzelpreisNetto() {
|
||||||
|
return einzelpreisNetto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEinzelpreisNetto(BigDecimal einzelpreisNetto) {
|
||||||
|
this.einzelpreisNetto = einzelpreisNetto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MwstSatz getMwstSatz() {
|
||||||
|
return mwstSatz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMwstSatz(MwstSatz mwstSatz) {
|
||||||
|
this.mwstSatz = mwstSatz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBeschreibung() {
|
||||||
|
return beschreibung;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBeschreibung(String beschreibung) {
|
||||||
|
this.beschreibung = beschreibung;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKategorie() {
|
||||||
|
return kategorie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKategorie(String kategorie) {
|
||||||
|
this.kategorie = kategorie;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Produkt{" +
|
||||||
|
"produktId='" + produktId + '\'' +
|
||||||
|
", bezeichnung='" + bezeichnung + '\'' +
|
||||||
|
", einzelpreisNetto=" + einzelpreisNetto +
|
||||||
|
", mwstSatz=" + mwstSatz +
|
||||||
|
", kategorie='" + kategorie + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
package de.hsmannheim.faktura.produkt.repository;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import de.hsmannheim.faktura.produkt.Produkt;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
|
public class ProduktRepository {
|
||||||
|
|
||||||
|
|
||||||
|
private static final String FILE_PATH = "./data/products.json";
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
private List<Produkt> cache;
|
||||||
|
|
||||||
|
public ProduktRepository() {
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
this.cache = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Produkt> findAll() {
|
||||||
|
return new ArrayList<>(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Optional<Produkt> findById(String produktId) {
|
||||||
|
return cache.stream()
|
||||||
|
.filter(p -> p.getProduktId().equals(produktId))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(Produkt product) {
|
||||||
|
|
||||||
|
Optional<Produkt> existingProduct = findById(product.getProduktId());
|
||||||
|
|
||||||
|
if (existingProduct.isPresent()) {
|
||||||
|
int index = cache.indexOf(existingProduct.get());
|
||||||
|
cache.set(index, product);
|
||||||
|
} else {
|
||||||
|
cache.add(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean deleteById(String produktId) {
|
||||||
|
boolean removed = cache.removeIf(p -> p.getProduktId().equals(produktId));
|
||||||
|
if (removed) {
|
||||||
|
saveData();
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void loadData() {
|
||||||
|
File file = new File(FILE_PATH);
|
||||||
|
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
this.cache = new ArrayList<>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.cache = objectMapper.readValue(file, new TypeReference<List<Produkt>>() {});
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Fehler beim Laden der Produktdaten: " + e.getMessage());
|
||||||
|
|
||||||
|
this.cache = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void saveData() {
|
||||||
|
File file = new File(FILE_PATH);
|
||||||
|
|
||||||
|
File directory = file.getParentFile();
|
||||||
|
if (directory != null && !directory.exists()) {
|
||||||
|
directory.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
objectMapper.writeValue(file, cache);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Fehler beim Speichern der Produktdaten: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package de.hsmannheim.faktura.produkt.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import de.hsmannheim.faktura.produkt.Produkt;
|
||||||
|
import de.hsmannheim.faktura.produkt.repository.ProduktRepository;
|
||||||
|
|
||||||
|
|
||||||
|
public class ProduktService {
|
||||||
|
|
||||||
|
private final ProduktRepository produktRepository;
|
||||||
|
|
||||||
|
public ProduktService(ProduktRepository produktRepository) {
|
||||||
|
this.produktRepository = produktRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Produkt> getAllProducts() {
|
||||||
|
return produktRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Optional<Produkt> getProductById(String produktId) {
|
||||||
|
return produktRepository.findById(produktId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void saveProduct(Produkt product) {
|
||||||
|
if (product == null) {
|
||||||
|
throw new IllegalArgumentException("Das Produkt darf nicht null sein.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product.getEinzelpreisNetto() == null || product.getEinzelpreisNetto().compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
throw new IllegalArgumentException("Anforderung F-SH-PV-06 verletzt: Der Einzelpreis Netto darf nicht negativ sein.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product.getProduktId() == null || product.getProduktId().trim().isEmpty()) {
|
||||||
|
|
||||||
|
product.setProduktId("PROD-" + java.util.UUID.randomUUID().toString().substring(0, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
produktRepository.save(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deleteProduct(String produktId) {
|
||||||
|
if (produktId == null || produktId.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return produktRepository.deleteById(produktId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Produkt> searchProducts(String query) {
|
||||||
|
if (query == null || query.trim().isEmpty()) {
|
||||||
|
return getAllProducts();
|
||||||
|
}
|
||||||
|
String lowerQuery = query.toLowerCase();
|
||||||
|
return getAllProducts().stream()
|
||||||
|
.filter(p -> p.getBezeichnung() != null && p.getBezeichnung().toLowerCase().contains(lowerQuery))
|
||||||
|
.collect(java.util.stream.Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package de.hsmannheim.faktura.produkt.service;
|
||||||
|
|
||||||
|
import de.hsmannheim.faktura.produkt.Produkt;
|
||||||
|
import de.hsmannheim.faktura.produkt.MwstSatz;
|
||||||
|
import de.hsmannheim.faktura.produkt.repository.ProduktRepository;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class ProduktServiceTest {
|
||||||
|
|
||||||
|
private ProduktService produktService;
|
||||||
|
private ProduktRepository produktRepository;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
produktRepository = new ProduktRepository();
|
||||||
|
produktService = new ProduktService(produktRepository);
|
||||||
|
|
||||||
|
// Cache leeren, damit alte JSON-Einträge die Tests nicht verfälschen
|
||||||
|
for (Produkt p : produktRepository.findAll()) {
|
||||||
|
produktRepository.deleteById(p.getProduktId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaveValidProduct() {
|
||||||
|
Produkt valid = new Produkt("P-0001", "M6 Schraube", new BigDecimal("10.50"), MwstSatz.NORMAL, "Beschreibung", "Werkzeug");
|
||||||
|
assertDoesNotThrow(() -> produktService.saveProduct(valid));
|
||||||
|
assertTrue(produktService.getProductById("P-0001").isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNegativePriceRejection() {
|
||||||
|
// AC-SH-PV-01: Preis kleiner als 0.00 muss abgelehnt werden
|
||||||
|
Produkt invalidProduct = new Produkt("P-9999", "Testprodukt", new BigDecimal("-1.00"), MwstSatz.NORMAL, "Test", "Test");
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> produktService.saveProduct(invalidProduct));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchCaseInsensitive() {
|
||||||
|
// AC-SH-PV-03: Suchtest für Groß-/Kleinschreibung
|
||||||
|
Produkt p1 = new Produkt("P-0001", "M6 Schraube", new BigDecimal("1.00"), MwstSatz.NORMAL, "", "");
|
||||||
|
Produkt p2 = new Produkt("P-0002", "M8 SCHRAUBE", new BigDecimal("2.00"), MwstSatz.NORMAL, "", "");
|
||||||
|
|
||||||
|
produktService.saveProduct(p1);
|
||||||
|
produktService.saveProduct(p2);
|
||||||
|
|
||||||
|
List<Produkt> results = produktService.searchProducts("schraube");
|
||||||
|
assertEquals(2, results.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue