mirror of
https://github.com/runbear-io/beardrive.git
synced 2026-08-25 08:08:08 +02:00
Introduce a pluggable metadata persistence layer: a MetaStore of typed repositories (AccountRepo, ProjectRepo, OrgRepo, ShareRepo, DeviceRepo). The five registries keep their in-memory maps + logic and now persist each change as one record through a repo, instead of rewriting a whole JSON file inline. The `file` backend (db_file.go) reproduces the exact on-disk JSON formats, so a running hub loads unchanged. Open*(path) constructors stay as thin wrappers over the file backend. Zero behavior change: full webapp suite green; the example hub boots and existing auth.json/orgs.json/projects.json load and sign-in works. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7Q9ZKSZRTdvrSJkYLUmYs
78 lines
2.3 KiB
Go
78 lines
2.3 KiB
Go
package webapp
|
|
|
|
import (
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRateLimiterBucket(t *testing.T) {
|
|
l := newRateLimiter(60) // 1/s sustained, burst 15
|
|
now := time.Unix(0, 0)
|
|
l.now = func() time.Time { return now }
|
|
|
|
for i := 0; i < 15; i++ {
|
|
if !l.allow("a") {
|
|
t.Fatalf("burst request %d denied", i)
|
|
}
|
|
}
|
|
if l.allow("a") {
|
|
t.Fatal("request past the burst allowed")
|
|
}
|
|
if !l.allow("b") {
|
|
t.Fatal("another key must have its own bucket")
|
|
}
|
|
now = now.Add(2 * time.Second) // refills 2 tokens
|
|
if !l.allow("a") || !l.allow("a") {
|
|
t.Fatal("refilled tokens denied")
|
|
}
|
|
if l.allow("a") {
|
|
t.Fatal("third request after a 2-token refill allowed")
|
|
}
|
|
}
|
|
|
|
// /s/* answers 429 once an IP exhausts its bucket; other IPs are unaffected.
|
|
func TestSharedRouteRateLimited(t *testing.T) {
|
|
srv, p, _, _, h := shareHub(t)
|
|
srv.ShareRPM = 60 // burst 15
|
|
_, shareURL := authedShare(t, srv, h, p.ID, "wiki/notes.md")
|
|
path := shareURL[strings.Index(shareURL, "/s/"):]
|
|
|
|
get := func(ip string) int {
|
|
req := httptest.NewRequest("GET", path, nil)
|
|
req.RemoteAddr = ip + ":1234"
|
|
return doHTTP(h, req).Code
|
|
}
|
|
last := 0
|
|
for i := 0; i < 16; i++ {
|
|
last = get("10.0.0.1")
|
|
}
|
|
if last != 429 {
|
|
t.Fatalf("16th request from one IP: %d, want 429", last)
|
|
}
|
|
if code := get("10.0.0.2"); code != 200 {
|
|
t.Fatalf("fresh IP after another's limit: %d, want 200", code)
|
|
}
|
|
}
|
|
|
|
// Rendered markdown share pages carry the BearDrive footer; shared raw HTML
|
|
// is served byte-for-byte, never injected into.
|
|
func TestSharedFooterOnMarkdownOnly(t *testing.T) {
|
|
srv, p, _, _, h := shareHub(t)
|
|
const footer = "Shared with"
|
|
|
|
_, mdURL := authedShare(t, srv, h, p.ID, "wiki/notes.md")
|
|
rec := doHTTP(h, httptest.NewRequest("GET", mdURL[strings.Index(mdURL, "/s/"):], nil))
|
|
if rec.Code != 200 || !strings.Contains(rec.Body.String(), footer) ||
|
|
!strings.Contains(rec.Body.String(), "github.com/runbear-io/beardrive") {
|
|
t.Fatalf("markdown share must carry the footer: %d %s", rec.Code, rec.Body)
|
|
}
|
|
|
|
_, htmlURL := authedShare(t, srv, h, p.ID, "wiki/report.html")
|
|
rec = doHTTP(h, httptest.NewRequest("GET", htmlURL[strings.Index(htmlURL, "/s/"):], nil))
|
|
if rec.Code != 200 || strings.Contains(rec.Body.String(), footer) {
|
|
t.Fatalf("raw HTML share must be untouched: %d %s", rec.Code, rec.Body)
|
|
}
|
|
}
|