package storage import ( "bytes" "context" "errors" "io" "net/url" "strconv" "testing" "time" ) func newTestLocalDriver(t *testing.T) *LocalDriver { t.Helper() return NewLocalDriver(t.TempDir(), []byte("test-signing-secret"), "https://files.example.test") } // TestLocalDriver_RoundTrip ist Pruefung 1 fuer den lokalen Treiber: // Upload/Download-Roundtrip. func TestLocalDriver_RoundTrip(t *testing.T) { d := newTestLocalDriver(t) ctx := context.Background() key := "documents/doc-1/revisions/rev-1" content := []byte("hallo welt") written, err := d.Put(ctx, key, bytes.NewReader(content), int64(len(content)), "text/plain") if err != nil { t.Fatalf("put: %v", err) } if written != int64(len(content)) { t.Fatalf("geschriebene groesse = %d, want %d", written, len(content)) } 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) } } // TestLocalDriver_MissingObject ist Pruefung 3: klarer Fehler bei // fehlendem Objekt, sowohl fuer Get als auch Delete. func TestLocalDriver_MissingObject(t *testing.T) { d := newTestLocalDriver(t) ctx := context.Background() if _, err := d.Get(ctx, "nie-angelegt"); !errors.Is(err, ErrNotFound) { t.Fatalf("get eines fehlenden objekts: erwartet ErrNotFound, habe %v", err) } if err := d.Delete(ctx, "nie-angelegt"); !errors.Is(err, ErrNotFound) { t.Fatalf("delete eines fehlenden objekts: erwartet ErrNotFound, habe %v", err) } } // TestLocalDriver_SignedURL_ExpiredIsRejected ist Pruefung 2: eine // abgelaufene signierte URL wird abgewiesen. func TestLocalDriver_SignedURL_ExpiredIsRejected(t *testing.T) { d := newTestLocalDriver(t) ctx := context.Background() key := "documents/doc-2/revisions/rev-1" // Gueltige, noch nicht abgelaufene URL wird akzeptiert. urlValid, err := d.SignedURL(ctx, key, time.Hour) if err != nil { t.Fatalf("signedurl (gueltig): %v", err) } expiry, sig := parseSignedURLQuery(t, urlValid) if err := d.VerifySignedURL(key, expiry, sig); err != nil { t.Fatalf("gueltige url wurde abgewiesen: %v", err) } // Bereits abgelaufene URL (negative TTL) wird abgewiesen. urlExpired, err := d.SignedURL(ctx, key, -time.Hour) if err != nil { t.Fatalf("signedurl (abgelaufen): %v", err) } expiredExpiry, expiredSig := parseSignedURLQuery(t, urlExpired) if err := d.VerifySignedURL(key, expiredExpiry, expiredSig); !errors.Is(err, ErrURLExpired) { t.Fatalf("abgelaufene url: erwartet ErrURLExpired, habe %v", err) } // Manipulierte Signatur wird abgewiesen. if err := d.VerifySignedURL(key, expiry, "manipuliert"); !errors.Is(err, ErrInvalidSignature) { t.Fatalf("manipulierte signatur: erwartet ErrInvalidSignature, habe %v", err) } } func parseSignedURLQuery(t *testing.T, rawURL string) (expiry int64, sig string) { t.Helper() parsed, err := url.Parse(rawURL) if err != nil { t.Fatalf("signierte url parsen: %v (%s)", err, rawURL) } q := parsed.Query() expInt, err := strconv.ParseInt(q.Get("exp"), 10, 64) if err != nil { t.Fatalf("exp parsen: %v", err) } return expInt, q.Get("sig") }