docs: log session outcomes and apply enhancements across multiple components
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Has been cancelled

- **Docker Fixes:** Resolved failed builds for Gateway and Ping services by switching to `eclipse-temurin:21-jdk-alpine`, correcting Gradle configurations, and fixing cache mount paths.
- **ZNS-Import Consul Registration:** Enabled Consul service discovery by updating `application.yaml` and `build.gradle.kts`.
- **pgAdmin Provisioning:** Preconfigured the database server in `servers.json` and updated `dc-ops.yaml` for seamless setup.
- **Postman Documentation:** Added a detailed Postman test guide covering environment setup, endpoint groups, and recommended test sequences.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-03 14:24:42 +02:00
parent ed3d327c82
commit b9ec070993
16 changed files with 1685 additions and 22 deletions
@@ -0,0 +1,960 @@
# 🧪 Postman Tests — Vollständige Dokumentation
> **Stand:** 3. April 2026
> **Erstellt von:** 👷 Backend Developer & 🧐 QA Specialist
> **Collection-Datei:**
`backend/infrastructure/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json`
---
## Inhaltsverzeichnis
1. [Setup & Variablen](#1-setup--variablen)
2. [Smoke-Tests: System & Health](#2-smoke-tests-system--health)
3. [Ping Service](#3-ping-service)
4. [Authentication Context](#4-authentication-context)
5. [Master Data: Countries](#5-master-data-countries)
6. [Horse Registry (Pferde)](#6-horse-registry-pferde)
7. [ZNS Import Service](#7-zns-import-service)
8. [Empfohlene Test-Reihenfolge](#8-empfohlene-test-reihenfolge)
---
## 1. Setup & Variablen
### Collection importieren
1. Postman öffnen → **Import** → Datei auswählen:
```
backend/infrastructure/gateway/src/main/resources/static/docs/postman/Meldestelle_API_Collection.json
```
2. Collection `Meldestelle API Collection` erscheint im linken Panel.
### Environment-Variablen anlegen
In Postman ein **Environment** `Meldestelle Local` anlegen mit folgenden Variablen:
| Variable | Initial Value | Beschreibung |
|-------------|-------------------------|------------------------------------|
| `baseUrl` | `http://localhost:8081` | API-Gateway (Haupt-Einstiegspunkt) |
| `pingUrl` | `http://localhost:8082` | Ping-Service direkt (ohne Gateway) |
| `znsUrl` | `http://localhost:8095` | ZNS-Import-Service direkt |
| `authToken` | *(leer, wird befüllt)* | JWT-Token nach Login |
| `horseId` | *(leer, wird befüllt)* | ID eines angelegten Pferdes |
| `countryId` | *(leer, wird befüllt)* | ID eines angelegten Landes |
| `horseId1` | *(leer)* | Für Batch-Delete Tests |
| `horseId2` | *(leer)* | Für Batch-Delete Tests |
> ⚠️ **Wichtig:** Sicherstellen dass alle Services laufen bevor Tests gestartet werden (siehe Betriebsanleitung).
---
## 2. Smoke-Tests: System & Health
> **Zweck:** Schnell prüfen ob das API-Gateway und alle Services erreichbar sind. Immer zuerst ausführen!
---
### 2.1 API Gateway Info
| Feld | Wert |
|-------------|----------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
```json
{
"service": "Meldestelle API Gateway",
"version": "...",
"status": "running"
}
```
**Wozu:** Bestätigt dass das Gateway läuft und auf Anfragen antwortet.
---
### 2.2 Health Check (Gateway)
| Feld | Wert |
|-------------|----------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/health` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
```json
{
"status": "UP"
}
```
---
### 2.3 API Documentation
| Feld | Wert |
|-------------|-------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` — OpenAPI JSON-Dokumentation
---
### 2.4 Swagger UI
| Feld | Wert |
|-------------|-----------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/swagger` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` — Swagger HTML-Interface
**Tipp:** Im Browser öffnen für interaktive API-Erkundung.
---
## 3. Ping Service
> **Zweck:** Funktionsfähigkeit des Ping-Service testen, Circuit Breaker und Sync-Funktionen validieren.
> **Direkt-URL:** `{{pingUrl}}` = `http://localhost:8082`
> **Via Gateway:** `{{baseUrl}}` = `http://localhost:8081`
---
### 3.1 Simple Ping
| Feld | Wert |
|-------------|---------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/simple` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
```json
{
"message": "pong",
"timestamp": "2026-04-03T14:00:00+02:00",
"service": "ping-service",
"status": "simple"
}
```
**Wozu:** Einfachster Smoke-Test — bestätigt dass der Service läuft und die DB erreichbar ist.
---
### 3.2 Simple Ping via Gateway
| Feld | Wert |
|-------------|---------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/ping/simple` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` (identisch wie 3.1)
**Wozu:** Bestätigt dass das Gateway den Ping-Service korrekt routet (Service Discovery via Consul).
---
### 3.3 Enhanced Ping (mit Circuit Breaker)
| Feld | Wert |
|-----------------|-----------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/enhanced` |
| **Auth** | Keine |
| **Query-Param** | `simulate=false` (Standard) |
**Erwartete Antwort:** `200 OK`
```json
{
"message": "enhanced pong",
"timestamp": "...",
"service": "ping-service",
"status": "enhanced",
"circuitBreakerStatus": "CLOSED"
}
```
**Wozu:** Bestätigt dass Resilience4j Circuit Breaker korrekt konfiguriert ist.
---
### 3.4 Enhanced Ping — Fehler simulieren
| Feld | Wert |
|-------------|-------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/enhanced?simulate=true` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` mit Fallback-Response (kein 500-Fehler!)
```json
{
"message": "fallback response",
"status": "fallback",
"circuitBreakerStatus": "OPEN",
"error": "..."
}
```
**Wozu:** Bestätigt dass der Circuit Breaker Fallback greift (60% simulierte Fehlerrate). Der Service antwortet trotz
Fehler mit einem sinnvollen Fallback.
**Mehrfach ausführen:** Nach ~5 Anfragen mit `simulate=true` öffnet der Circuit Breaker.
---
### 3.5 Public Ping (kein Auth erforderlich)
| Feld | Wert |
|-------------|---------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/public` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
**Wozu:** Bestätigt dass der `/ping/public`-Endpunkt ohne JWT-Token erreichbar ist (Security-Konfiguration).
---
### 3.6 Secure Ping (Auth erforderlich)
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/secure` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Mit gültigem Token:** `200 OK`
**Ohne Token:** `401 Unauthorized`
**Wozu:** Bestätigt dass JWT-Authentifizierung korrekt erzwungen wird.
---
### 3.7 Sync Pings (seit Timestamp)
| Feld | Wert |
|-----------------|-----------------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/sync` |
| **Auth** | Keine |
| **Query-Param** | `since=0` (alle Pings seit Epoch) |
**Erwartete Antwort:** `200 OK`
```json
[
{
"id": "...",
"message": "pong",
"timestamp": "...",
"lamportClock": 1
}
]
```
**Wozu:** Testet den LAN-Sync-Endpunkt (Lamport-Uhren, Event-Sourcing Light). `since` ist ein Unix-Timestamp in
Millisekunden.
---
### 3.8 Ping Service Health
| Feld | Wert |
|-------------|---------------------------|
| **Methode** | `GET` |
| **URL** | `{{pingUrl}}/ping/health` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
```json
{
"status": "up",
"timestamp": "...",
"service": "ping-service",
"healthy": true
}
```
---
## 4. Authentication Context
> **Zweck:** Benutzer-Registrierung, Login und Profilverwaltung über Keycloak testen.
> **Reihenfolge:** Zuerst registrieren → dann einloggen → Token in `{{authToken}}` speichern.
---
### 4.1 User Registration
| Feld | Wert |
|-------------|----------------------------------|
| **Methode** | `POST` |
| **URL** | `{{baseUrl}}/auth/register` |
| **Header** | `Content-Type: application/json` |
**Request Body:**
```json
{
"email": "test@example.com",
"password": "SecurePassword123!",
"firstName": "Test",
"lastName": "User",
"phoneNumber": "+43123456789"
}
```
**Erwartete Antwort:** `201 Created`
```json
{
"userId": "uuid-...",
"email": "test@example.com",
"message": "Registrierung erfolgreich"
}
```
**Wozu:** Legt einen neuen Benutzer in Keycloak an.
---
### 4.2 User Login
| Feld | Wert |
|-------------|----------------------------------|
| **Methode** | `POST` |
| **URL** | `{{baseUrl}}/auth/login` |
| **Header** | `Content-Type: application/json` |
**Request Body:**
```json
{
"email": "test@example.com",
"password": "SecurePassword123!"
}
```
**Erwartete Antwort:** `200 OK`
```json
{
"accessToken": "eyJhbGci...",
"refreshToken": "...",
"expiresIn": 300
}
```
> **⚡ Tipp:** Postman-Test-Script einfügen um Token automatisch zu speichern:
> ```javascript
> var json = pm.response.json();
> pm.environment.set("authToken", json.accessToken);
> ```
---
### 4.3 Get User Profile
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/auth/profile` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK`
```json
{
"userId": "...",
"email": "test@example.com",
"firstName": "Test",
"lastName": "User"
}
```
---
### 4.4 Update User Profile
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `PUT` |
| **URL** | `{{baseUrl}}/auth/profile` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"firstName": "Updated",
"lastName": "User",
"phoneNumber": "+43987654321"
}
```
**Erwartete Antwort:** `200 OK` — aktualisierte Profildaten
---
### 4.5 Change Password
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `POST` |
| **URL** | `{{baseUrl}}/auth/change-password` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"currentPassword": "SecurePassword123!",
"newPassword": "NewSecurePassword456!"
}
```
**Erwartete Antwort:** `200 OK`
**Fehlerfall (falsches Passwort):** `400 Bad Request`
---
## 5. Master Data: Countries
> **Zweck:** Länderstammdaten verwalten. Diese sind Voraussetzung für Pferde- und Reiter-Datensätze.
> **Basis-URL:** `{{baseUrl}}/api/masterdata/countries`
> **Hinweis:** GET-Endpunkte sind ohne Auth zugänglich; POST/PUT/DELETE benötigen `{{authToken}}`.
---
### 5.1 Get All Countries
| Feld | Wert |
|-------------|----------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/masterdata/countries` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` — Array aller Länder (inkl. inaktive)
---
### 5.2 Get Active Countries
| Feld | Wert |
|-------------|-----------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/masterdata/countries/active` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` — Nur Länder mit `istAktiv: true`
**Wozu:** Für Dropdown-Listen in der Desktop-App (nur aktive Länder anzeigen).
---
### 5.3 Get Country by ID
| Feld | Wert |
|-------------|------------------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/masterdata/countries/{{countryId}}` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK` — Einzelnes Land
**Fehlerfall:** `404 Not Found`
---
### 5.4 Get Country by ISO Code
| Feld | Wert |
|-------------|-----------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/masterdata/countries/iso/AT` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
```json
{
"id": "...",
"isoAlpha2Code": "AT",
"nameDeutsch": "Österreich",
"istEuMitglied": true,
"istAktiv": true
}
```
**Wozu:** Österreich ist das primäre Land — dieser Test prüft ob die Stammdaten korrekt befüllt sind.
---
### 5.5 Create Country
| Feld | Wert |
|-------------|----------------------------------------|
| **Methode** | `POST` |
| **URL** | `{{baseUrl}}/api/masterdata/countries` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"isoAlpha2Code": "TS",
"isoAlpha3Code": "TST",
"isoNumerischerCode": "999",
"nameDeutsch": "Testland",
"nameEnglisch": "Testland",
"istEuMitglied": false,
"istEwrMitglied": false,
"istAktiv": true,
"sortierReihenfolge": 999
}
```
**Erwartete Antwort:** `201 Created`
> **⚡ Tipp:** `countryId` aus der Antwort in Environment-Variable speichern:
> ```javascript
> var json = pm.response.json();
> pm.environment.set("countryId", json.id);
> ```
---
### 5.6 Update Country
| Feld | Wert |
|-------------|------------------------------------------------------|
| **Methode** | `PUT` |
| **URL** | `{{baseUrl}}/api/masterdata/countries/{{countryId}}` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"isoAlpha2Code": "TS",
"isoAlpha3Code": "TST",
"isoNumerischerCode": "999",
"nameDeutsch": "Updated Testland",
"nameEnglisch": "Updated Testland",
"istEuMitglied": false,
"istEwrMitglied": false,
"istAktiv": true,
"sortierReihenfolge": 999
}
```
**Erwartete Antwort:** `200 OK`
---
### 5.7 Delete Country
| Feld | Wert |
|-------------|------------------------------------------------------|
| **Methode** | `DELETE` |
| **URL** | `{{baseUrl}}/api/masterdata/countries/{{countryId}}` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `204 No Content`
**Fehlerfall (nicht gefunden):** `404 Not Found`
---
## 6. Horse Registry (Pferde)
> **Zweck:** Pferde-Stammdaten verwalten (CRUD).
> **⚠️ Hinweis Tabellen-Name:** In der Datenbank heißt die Tabelle `horse` (englisch), nicht `pferd`.
> **Basis-URL:** `{{baseUrl}}/api/horses`
---
### 6.1 Get All Horses
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/horses` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK` — Array aller Pferde
**Nach ZNS-Import:** Hier erscheinen alle importierten Pferde.
---
### 6.2 Get Active Horses
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/horses/active` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK` — Nur Pferde mit `istAktiv: true`
---
### 6.3 Get Horse by ID
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/horses/{{horseId}}` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK`
```json
{
"id": "uuid-...",
"pferdeName": "Amadeus",
"geschlecht": "WALLACH",
"geburtsdatum": "2020-05-15",
"rasse": "Warmblut",
"farbe": "Braun",
"stockmass": 165,
"istAktiv": true
}
```
---
### 6.4 Search Horses by Name
| Feld | Wert |
|-------------|----------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/horses/search` |
| **Query** | `name=Test` (Suchbegriff), `limit=10` (max.) |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK` — gefilterte Pferde-Liste
**Wozu:** Echtzeit-Suche in der Desktop-App. Testen mit einem Namen aus dem ZNS-Import.
---
### 6.5 Get Horses by Owner
| Feld | Wert |
|-------------|--------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/horses/owner/{{ownerId}}` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK` — alle Pferde eines bestimmten Besitzers
---
### 6.6 Create Horse
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `POST` |
| **URL** | `{{baseUrl}}/api/horses` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"pferdeName": "Test Horse",
"geschlecht": "WALLACH",
"geburtsdatum": "2020-05-15",
"rasse": "Warmblut",
"farbe": "Braun",
"zuechterName": "Test Breeder",
"stockmass": 165,
"istAktiv": true,
"bemerkungen": "Test horse for API demonstration"
}
```
**Gültige Werte für `geschlecht`:** `WALLACH`, `STUTE`, `HENGST`
**Erwartete Antwort:** `201 Created`
> **⚡ Tipp:** `horseId` automatisch speichern:
> ```javascript
> var json = pm.response.json();
> pm.environment.set("horseId", json.id);
> ```
---
### 6.7 Update Horse
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `PUT` |
| **URL** | `{{baseUrl}}/api/horses/{{horseId}}` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"pferdeName": "Updated Test Horse",
"geschlecht": "WALLACH",
"geburtsdatum": "2020-05-15",
"rasse": "Warmblut",
"farbe": "Dunkelbraun",
"zuechterName": "Updated Test Breeder",
"stockmass": 167,
"istAktiv": true,
"bemerkungen": "Updated test horse for API demonstration"
}
```
**Erwartete Antwort:** `200 OK`
---
### 6.8 Delete Horse
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `DELETE` |
| **URL** | `{{baseUrl}}/api/horses/{{horseId}}` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `204 No Content`
---
### 6.9 Batch Delete Horses
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `DELETE` |
| **URL** | `{{baseUrl}}/api/horses/batch` |
| **Header** | `Content-Type: application/json` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Request Body:**
```json
{
"horseIds": ["{{horseId1}}", "{{horseId2}}"],
"forceDelete": false
}
```
**`forceDelete: false`** → Nur löschen wenn keine Abhängigkeiten vorhanden
**`forceDelete: true`** → Auch bei Abhängigkeiten löschen (⚠️ Vorsicht!)
**Erwartete Antwort:** `200 OK`
---
### 6.10 Get Horse Statistics
| Feld | Wert |
|-------------|---------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{baseUrl}}/api/horses/stats` |
| **Header** | `Authorization: Bearer {{authToken}}` |
**Erwartete Antwort:** `200 OK`
```json
{
"total": 42,
"active": 38,
"inactive": 4,
"byGeschlecht": {
"WALLACH": 20,
"STUTE": 18,
"HENGST": 4
}
}
```
**Wozu:** Schnelle Übersicht über den Datenbestand nach dem ZNS-Import.
---
## 7. ZNS Import Service
> **Zweck:** ZNS-Daten (Reiter, Pferde, Vereine, Funktionäre) aus einer `.zip`/`.dat`-Datei importieren.
> **Direkt-URL:** `{{znsUrl}}` = `http://localhost:8095`
> **Basis-Pfad:** `/api/v1/import/zns`
---
### 7.1 ZNS Import starten
| Feld | Wert |
|--------------|------------------------------------------|
| **Methode** | `POST` |
| **URL** | `{{znsUrl}}/api/v1/import/zns` |
| **Body-Typ** | `form-data` |
| **Key** | `file` (Type: **File**) |
| **Value** | ZNS-Datei auswählen (`.zip` oder `.dat`) |
**Schritt-für-Schritt in Postman:**
1. Methode: `POST`
2. URL: `http://localhost:8095/api/v1/import/zns`
3. Tab **Body** → **form-data**
4. Key: `file` → Typ auf **File** umstellen (Dropdown neben dem Key-Feld)
5. Value: ZNS-Datei auswählen
6. **Send** klicken
**Erwartete Antwort:** `202 Accepted`
```json
{
"jobId": "abc-123-def-456",
"status": "STARTED",
"message": "Import gestartet",
"startedAt": "2026-04-03T14:00:00+02:00"
}
```
> **⚡ Tipp:** `jobId` automatisch speichern:
> ```javascript
> var json = pm.response.json();
> pm.environment.set("znsJobId", json.jobId);
> ```
---
### 7.2 ZNS Import-Status abfragen
| Feld | Wert |
|-------------|----------------------------------------------------|
| **Methode** | `GET` |
| **URL** | `{{znsUrl}}/api/v1/import/zns/{{znsJobId}}/status` |
| **Auth** | Keine |
**Antwort während Import:** `200 OK`
```json
{
"jobId": "abc-123-def-456",
"status": "IN_PROGRESS",
"processedRecords": 150,
"totalRecords": 500,
"progressPercent": 30,
"startedAt": "2026-04-03T14:00:00+02:00"
}
```
**Antwort nach Abschluss:** `200 OK`
```json
{
"jobId": "abc-123-def-456",
"status": "COMPLETED",
"processedRecords": 500,
"totalRecords": 500,
"progressPercent": 100,
"importedReiter": 200,
"importedPferde": 180,
"importedVereine": 30,
"importedFunktionaere": 90,
"startedAt": "2026-04-03T14:00:00+02:00",
"completedAt": "2026-04-03T14:01:30+02:00"
}
```
**Mögliche Status-Werte:**
| Status | Bedeutung |
|---------------|----------------------------------------|
| `STARTED` | Job wurde angenommen, noch nicht aktiv |
| `IN_PROGRESS` | Import läuft gerade |
| `COMPLETED` | Import erfolgreich abgeschlossen |
| `FAILED` | Import fehlgeschlagen (Fehlerdetails) |
**Wozu:** Status pollen bis `COMPLETED` — dann Daten in pgAdmin oder Desktop-App prüfen.
---
### 7.3 ZNS Import Health-Check
| Feld | Wert |
|-------------|------------------------------|
| **Methode** | `GET` |
| **URL** | `{{znsUrl}}/actuator/health` |
| **Auth** | Keine |
**Erwartete Antwort:** `200 OK`
```json
{
"status": "UP",
"components": {
"db": { "status": "UP" },
"diskSpace": { "status": "UP" }
}
}
```
**⚠️ Consul-Check:** Nach dem Neustart des ZNS-Service sollte er im Consul-UI unter http://localhost:8500 unter dem
Namen `zns-import-service` erscheinen.
---
## 8. Empfohlene Test-Reihenfolge
### Schnell-Smoke-Test (5 Minuten)
```
1. GET {{pingUrl}}/ping/simple → "pong" ✓
2. GET {{baseUrl}}/health → "UP" ✓
3. GET {{znsUrl}}/actuator/health → "UP" ✓
4. GET {{baseUrl}}/api/masterdata/countries/iso/AT → Österreich ✓
```
### Vollständiger Integrations-Test (heute, ZNS-Import)
```
1. GET {{pingUrl}}/ping/simple → Smoke-Test
2. GET {{pingUrl}}/ping/enhanced → Circuit Breaker CLOSED
3. GET {{pingUrl}}/ping/enhanced?simulate=true → Fallback greift
4. POST {{baseUrl}}/auth/login → Token speichern
5. GET {{baseUrl}}/api/masterdata/countries/iso/AT → Stammdaten vorhanden
6. GET {{baseUrl}}/api/horses → (leer vor Import)
7. GET {{znsUrl}}/actuator/health → ZNS bereit
8. POST {{znsUrl}}/api/v1/import/zns → ZNS-Datei hochladen → jobId speichern
9. GET {{znsUrl}}/api/v1/import/zns/{{znsJobId}}/status → Status pollen bis COMPLETED
10. GET {{baseUrl}}/api/horses → Pferde jetzt befüllt ✓
11. GET {{baseUrl}}/api/horses/stats → Statistiken prüfen
12. GET {{baseUrl}}/api/horses/search?name=... → Suche testen
```
---
## Anhang: Alle URLs auf einen Blick
| Service | URL | Verwendung |
|----------------|-----------------------|--------------------------|
| API-Gateway | http://localhost:8081 | Haupt-Einstiegspunkt |
| Ping (direkt) | http://localhost:8082 | Ping-Endpunkte direkt |
| ZNS-Import | http://localhost:8095 | Import-Endpunkte |
| Keycloak Admin | http://localhost:8180 | Auth-Verwaltung |
| Consul UI | http://localhost:8500 | Service-Discovery prüfen |
| Zipkin | http://localhost:9411 | Traces nachverfolgen |
| pgAdmin | http://localhost:8888 | DB-Inhalte prüfen |
| Mailpit | http://localhost:8025 | E-Mail-Mock |