diff --git a/.gitignore b/.gitignore index 747d42e..09c5d49 100644 --- a/.gitignore +++ b/.gitignore @@ -301,3 +301,10 @@ TSWLatexianTemp* # Uncomment the next line to have this generated file ignored. #*Notes.bib +# ---> Java / Maven +target/ +*.class + +# ---> JetBrains (IntelliJ IDEA) +.idea/ +*.iml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/SE1_Team_2.iml b/.idea/SE1_Team_2.iml deleted file mode 100644 index d6ebd48..0000000 --- a/.idea/SE1_Team_2.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml deleted file mode 100644 index d43d322..0000000 --- a/.idea/checkstyle-idea.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - 12.3.1 - JavaOnly - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index d96e79f..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index aa00ffa..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index 712ab9d..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index e122dea..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/model/Kunde.java b/src/main/java/de/hsmannheim/faktura/kunde/model/Kunde.java new file mode 100644 index 0000000..7ff3c19 --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/model/Kunde.java @@ -0,0 +1,91 @@ +package de.hsmannheim.faktura.kunde.model; + +import java.util.Objects; + +/** + * Datenobjekt "Kunde" gemäß Pflichtenheft Kap. 6.1.2. + * + * Reiner Datentyp (POJO) ohne fachliche Validierung – die Prüfung von + * Pflichtfeldern, der Bedingung firmenname||nachname und des E-Mail-Formats + * erfolgt in Phase 3 im KundeService, nicht in dieser Klasse. + */ +public class Kunde { + + private long kundeId; // Pflicht: fortlaufender, eindeutiger Primärschlüssel + private String firmenname; // Pflicht*: sofern kein Nachname (Constraint in Phase 3) + private String nachname; // Pflicht*: sofern kein Firmenname + private String vorname; // Optional + private String strasse; // Pflicht: Straße + Hausnummer + private String plz; // Pflicht: String, um führende Nullen zu erhalten! + private String ort; // Pflicht + private String telefon; // Optional + private String email; // Optional: Formatprüfung in Phase 3 + private String ustIdNr; // Optional + private String lieferadresse; // Optional: abweichende Lieferadresse + private String ansprechpartner; // Optional + + /** No-arg-Konstruktor – von Jackson für die JSON-Deserialisierung benötigt (P1-3). */ + public Kunde() { + } + + // --- Getter und Setter (P1-2) --- + + public long getKundeId() { return kundeId; } + public void setKundeId(long kundeId) { this.kundeId = kundeId; } + + public String getFirmenname() { return firmenname; } + public void setFirmenname(String firmenname) { this.firmenname = firmenname; } + + public String getNachname() { return nachname; } + public void setNachname(String nachname) { this.nachname = nachname; } + + public String getVorname() { return vorname; } + public void setVorname(String vorname) { this.vorname = vorname; } + + public String getStrasse() { return strasse; } + public void setStrasse(String strasse) { this.strasse = strasse; } + + public String getPlz() { return plz; } + public void setPlz(String plz) { this.plz = plz; } + + public String getOrt() { return ort; } + public void setOrt(String ort) { this.ort = ort; } + + public String getTelefon() { return telefon; } + public void setTelefon(String telefon) { this.telefon = telefon; } + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + + public String getUstIdNr() { return ustIdNr; } + public void setUstIdNr(String ustIdNr) { this.ustIdNr = ustIdNr; } + + public String getLieferadresse() { return lieferadresse; } + public void setLieferadresse(String lieferadresse) { this.lieferadresse = lieferadresse; } + + public String getAnsprechpartner() { return ansprechpartner; } + public void setAnsprechpartner(String ansprechpartner) { this.ansprechpartner = ansprechpartner; } + + // --- Identität über den Primärschlüssel kundeId (P1-2) --- + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Kunde other = (Kunde) o; + return kundeId == other.kundeId; + } + + @Override + public int hashCode() { + return Objects.hash(kundeId); + } + + @Override + public String toString() { + return "Kunde{kundeId=" + kundeId + + ", firmenname='" + firmenname + '\'' + + ", nachname='" + nachname + '\'' + + ", ort='" + ort + '\'' + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeContainer.java b/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeContainer.java new file mode 100644 index 0000000..1c2bced --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeContainer.java @@ -0,0 +1,24 @@ +package de.hsmannheim.faktura.kunde.repository; + +import de.hsmannheim.faktura.kunde.model.Kunde; + +import java.util.ArrayList; +import java.util.List; + +/** + * Reiner Persistenz-Container: bündelt die gespeicherten Kunden und den + * fortlaufenden ID-Zähler in einem JSON-Objekt. Der mitgespeicherte Zähler + * garantiert, dass vergebene IDs nach einem Neustart nicht wiederverwendet + * werden (P2-3). + */ +public class KundeContainer { + + private List kunden = new ArrayList<>(); + private long naechsteId = 1; + + public List getKunden() { return kunden; } + public void setKunden(List kunden) { this.kunden = kunden; } + + public long getNaechsteId() { return naechsteId; } + public void setNaechsteId(long naechsteId) { this.naechsteId = naechsteId; } +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeRepository.java b/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeRepository.java new file mode 100644 index 0000000..2c0e58a --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeRepository.java @@ -0,0 +1,22 @@ +package de.hsmannheim.faktura.kunde.repository; + +import de.hsmannheim.faktura.kunde.model.Kunde; + +import java.util.List; +import java.util.Optional; + +/** + * Abstrakte Datenhaltung für Kunden (IF-02, NF-ARCH-02). + * Kapselt die Speichertechnologie, sodass sie austauschbar ist. + */ +public interface KundeRepository { + + /** Legt neu an (kundeId == 0) oder aktualisiert; liefert die (ggf. neu vergebene) kundeId. */ + long speichern(Kunde kunde); + + Optional findeById(long kundeId); + + List findeAlle(); + + void loeschen(long kundeId); +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeRepositoryJson.java b/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeRepositoryJson.java new file mode 100644 index 0000000..f7fbdc5 --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/repository/KundeRepositoryJson.java @@ -0,0 +1,89 @@ +package de.hsmannheim.faktura.kunde.repository; + +import com.fasterxml.jackson.databind.ObjectMapper; +import de.hsmannheim.faktura.kunde.model.Kunde; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * JSON-basierte Umsetzung von KundeRepository (NF-ARCH-01). + * Hält die Kunden im Speicher und schreibt nach jeder Änderung in eine + * lokale JSON-Datei. Beim Erzeugen wird eine vorhandene Datei geladen. + */ +public class KundeRepositoryJson implements KundeRepository { + + private final File datei; + private final ObjectMapper mapper = new ObjectMapper(); + + private final Map kunden = new LinkedHashMap<>(); + private long naechsteId = 1; + + public KundeRepositoryJson(File datei) { + this.datei = datei; + laden(); + } + + @Override + public long speichern(Kunde kunde) { + if (kunde.getKundeId() == 0) { + // Neuanlage: nächste freie ID vergeben, Zähler hochzählen (kein Reuse) + kunde.setKundeId(naechsteId); + naechsteId++; + } + kunden.put(kunde.getKundeId(), kunde); + speichernInDatei(); + return kunde.getKundeId(); + } + + @Override + public Optional findeById(long kundeId) { + return Optional.ofNullable(kunden.get(kundeId)); + } + + @Override + public List findeAlle() { + return new ArrayList<>(kunden.values()); + } + + @Override + public void loeschen(long kundeId) { + kunden.remove(kundeId); + speichernInDatei(); + } + + // --- interne Persistenz-Hilfen --- + + private void laden() { + if (!datei.exists()) { + return; // erster Start: leeres Repository + } + try { + KundeContainer container = mapper.readValue(datei, KundeContainer.class); + kunden.clear(); + for (Kunde k : container.getKunden()) { + kunden.put(k.getKundeId(), k); + } + naechsteId = container.getNaechsteId(); + } catch (IOException e) { + throw new UncheckedIOException("Kundendaten konnten nicht geladen werden: " + datei, e); + } + } + + private void speichernInDatei() { + try { + KundeContainer container = new KundeContainer(); + container.setKunden(new ArrayList<>(kunden.values())); + container.setNaechsteId(naechsteId); + mapper.writerWithDefaultPrettyPrinter().writeValue(datei, container); + } catch (IOException e) { + throw new UncheckedIOException("Kundendaten konnten nicht gespeichert werden: " + datei, e); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/service/BelegReferenzPruefer.java b/src/main/java/de/hsmannheim/faktura/kunde/service/BelegReferenzPruefer.java new file mode 100644 index 0000000..6477411 --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/service/BelegReferenzPruefer.java @@ -0,0 +1,12 @@ +package de.hsmannheim.faktura.kunde.service; + +/** + * Schmale Abstraktion über die Belegseite (Gruppe F) für die GR-05-Löschsperre. + * Liefert true, wenn der Kunde in mindestens einem Beleg referenziert ist. + * Die echte Umsetzung liefert Gruppe F (BelegRepository, OP-1/P0-4); bis dahin + * und in Tests wird gegen dieses Interface programmiert. + */ +@FunctionalInterface +public interface BelegReferenzPruefer { + boolean istKundeReferenziert(long kundeId); +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/service/KundeService.java b/src/main/java/de/hsmannheim/faktura/kunde/service/KundeService.java new file mode 100644 index 0000000..eddc987 --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/service/KundeService.java @@ -0,0 +1,89 @@ +package de.hsmannheim.faktura.kunde.service; + +import de.hsmannheim.faktura.kunde.model.Kunde; +import de.hsmannheim.faktura.kunde.repository.KundeRepository; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Fachliche Logik der Kundenverwaltung (F-KV-01…04 / BA-KV-01…04). + * Orchestriert Validierung (Phase 3) und Persistenz (Phase 2); enthält + * selbst keine Speicher- oder Prüf-Details (NF-MAINT-03, Schichtentrennung). + * Die vier öffentlichen Methoden entsprechen exakt dem Schnittstellenvertrag K1. + */ +public class KundeService { + + private final KundeRepository repository; + private final KundeValidator validator; + private final BelegReferenzPruefer belegPruefer; + + public KundeService(KundeRepository repository, + KundeValidator validator, + BelegReferenzPruefer belegPruefer) { + this.repository = repository; + this.validator = validator; + this.belegPruefer = belegPruefer; + } + + /** P4-1 · F-KV-01 / BA-KV-01: validieren, ID vergeben lassen, persistieren. */ + public long anlegen(Kunde kunde) { + validator.pruefe(kunde); // wirft bei Verstoß -> speichern wird nie erreicht (P3-4) + kunde.setKundeId(0); // Neuanlage erzwingen: Repository vergibt die nächste ID + return repository.speichern(kunde); + } + + /** P4-2 · F-KV-02 / BA-KV-02: vorhandenen Kunden laden, validieren, persistieren. */ + public void bearbeiten(Kunde kunde) { + if (repository.findeById(kunde.getKundeId()).isEmpty()) { + throw new ValidierungsException( + "Kunde mit ID " + kunde.getKundeId() + " existiert nicht."); + } + validator.pruefe(kunde); + repository.speichern(kunde); + } + + /** P4-3 · F-KV-03 / BA-KV-03: Treffer über Firmenname/Nachname UND Kundennummer. */ + public List suchen(String suchbegriff) { + if (suchbegriff == null || suchbegriff.isBlank()) { + return List.of(); + } + String begriff = suchbegriff.trim().toLowerCase(); + return repository.findeAlle().stream() + .filter(k -> passt(k, begriff)) + .collect(Collectors.toList()); + } + + /** P5-1 · K3: Kundenauflösung per ID für Gruppe F (Ersatz für deren FakeCustomerLookup). */ + public Optional finde(long kundeId) { + return repository.findeById(kundeId); + } + + /** P4-4 / P4-5 · F-KV-04 / BA-KV-04 / GR-05 / NF-SEC-02: nur löschen, wenn unreferenziert. */ + public void loeschen(long kundeId) { + Optional vorhanden = repository.findeById(kundeId); + if (vorhanden.isEmpty()) { + throw new ValidierungsException( + "Kunde mit ID " + kundeId + " existiert nicht."); + } + if (belegPruefer.istKundeReferenziert(kundeId)) { + throw new LoeschsperreException( + "Kunde " + kundeId + " ist in mindestens einem Beleg referenziert " + + "und darf nicht gelöscht werden (GR-05)."); + } + repository.loeschen(kundeId); + } + + // --- interne Suchhilfen --- + + private boolean passt(Kunde k, String begriff) { + if (enthaelt(k.getFirmenname(), begriff)) return true; + if (enthaelt(k.getNachname(), begriff)) return true; + return String.valueOf(k.getKundeId()).equals(begriff); // Suche über Kundennummer + } + + private boolean enthaelt(String feld, String begriff) { + return feld != null && feld.toLowerCase().contains(begriff); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/service/KundeValidator.java b/src/main/java/de/hsmannheim/faktura/kunde/service/KundeValidator.java new file mode 100644 index 0000000..a774c6e --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/service/KundeValidator.java @@ -0,0 +1,52 @@ +package de.hsmannheim.faktura.kunde.service; + +import de.hsmannheim.faktura.kunde.model.Kunde; + +import java.util.regex.Pattern; + +/** + * Prüft einen Kunden gegen die fachlichen Regeln aus Pflichtenheft Kap. 6.1.2. + * Wirft bei Verstoß eine ValidierungsException – der aufrufende Service + * persistiert dann nicht (P3-4). + */ +public class KundeValidator { + + // Pragmatische Prüfung: etwas vor dem @, etwas danach, ein Punkt mit Endung. + // Lehnt z. B. "kunde@" ab (MT-KV-04), ohne RFC-Vollständigkeit zu erzwingen. + private static final Pattern EMAIL_MUSTER = + Pattern.compile("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"); + + public void pruefe(Kunde kunde) { + if (kunde == null) { + throw new ValidierungsException("Kunde darf nicht null sein."); + } + + // P3-2: mindestens Firmenname ODER Nachname + if (istLeer(kunde.getFirmenname()) && istLeer(kunde.getNachname())) { + throw new ValidierungsException( + "Entweder Firmenname oder Nachname muss angegeben sein."); + } + + // P3-1: Pflichtfelder der Adresse + if (istLeer(kunde.getStrasse())) { + throw new ValidierungsException("Straße ist ein Pflichtfeld."); + } + if (istLeer(kunde.getPlz())) { + throw new ValidierungsException("PLZ ist ein Pflichtfeld."); + } + if (istLeer(kunde.getOrt())) { + throw new ValidierungsException("Ort ist ein Pflichtfeld."); + } + + // P3-3: E-Mail nur prüfen, wenn überhaupt angegeben + if (!istLeer(kunde.getEmail()) + && !EMAIL_MUSTER.matcher(kunde.getEmail()).matches()) { + throw new ValidierungsException( + "E-Mail-Adresse hat kein gültiges Format: " + kunde.getEmail()); + } + } + + private boolean istLeer(String wert) { + return wert == null || wert.isBlank(); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/service/LoeschsperreException.java b/src/main/java/de/hsmannheim/faktura/kunde/service/LoeschsperreException.java new file mode 100644 index 0000000..c0c8c09 --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/service/LoeschsperreException.java @@ -0,0 +1,11 @@ +package de.hsmannheim.faktura.kunde.service; + +/** + * Wird geworfen, wenn ein Kunde wegen Beleg-Referenz (GR-05) nicht + * gelöscht werden darf. + */ +public class LoeschsperreException extends RuntimeException { + public LoeschsperreException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsmannheim/faktura/kunde/service/ValidierungsException.java b/src/main/java/de/hsmannheim/faktura/kunde/service/ValidierungsException.java new file mode 100644 index 0000000..93dc8ab --- /dev/null +++ b/src/main/java/de/hsmannheim/faktura/kunde/service/ValidierungsException.java @@ -0,0 +1,13 @@ +package de.hsmannheim.faktura.kunde.service; + +/** + * Wird geworfen, wenn ein Kunde gegen eine fachliche Validierungsregel + * verstößt (Pflichtenheft Kap. 6.1.2). Unchecked, damit die im + * Schnittstellenvertrag K1 festgelegten Service-Signaturen ohne + * throws-Klausel auskommen. + */ +public class ValidierungsException extends RuntimeException { + public ValidierungsException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/test/java/de/hsmannheim/faktura/kunde/repository/KundeRepositoryJsonTest.java b/src/test/java/de/hsmannheim/faktura/kunde/repository/KundeRepositoryJsonTest.java new file mode 100644 index 0000000..cdf2f76 --- /dev/null +++ b/src/test/java/de/hsmannheim/faktura/kunde/repository/KundeRepositoryJsonTest.java @@ -0,0 +1,40 @@ +package de.hsmannheim.faktura.kunde.repository; + +import de.hsmannheim.faktura.kunde.model.Kunde; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.nio.file.Path; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class KundeRepositoryJsonTest { + + @Test + void persistenz_ueberlebt_neustart(@TempDir Path tempDir) { + File datei = tempDir.resolve("kunden.json").toFile(); + + // 1. Repository anlegen und einen Kunden speichern + KundeRepository repo1 = new KundeRepositoryJson(datei); + Kunde kunde = new Kunde(); + kunde.setNachname("Mustermann"); + kunde.setStrasse("Hauptstr. 1"); + kunde.setPlz("01067"); // führende Null! + kunde.setOrt("Dresden"); + long id = repo1.speichern(kunde); + + // 2. Neues Repository auf dieselbe Datei = simulierter Neustart + KundeRepository repo2 = new KundeRepositoryJson(datei); + Optional geladen = repo2.findeById(id); + + // 3. Kunde ist unverändert wiederhergestellt + assertTrue(geladen.isPresent(), "Kunde sollte nach Neustart vorhanden sein"); + assertEquals("Mustermann", geladen.get().getNachname()); + assertEquals("01067", geladen.get().getPlz(), + "Führende Null der PLZ muss erhalten bleiben"); + assertEquals("Dresden", geladen.get().getOrt()); + } +} \ No newline at end of file diff --git a/src/test/java/de/hsmannheim/faktura/kunde/service/KundeServiceTest.java b/src/test/java/de/hsmannheim/faktura/kunde/service/KundeServiceTest.java new file mode 100644 index 0000000..c58e6f5 --- /dev/null +++ b/src/test/java/de/hsmannheim/faktura/kunde/service/KundeServiceTest.java @@ -0,0 +1,99 @@ +package de.hsmannheim.faktura.kunde.service; + +import de.hsmannheim.faktura.kunde.model.Kunde; +import de.hsmannheim.faktura.kunde.repository.KundeRepository; +import de.hsmannheim.faktura.kunde.repository.KundeRepositoryJson; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class KundeServiceTest { + + private KundeService service; + private boolean kundeIstReferenziert; // steuert den BelegReferenzPruefer im Test + + @BeforeEach + void setUp(@TempDir Path tempDir) { + File datei = tempDir.resolve("kunden.json").toFile(); + KundeRepository repository = new KundeRepositoryJson(datei); + KundeValidator validator = new KundeValidator(); + BelegReferenzPruefer belegPruefer = kundeId -> kundeIstReferenziert; + service = new KundeService(repository, validator, belegPruefer); + kundeIstReferenziert = false; + } + + private Kunde gueltigerKunde() { + Kunde k = new Kunde(); + k.setNachname("Mustermann"); + k.setStrasse("Hauptstr. 1"); + k.setPlz("68159"); + k.setOrt("Mannheim"); + return k; + } + + @Test + void anlegen_vergibtIdUndPersistiert() { // BA-KV-01 + long id = service.anlegen(gueltigerKunde()); + assertTrue(id > 0); + assertEquals(1, service.suchen("Mustermann").size()); + } + + @Test + void anlegen_ungueltig_wirdNichtGespeichert() { // P3-4 im Service + Kunde k = gueltigerKunde(); + k.setNachname(null); + assertThrows(ValidierungsException.class, () -> service.anlegen(k)); + assertTrue(service.suchen("Mustermann").isEmpty()); + } + + @Test + void bearbeiten_aendertGespeichertenKunden() { // BA-KV-02 + service.anlegen(gueltigerKunde()); + Kunde k = service.suchen("Mustermann").get(0); + k.setTelefon("0621 12345"); + service.bearbeiten(k); + assertEquals("0621 12345", service.suchen("Mustermann").get(0).getTelefon()); + } + + @Test + void suchen_ueberKundennummer_findet() { // BA-KV-03 / MT-KV-08 + long id = service.anlegen(gueltigerKunde()); + assertEquals(1, service.suchen(String.valueOf(id)).size()); + } + + @Test + void suchen_ohneTreffer_istLeer() { // BA-KV-03 / MT-KV-09 + service.anlegen(gueltigerKunde()); + assertTrue(service.suchen("Gibtsnicht").isEmpty()); + } + + @Test + void loeschen_nichtReferenziert_entfernt() { // BA-KV-04 / MT-KV-10 + long id = service.anlegen(gueltigerKunde()); + kundeIstReferenziert = false; + service.loeschen(id); + assertTrue(service.suchen("Mustermann").isEmpty()); + } + + @Test + void loeschen_referenziert_wirdAbgewiesen() { // BA-KV-04 / GR-05 / MT-KV-11 + long id = service.anlegen(gueltigerKunde()); + kundeIstReferenziert = true; + assertThrows(LoeschsperreException.class, () -> service.loeschen(id)); + assertEquals(1, service.suchen("Mustermann").size()); // noch vorhanden + } + + @Test + void finde_liefertAngelegtenKunden() { // K3 + long id = service.anlegen(gueltigerKunde()); + assertTrue(service.finde(id).isPresent()); + assertEquals(id, service.finde(id).get().getKundeId()); + assertTrue(service.finde(999L).isEmpty()); // unbekannte ID -> leer + } +} \ No newline at end of file diff --git a/src/test/java/de/hsmannheim/faktura/kunde/service/KundeValidatorTest.java b/src/test/java/de/hsmannheim/faktura/kunde/service/KundeValidatorTest.java new file mode 100644 index 0000000..bf2395e --- /dev/null +++ b/src/test/java/de/hsmannheim/faktura/kunde/service/KundeValidatorTest.java @@ -0,0 +1,54 @@ +package de.hsmannheim.faktura.kunde.service; + +import de.hsmannheim.faktura.kunde.model.Kunde; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class KundeValidatorTest { + + private final KundeValidator validator = new KundeValidator(); + + private Kunde gueltigerKunde() { + Kunde k = new Kunde(); + k.setNachname("Mustermann"); + k.setStrasse("Hauptstr. 1"); + k.setPlz("68159"); + k.setOrt("Mannheim"); + return k; + } + + @Test + void gueltigerKunde_wirftNicht() { + assertDoesNotThrow(() -> validator.pruefe(gueltigerKunde())); + } + + @Test + void ohneFirmennameUndNachname_wirdAbgelehnt() { // → MT-KV-02 + Kunde k = gueltigerKunde(); + k.setNachname(null); + assertThrows(ValidierungsException.class, () -> validator.pruefe(k)); + } + + @Test + void ohneStrasse_wirdAbgelehnt() { // → MT-KV-03 + Kunde k = gueltigerKunde(); + k.setStrasse(null); + assertThrows(ValidierungsException.class, () -> validator.pruefe(k)); + } + + @Test + void ungueltigeEmail_wirdAbgelehnt() { // → MT-KV-04 + Kunde k = gueltigerKunde(); + k.setEmail("kunde@"); + assertThrows(ValidierungsException.class, () -> validator.pruefe(k)); + } + + @Test + void gueltigeEmail_wirftNicht() { + Kunde k = gueltigerKunde(); + k.setEmail("kunde@example.com"); + assertDoesNotThrow(() -> validator.pruefe(k)); + } +}