Files
iu eef7c6c5d0 fix(storage): emit origin-relative object URLs
Build signed object URLs as path-only (/api/objects/...) instead of
prefixing APP_BASE_URL, so media loads from whatever host or reverse
proxy served the app. Remove the now-unused base URL from the signer and
drop the APP_BASE_URL / Server.BaseURL config.
2026-10-12 12:14:46 +03:00

83 lines
2.0 KiB
Go

package storage
import (
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"testing"
"time"
)
func expiryOf(t *testing.T, raw string) int64 {
t.Helper()
u, err := url.Parse(raw)
if err != nil {
t.Fatalf("parse url: %v", err)
}
e, err := strconv.ParseInt(u.Query().Get("e"), 10, 64)
if err != nil {
t.Fatalf("parse expiry: %v", err)
}
return e
}
func TestSignerURLStableWithinWindow(t *testing.T) {
s, err := NewSigner("secret", time.Hour, time.Hour)
if err != nil {
t.Fatalf("new signer: %v", err)
}
first := s.URL("objects/a/thumb.webp")
second := s.URL("objects/a/thumb.webp")
if first != second {
t.Fatalf("URL changed within window:\n %q\n %q", first, second)
}
}
func TestSignerURLChangesAcrossWindow(t *testing.T) {
s, err := NewSigner("secret", time.Hour, 10*time.Minute)
if err != nil {
t.Fatalf("new signer: %v", err)
}
base := time.Now()
s.now = func() time.Time { return base }
first := expiryOf(t, s.URL("objects/a/thumb.webp"))
s.now = func() time.Time { return base.Add(s.window) }
second := expiryOf(t, s.URL("objects/a/thumb.webp"))
if first == second {
t.Fatalf("expiry did not advance across window: %d", first)
}
}
func TestSignerURLVerifies(t *testing.T) {
s, err := NewSigner("secret", time.Hour, 10*time.Minute)
if err != nil {
t.Fatalf("new signer: %v", err)
}
key := "objects/a/thumb.webp"
raw := s.URL(key)
req := httptest.NewRequest(http.MethodGet, raw, nil)
if err := s.VerifyRequest(key, req); err != nil {
t.Fatalf("verify issued URL: %v", err)
}
}
func TestSignerURLIsOriginRelative(t *testing.T) {
s, err := NewSigner("secret", time.Hour, 10*time.Minute)
if err != nil {
t.Fatalf("new signer: %v", err)
}
raw := s.URL("objects/a/thumb.webp")
u, err := url.Parse(raw)
if err != nil {
t.Fatalf("parse url: %v", err)
}
if u.Scheme != "" || u.Host != "" || u.IsAbs() {
t.Fatalf("url is not origin-relative: %q", raw)
}
if !strings.HasPrefix(raw, "/api/objects/") {
t.Fatalf("url = %q, want /api/objects/ prefix", raw)
}
}