feat(kunde): Validierung, KundeService + DP-Schnittstelle (Phase 3-5)
parent
9eaa2b6790
commit
8fd72c2691
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CheckStyle-IDEA" serialisationVersion="2">
|
||||
<checkstyleVersion>12.3.1</checkstyleVersion>
|
||||
<scanScope>JavaOnly</scanScope>
|
||||
<option name="thirdPartyClasspath" />
|
||||
<option name="activeLocationIds" />
|
||||
<option name="locations">
|
||||
<list>
|
||||
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="fakturierungssystem" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="openjdk-23" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -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 + '\'' + '}';
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Kunde> kunden = new ArrayList<>();
|
||||
private long naechsteId = 1;
|
||||
|
||||
public List<Kunde> getKunden() { return kunden; }
|
||||
public void setKunden(List<Kunde> kunden) { this.kunden = kunden; }
|
||||
|
||||
public long getNaechsteId() { return naechsteId; }
|
||||
public void setNaechsteId(long naechsteId) { this.naechsteId = naechsteId; }
|
||||
}
|
||||
|
|
@ -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<Kunde> findeById(long kundeId);
|
||||
|
||||
List<Kunde> findeAlle();
|
||||
|
||||
void loeschen(long kundeId);
|
||||
}
|
||||
|
|
@ -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<Long, Kunde> 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<Kunde> findeById(long kundeId) {
|
||||
return Optional.ofNullable(kunden.get(kundeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Kunde> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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<Kunde> 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<Kunde> 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<Kunde> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Kunde> 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue