Ein Driver-Interface, zwei austauschbare Treiber: LocalDriver (Entwicklung, Dateisystem, HMAC-signierte URLs) und S3Driver (Produktion, S3-kompatibel via aws-sdk-go-v2, echte presigned URLs). Pfadschema documents/<id>/ revisions/<id> innerhalb des mandantenspezifischen Buckets. Service verbindet Driver mit Nutzungsmeldung an Core LIC-05 (HTTPUsageReporter, Vertrag von internal/resync.Handler nachgebildet, DMS kann Cores internal/- Pakete als eigenes Modul nicht importieren). Auf 192.168.1.131 verifiziert, S3-Treiber gegen echtes lokal installiertes MinIO (kein Mock): Round-Trip beide Treiber, abgelaufene presigned URL real mit 403 abgewiesen (manuell zusaetzlich zum Unit-Test verifiziert), klare ErrNotFound bei fehlendem Objekt beide Treiber, Nutzungsmeldung mit korrektem Tenant/Metrik/Delta bei Put/Delete. Befund dokumentiert: Cores internal/resync.Handler (Gegenstelle fuer die Nutzungsmeldung) ist noch in keinem cmd/*/main.go verdrahtet (dieselbe Fehlerklasse wie QA-05/AUD-06) - HTTPUsageReporter daher gegen den dokumentierten Vertrag getestet, nicht gegen eine laufende Core-Instanz. Siehe dms/docs/FDN-03-PRUEFPROTOKOLL.md. golangci-lint auf v2.1.6 aktualisiert (v1.63.4 konnte go1.24-Zielstand nicht linten), .golangci.yml auf v2-Konfigurationsformat migriert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// requireS3TestEnv liefert die S3-Testkonfiguration oder ueberspringt den
|
|
// Test — dasselbe Muster wie TEST_ADMIN_DSN im Core-Modul: kein S3-
|
|
// kompatibler Speicher in dieser Umgebung verfuegbar/geprueft (siehe
|
|
// FDN-03-Pruefprotokoll), daher hier bewusst als optional markiert statt
|
|
// den Treiber ungetestet zu lassen.
|
|
func requireS3TestEnv(t *testing.T) *S3Driver {
|
|
t.Helper()
|
|
bucket := os.Getenv("TEST_S3_BUCKET")
|
|
if bucket == "" {
|
|
t.Skip("TEST_S3_BUCKET nicht gesetzt, S3-Integrationstest uebersprungen")
|
|
}
|
|
endpoint := os.Getenv("TEST_S3_ENDPOINT")
|
|
region := os.Getenv("TEST_S3_REGION")
|
|
if region == "" {
|
|
region = "us-east-1"
|
|
}
|
|
accessKey := os.Getenv("TEST_S3_ACCESS_KEY_ID")
|
|
secretKey := os.Getenv("TEST_S3_SECRET_ACCESS_KEY")
|
|
|
|
d, err := NewS3Driver(context.Background(), bucket, region, endpoint, accessKey, secretKey, true)
|
|
if err != nil {
|
|
t.Fatalf("s3-treiber aufbauen: %v", err)
|
|
}
|
|
return d
|
|
}
|
|
|
|
// TestS3Driver_RoundTrip ist Pruefung 1 fuer den S3-Treiber.
|
|
func TestS3Driver_RoundTrip(t *testing.T) {
|
|
d := requireS3TestEnv(t)
|
|
ctx := context.Background()
|
|
key := "fdn03-test/roundtrip"
|
|
content := []byte("s3 roundtrip inhalt")
|
|
|
|
t.Cleanup(func() { _ = d.Delete(ctx, key) })
|
|
|
|
if _, err := d.Put(ctx, key, bytes.NewReader(content), int64(len(content)), "text/plain"); err != nil {
|
|
t.Fatalf("put: %v", err)
|
|
}
|
|
rc, err := d.Get(ctx, key)
|
|
if err != nil {
|
|
t.Fatalf("get: %v", err)
|
|
}
|
|
defer func() { _ = rc.Close() }()
|
|
got, err := io.ReadAll(rc)
|
|
if err != nil {
|
|
t.Fatalf("lesen: %v", err)
|
|
}
|
|
if !bytes.Equal(got, content) {
|
|
t.Fatalf("gelesener inhalt = %q, want %q", got, content)
|
|
}
|
|
}
|
|
|
|
// TestS3Driver_MissingObject ist Pruefung 3 fuer den S3-Treiber.
|
|
func TestS3Driver_MissingObject(t *testing.T) {
|
|
d := requireS3TestEnv(t)
|
|
ctx := context.Background()
|
|
|
|
if _, err := d.Get(ctx, "fdn03-test/nie-angelegt"); !errors.Is(err, ErrNotFound) {
|
|
t.Fatalf("get eines fehlenden objekts: erwartet ErrNotFound, habe %v", err)
|
|
}
|
|
if err := d.Delete(ctx, "fdn03-test/nie-angelegt"); !errors.Is(err, ErrNotFound) {
|
|
t.Fatalf("delete eines fehlenden objekts: erwartet ErrNotFound, habe %v", err)
|
|
}
|
|
}
|
|
|
|
// TestS3Driver_SignedURL ist Pruefung 2 fuer den S3-Treiber: eine
|
|
// presigned URL wird erzeugt und ist innerhalb der Gueltigkeit abrufbar.
|
|
func TestS3Driver_SignedURL(t *testing.T) {
|
|
d := requireS3TestEnv(t)
|
|
ctx := context.Background()
|
|
key := "fdn03-test/signed-url"
|
|
content := []byte("presigned")
|
|
t.Cleanup(func() { _ = d.Delete(ctx, key) })
|
|
|
|
if _, err := d.Put(ctx, key, bytes.NewReader(content), int64(len(content)), "text/plain"); err != nil {
|
|
t.Fatalf("put: %v", err)
|
|
}
|
|
|
|
url, err := d.SignedURL(ctx, key, time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("signedurl: %v", err)
|
|
}
|
|
if url == "" {
|
|
t.Fatal("erwartet nicht-leere presigned url")
|
|
}
|
|
}
|