# FDN-03 – Prüfprotokoll: Objekt-Storage-Abstraktion Welle 2. Voraussetzung: FDN-01 (Status "Fertig"), Core LIC-05 (Status "Fertig"). ## Umsetzung `internal/storage`: - `Driver`-Interface (Akzeptanzkriterium 1): `Put`/`Get`/`Delete`/`SignedURL`. - `LocalDriver` — Entwicklungs-Treiber, Dateisystem, signierte URLs über HMAC-SHA256 (timing-safe verglichen, `crypto/subtle`, dieselbe Konvention wie Core IAM-15). - `S3Driver` — Produktions-Treiber, S3-kompatibel (`aws-sdk-go-v2`), presigned URLs über `s3.PresignClient`. - `ObjectKey(documentID, revisionID)` — Pfadschema `documents//revisions/` innerhalb des bereits mandantenspezifischen Buckets (Akzeptanzkriterium 3; die Bucket-Trennung selbst ist Core TEN-01). - `Service` — verbindet `Driver` mit `UsageReporter`: jeder `Put`/`Delete` löst genau eine Nutzungsmeldung mit der tatsächlichen Objektgröße aus (Akzeptanzkriterium 4). Repository-Code soll ausschließlich `Service` aufrufen, nie einen `Driver` direkt. - `HTTPUsageReporter` — meldet über Cores Service-Credential-authentifizierten Resync-Endpunkt (`internal/resync.Handler.UsageHandler`, API-06/AUD-06-Muster), Metrikname `storage_bytes` (gespiegelt aus Core `internal/usage.StorageBytesMetric`, LIC-05 — DMS kann Cores `internal/`-Pakete als eigenes Go-Modul nicht importieren). ## Wichtiger Befund: Core-Endpunkt noch nicht live verdrahtet `internal/resync.Handler` (die Gegenstelle für `HTTPUsageReporter`) ist im Core-Modul vollständig implementiert und getestet, aber **in keinem `cmd/*/main.go` registriert** (per `grep` bestätigt, Stand 2026-08-29) — dieselbe Fehlerklasse wie der QA-05/AUD-06-Befund (Bausteine existieren, sind aber nicht in einen laufenden Dienst verdrahtet). `HTTPUsageReporter` ist daher gegen den **dokumentierten Vertrag** (exakte Feldnamen/Header aus `internal/resync/handler.go` gelesen) getestet, nicht gegen eine echte laufende Core-Instanz. Prüfung 4 ist damit im Rahmen dessen erfüllt, was DMS beeinflussen kann — die Lücke auf Core-Seite ist ein Core-Board-Thema (Empfehlung: analog AUD-06 ein Ticket "Resync-Endpunkt in Core-Server verdrahten" anlegen), nicht Bestandteil dieser DMS-Kachel. ## Prüfungen | # | Prüfung | Ergebnis | |---|---|---| | 1 | Round-Trip-Test Upload/Download je Treiber | **bestanden** — `TestLocalDriver_RoundTrip` (Dateisystem) und `TestS3Driver_RoundTrip` (echtes MinIO auf 192.168.1.131, kein Mock) | | 2 | Abgelaufene signierte URL wird abgewiesen | **bestanden** — `TestLocalDriver_SignedURL_ExpiredIsRejected` (Signatur-/Ablauflogik) UND manuell gegen echtes MinIO verifiziert: presigned URL liefert `200` innerhalb der Gültigkeit, `403` nach Ablauf (2s TTL, siehe Sitzungsprotokoll) | | 3 | Verhalten bei fehlendem Objekt liefert klaren Fehler | **bestanden** — `TestLocalDriver_MissingObject`/`TestS3Driver_MissingObject`: beide Treiber liefern `ErrNotFound` für `Get` UND `Delete` eines nicht existierenden Objekts | | 4 | Melde-Aufruf an Core LIC-05 bei Schreib-/Löschvorgang nachweislich ausgelöst, korrekte Größe | **bestanden** (mit Einschränkung s.o.) — `TestService_PutReportsPositiveDelta`/`TestService_DeleteReportsNegativeDelta` (Fake-Reporter zeichnet Aufrufe auf, prüft Tenant/Metrik/Delta) UND `TestHTTPUsageReporter_SendsCorrectContractToCore` (echter HTTP-Request gegen `httptest.Server`, der Cores Vertrag nachbildet — Header, JSON-Feldnamen) | ## Build/Test-Ergebnis (192.168.1.131) ``` go build ./... -> clean go vet ./... -> clean make lint -> clean (golangci-lint v2.1.6, aus Quelle mit go1.24.4 gebaut, da v1.63.4 den Zielstand go1.24 nicht linten konnte — .golangci.yml auf v2-Konfigurationsformat migriert) go test ./internal/storage/... -v -count=1 -> 11/11 Tests ok (3 S3-Tests real gegen lokal installiertes MinIO statt uebersprungen) ``` ## Gesamtergebnis **Bestanden**, mit einer dokumentierten Abhängigkeit auf Core-Seite (Abschnitt "Wichtiger Befund") — Core muss `internal/resync.Handler` noch in einen laufenden Dienst verdrahten, bevor `HTTPUsageReporter` echte Nutzungsmeldungen an eine Produktivinstanz senden kann. Alle vier Akzeptanzkriterien und alle vier Pflichtprüfungen im Rahmen des DMS-seitigen Scopes erfüllt.