Files
warpbox-dev/backend/libs/handlers/accounts_test.go
Daniel Legt 4eacb4cde2
All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m42s
fix(handlers): bypass box creation limits for batched uploads
Update `createOrAppendBox` to accept the upload policy and admin status, allowing policy enforcement to be handled during the box creation/append decision process. This ensures that appending files to an existing batch does not incorrectly trigger daily or active box creation limits, as no new box is being created.

Also, add unit tests to verify that batched uploads successfully bypass both daily and active box creation caps.
2026-06-01 00:20:18 +03:00

1103 lines
44 KiB
Go

package handlers
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
"warpbox.dev/backend/libs/services"
)
func TestLoggedInUploadStoresOwnerAndAnonymousUploadDoesNot(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
user, err := app.authService.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
_, token, err := app.authService.Login("daniel@example.test", "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "owned.txt", "owned")
request.Header.Set("Accept", "application/json")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusCreated {
t.Fatalf("owned upload status = %d, body = %s", response.Code, response.Body.String())
}
var ownedPayload services.UploadResult
if err := json.Unmarshal(response.Body.Bytes(), &ownedPayload); err != nil {
t.Fatalf("json.Unmarshal owned returned error: %v", err)
}
ownedBox, err := app.uploadService.GetBox(ownedPayload.BoxID)
if err != nil {
t.Fatalf("GetBox owned returned error: %v", err)
}
if ownedBox.OwnerID != user.ID {
t.Fatalf("owned OwnerID = %q, want %q", ownedBox.OwnerID, user.ID)
}
owned := uploadThroughApp(t, app)
anonymous, err := app.uploadService.GetBox(owned.BoxID)
if err != nil {
t.Fatalf("GetBox anonymous returned error: %v", err)
}
if anonymous.OwnerID != "" {
t.Fatalf("anonymous OwnerID = %q, want empty", anonymous.OwnerID)
}
boxes, err := app.uploadService.ListBoxes(0)
if err != nil {
t.Fatalf("ListBoxes returned error: %v", err)
}
foundOwned := false
for _, box := range boxes {
if box.OwnerID == user.ID {
foundOwned = true
}
}
if !foundOwned {
t.Fatalf("logged-in upload did not store owner id %q", user.ID)
}
}
func TestBearerTokenUploadActsAsUser(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
user, err := app.authService.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
tokenResult, err := app.authService.CreateAPIToken(user.ID, "cli")
if err != nil {
t.Fatalf("CreateAPIToken returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "owned.txt", "owned")
request.Header.Set("Accept", "application/json")
request.Header.Set("Authorization", "Bearer "+tokenResult.Plaintext)
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusCreated {
t.Fatalf("token upload status = %d, body = %s", response.Code, response.Body.String())
}
var payload services.UploadResult
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
t.Fatalf("json.Unmarshal returned error: %v", err)
}
box, err := app.uploadService.GetBox(payload.BoxID)
if err != nil {
t.Fatalf("GetBox returned error: %v", err)
}
if box.OwnerID != user.ID {
t.Fatalf("OwnerID = %q, want %q", box.OwnerID, user.ID)
}
// An invalid bearer token is an authentication failure, not an anonymous upload.
badRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "x.txt", "x")
badRequest.Header.Set("Accept", "application/json")
badRequest.Header.Set("Authorization", "Bearer wbx_bogus.secret")
badResponse := httptest.NewRecorder()
app.Upload(badResponse, badRequest)
if badResponse.Code != http.StatusUnauthorized {
t.Fatalf("invalid token upload status = %d, body = %s", badResponse.Code, badResponse.Body.String())
}
}
func TestAnonymousUploadWithoutBearerStillWorks(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
response := httptest.NewRecorder()
app.Upload(response, multipartUploadRequest(t, "/api/v1/upload", "file", "anonymous.txt", "anonymous"))
if response.Code != http.StatusCreated {
t.Fatalf("anonymous upload status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestDisabledUserBearerTokenCannotUpload(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
user, err := app.authService.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
tokenResult, err := app.authService.CreateAPIToken(user.ID, "cli")
if err != nil {
t.Fatalf("CreateAPIToken returned error: %v", err)
}
if err := app.authService.DisableUser(user.ID, true); err != nil {
t.Fatalf("DisableUser returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "blocked.txt", "blocked")
request.Header.Set("Accept", "application/json")
request.Header.Set("Authorization", "Bearer "+tokenResult.Plaintext)
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusUnauthorized {
t.Fatalf("disabled bearer upload status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestInviteHandlerCreatesUserAndMarksInviteUsed(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
invite, err := app.authService.CreateInvite("friend@example.test", services.UserRoleUser, admin.ID, 0)
if err != nil {
t.Fatalf("CreateInvite returned error: %v", err)
}
request := httptest.NewRequest(http.MethodPost, "/invite/"+invite.Token, strings.NewReader("username=friend&password=password123"))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.SetPathValue("token", invite.Token)
response := httptest.NewRecorder()
app.InvitePost(response, request)
if response.Code != http.StatusSeeOther {
t.Fatalf("InvitePost status = %d, body = %s", response.Code, response.Body.String())
}
if _, err := app.authService.AcceptInvite(invite.Token, "friend", "password123"); err == nil {
t.Fatalf("invite token remained reusable")
}
}
func TestNonOwnerCannotManageOwnedBox(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
owner, err := app.authService.CreateBootstrapUser("owner", "owner@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
invite, err := app.authService.CreateInvite("other@example.test", services.UserRoleUser, owner.ID, 0)
if err != nil {
t.Fatalf("CreateInvite returned error: %v", err)
}
other, err := app.authService.AcceptInvite(invite.Token, "other", "password123")
if err != nil {
t.Fatalf("AcceptInvite returned error: %v", err)
}
result := createOwnedBoxThroughApp(t, app, owner.ID)
if err := app.uploadService.RenameOwnedBox(result.BoxID, other.ID, "stolen"); err == nil {
t.Fatalf("RenameOwnedBox allowed non-owner")
}
}
func TestAdminUploadBypassesMaxUploadSize(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
_, token, err := app.authService.Login("admin@example.test", "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", int(app.uploadService.MaxUploadSize())+1))
request.Header.Set("Accept", "application/json")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusCreated {
t.Fatalf("admin upload status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestUnlimitedAnonymousUploadPolicyUsesNegativeOne(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy, err := app.settingsService.UploadPolicy()
if err != nil {
t.Fatalf("UploadPolicy returned error: %v", err)
}
policy.AnonymousMaxUploadMB = -1
policy.AnonymousDailyUploadMB = -1
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", int(app.uploadService.MaxUploadSize())+1))
request.Header.Set("Accept", "application/json")
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusCreated {
t.Fatalf("unlimited anonymous upload status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestAnonymousUploadDisabled(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousUploadsEnabled = false
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "note.txt", "hello")
request.Header.Set("Accept", "application/json")
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusForbidden {
t.Fatalf("status = %d, want 403, body = %s", response.Code, response.Body.String())
}
}
func TestAnonymousUploadLimits(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousMaxUploadMB = 1
policy.AnonymousDailyUploadMB = 0.001
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
large := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", 2*1024*1024))
large.Header.Set("Accept", "application/json")
large.RemoteAddr = "192.0.2.10:1234"
largeResponse := httptest.NewRecorder()
app.Upload(largeResponse, large)
if largeResponse.Code != http.StatusRequestEntityTooLarge {
t.Fatalf("large status = %d, body = %s", largeResponse.Code, largeResponse.Body.String())
}
daily := multipartUploadRequest(t, "/api/v1/upload", "file", "note.txt", strings.Repeat("x", 2048))
daily.Header.Set("Accept", "application/json")
daily.RemoteAddr = "192.0.2.10:1234"
dailyResponse := httptest.NewRecorder()
app.Upload(dailyResponse, daily)
if dailyResponse.Code != http.StatusTooManyRequests {
t.Fatalf("daily status = %d, body = %s", dailyResponse.Code, dailyResponse.Body.String())
}
}
func TestSignedInUploadQuotaAndOverride(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
user, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, user.ID, 0)
if err != nil {
t.Fatalf("CreateInvite returned error: %v", err)
}
normal, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
if err != nil {
t.Fatalf("AcceptInvite returned error: %v", err)
}
_, token, err := app.authService.Login(normal.Email, "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
policy := testPolicy(t, app)
policy.DefaultUserStorageMB = 0.001
policy.UserDailyUploadMB = 8
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "quota.txt", strings.Repeat("x", 2048))
request.Header.Set("Accept", "application/json")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusRequestEntityTooLarge {
t.Fatalf("quota status = %d, body = %s", response.Code, response.Body.String())
}
override := 10.0
if err := app.authService.SetUserStorageQuota(normal.ID, &override); err != nil {
t.Fatalf("SetUserStorageQuota returned error: %v", err)
}
request = multipartUploadRequest(t, "/api/v1/upload", "file", "quota.txt", strings.Repeat("x", 2048))
request.Header.Set("Accept", "application/json")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response = httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusCreated {
t.Fatalf("override status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestSignedInDailyCap(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, admin.ID, 0)
if err != nil {
t.Fatalf("CreateInvite returned error: %v", err)
}
user, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
if err != nil {
t.Fatalf("AcceptInvite returned error: %v", err)
}
_, token, err := app.authService.Login(user.Email, "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
policy := testPolicy(t, app)
policy.UserDailyUploadMB = 0.001
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "daily.txt", strings.Repeat("x", 2048))
request.Header.Set("Accept", "application/json")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusTooManyRequests {
t.Fatalf("daily status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestLayeredUploadLimits(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousDailyBoxes = 1
policy.AnonymousActiveBoxes = 10
policy.AnonymousMaxDays = 3
policy.LocalStorageMaxGB = 0.001
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
first := uploadThroughApp(t, app)
if first.BoxID == "" {
t.Fatalf("first upload did not return a box id")
}
secondRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "second.txt", "hello")
secondRequest.Header.Set("Accept", "application/json")
secondResponse := httptest.NewRecorder()
app.Upload(secondResponse, secondRequest)
if secondResponse.Code != http.StatusTooManyRequests {
t.Fatalf("daily box status = %d, body = %s", secondResponse.Code, secondResponse.Body.String())
}
policy.AnonymousDailyBoxes = 10
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
expiryRequest := multipartUploadRequestWithField(t, "/api/v1/upload", "file", "expiry.txt", "hello", "max_days", "30")
expiryRequest.Header.Set("Accept", "application/json")
expiryResponse := httptest.NewRecorder()
app.Upload(expiryResponse, expiryRequest)
if expiryResponse.Code != http.StatusRequestEntityTooLarge && expiryResponse.Code != http.StatusTooManyRequests {
t.Fatalf("expiry/box status = %d, body = %s", expiryResponse.Code, expiryResponse.Body.String())
}
}
func TestBatchedUploadAppendBypassesDailyBoxCreationCap(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousDailyBoxes = 1
policy.AnonymousActiveBoxes = 10
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
first := multipartUploadRequest(t, "/api/v1/upload", "file", "first.txt", "hello")
first.Header.Set("Accept", "application/json")
first.Header.Set(uploadBatchHeader, "sharex-test")
firstResponse := httptest.NewRecorder()
app.Upload(firstResponse, first)
if firstResponse.Code != http.StatusCreated {
t.Fatalf("first batched status = %d, body = %s", firstResponse.Code, firstResponse.Body.String())
}
second := multipartUploadRequest(t, "/api/v1/upload", "file", "second.txt", "hello")
second.Header.Set("Accept", "application/json")
second.Header.Set(uploadBatchHeader, "sharex-test")
secondResponse := httptest.NewRecorder()
app.Upload(secondResponse, second)
if secondResponse.Code != http.StatusCreated {
t.Fatalf("second batched status = %d, body = %s", secondResponse.Code, secondResponse.Body.String())
}
third := multipartUploadRequest(t, "/api/v1/upload", "file", "third.txt", "hello")
third.Header.Set("Accept", "application/json")
thirdResponse := httptest.NewRecorder()
app.Upload(thirdResponse, third)
if thirdResponse.Code != http.StatusTooManyRequests {
t.Fatalf("non-batched status = %d, body = %s", thirdResponse.Code, thirdResponse.Body.String())
}
}
func TestBatchedUploadAppendBypassesActiveBoxCreationCap(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousDailyBoxes = 10
policy.AnonymousActiveBoxes = 1
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
first := multipartUploadRequest(t, "/api/v1/upload", "file", "first.txt", "hello")
first.Header.Set("Accept", "application/json")
first.Header.Set(uploadBatchHeader, "active-cap")
firstResponse := httptest.NewRecorder()
app.Upload(firstResponse, first)
if firstResponse.Code != http.StatusCreated {
t.Fatalf("first batched status = %d, body = %s", firstResponse.Code, firstResponse.Body.String())
}
second := multipartUploadRequest(t, "/api/v1/upload", "file", "second.txt", "hello")
second.Header.Set("Accept", "application/json")
second.Header.Set(uploadBatchHeader, "active-cap")
secondResponse := httptest.NewRecorder()
app.Upload(secondResponse, second)
if secondResponse.Code != http.StatusCreated {
t.Fatalf("second batched status = %d, body = %s", secondResponse.Code, secondResponse.Body.String())
}
third := multipartUploadRequest(t, "/api/v1/upload", "file", "third.txt", "hello")
third.Header.Set("Accept", "application/json")
thirdResponse := httptest.NewRecorder()
app.Upload(thirdResponse, third)
if thirdResponse.Code != http.StatusTooManyRequests {
t.Fatalf("non-batched status = %d, body = %s", thirdResponse.Code, thirdResponse.Body.String())
}
}
func TestUserPolicyOverrideChangesUploadEnforcement(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, admin.ID, 0)
if err != nil {
t.Fatalf("CreateInvite returned error: %v", err)
}
user, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
if err != nil {
t.Fatalf("AcceptInvite returned error: %v", err)
}
dailyBoxes := 1
maxDays := 1
if err := app.authService.SetUserPolicy(user.ID, services.UserPolicy{DailyBoxes: &dailyBoxes, MaxDays: &maxDays}); err != nil {
t.Fatalf("SetUserPolicy returned error: %v", err)
}
_, token, err := app.authService.Login(user.Email, "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
first := multipartUploadRequest(t, "/api/v1/upload", "file", "one.txt", "hello")
first.Header.Set("Accept", "application/json")
first.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
firstResponse := httptest.NewRecorder()
app.Upload(firstResponse, first)
if firstResponse.Code != http.StatusCreated {
t.Fatalf("first status = %d, body = %s", firstResponse.Code, firstResponse.Body.String())
}
second := multipartUploadRequest(t, "/api/v1/upload", "file", "two.txt", "hello")
second.Header.Set("Accept", "application/json")
second.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
secondResponse := httptest.NewRecorder()
app.Upload(secondResponse, second)
if secondResponse.Code != http.StatusTooManyRequests {
t.Fatalf("second status = %d, body = %s", secondResponse.Code, secondResponse.Body.String())
}
}
func TestLocalStorageCapRejectsUpload(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousMaxUploadMB = 4
policy.AnonymousDailyUploadMB = 8
policy.LocalStorageMaxGB = 0.001
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", 2*1024*1024))
request.Header.Set("Accept", "application/json")
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusRequestEntityTooLarge {
t.Fatalf("storage cap status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestAdminSettingsPostChangesUploadEnforcement(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
_, token, err := app.authService.Login("admin@example.test", "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
settingsForm := strings.NewReader("anonymous_max_upload_mb=512&anonymous_daily_upload_mb=2048&user_daily_upload_mb=8192&default_user_storage_mb=51200&usage_retention_days=30&csrf_token=test-csrf")
settingsRequest := httptest.NewRequest(http.MethodPost, "/admin/settings", settingsForm)
settingsRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
settingsRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
settingsRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
settingsResponse := httptest.NewRecorder()
app.AdminSettingsPost(settingsResponse, settingsRequest)
if settingsResponse.Code != http.StatusSeeOther {
t.Fatalf("AdminSettingsPost status = %d, body = %s", settingsResponse.Code, settingsResponse.Body.String())
}
uploadRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "note.txt", "hello")
uploadRequest.Header.Set("Accept", "application/json")
uploadResponse := httptest.NewRecorder()
app.Upload(uploadResponse, uploadRequest)
if uploadResponse.Code != http.StatusForbidden {
t.Fatalf("upload status = %d, want 403, body = %s", uploadResponse.Code, uploadResponse.Body.String())
}
}
func TestAdminUserQuotaPostChangesEnforcement(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, admin.ID, 0)
if err != nil {
t.Fatalf("CreateInvite returned error: %v", err)
}
user, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
if err != nil {
t.Fatalf("AcceptInvite returned error: %v", err)
}
_, adminToken, err := app.authService.Login(admin.Email, "password123")
if err != nil {
t.Fatalf("admin Login returned error: %v", err)
}
quotaRequest := httptest.NewRequest(http.MethodPost, "/admin/users/"+user.ID+"/quota", strings.NewReader("storage_quota_mb=0.001&csrf_token=test-csrf"))
quotaRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
quotaRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
quotaRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
quotaRequest.SetPathValue("userID", user.ID)
quotaResponse := httptest.NewRecorder()
app.AdminUpdateUserQuota(quotaResponse, quotaRequest)
if quotaResponse.Code != http.StatusSeeOther {
t.Fatalf("AdminUpdateUserQuota status = %d, body = %s", quotaResponse.Code, quotaResponse.Body.String())
}
_, userToken, err := app.authService.Login(user.Email, "password123")
if err != nil {
t.Fatalf("user Login returned error: %v", err)
}
uploadRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "quota.txt", strings.Repeat("x", 2048))
uploadRequest.Header.Set("Accept", "application/json")
uploadRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: userToken})
uploadResponse := httptest.NewRecorder()
app.Upload(uploadResponse, uploadRequest)
if uploadResponse.Code != http.StatusRequestEntityTooLarge {
t.Fatalf("upload status = %d, want 413, body = %s", uploadResponse.Code, uploadResponse.Body.String())
}
}
func TestHomeReflectsUploadPolicySettings(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
policy := testPolicy(t, app)
policy.AnonymousMaxUploadMB = 123
policy.AnonymousDailyUploadMB = 456
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
}
request := httptest.NewRequest(http.MethodGet, "/", nil)
response := httptest.NewRecorder()
app.Home(response, request)
if response.Code != http.StatusOK {
t.Fatalf("Home status = %d", response.Code)
}
body := response.Body.String()
if !strings.Contains(body, "Max file size: 123 MB") || !strings.Contains(body, "456 MB") {
t.Fatalf("home did not reflect policy settings: %s", body)
}
if !strings.Contains(body, "warpbox.dev · test ·") {
t.Fatalf("home footer did not include app version: %s", body)
}
}
func TestAPIDocsHeaderReflectsLoggedInUser(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
_, token, err := app.authService.Login("admin@example.test", "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
request := httptest.NewRequest(http.MethodGet, "/api", nil)
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response := httptest.NewRecorder()
app.APIDocs(response, request)
if response.Code != http.StatusOK {
t.Fatalf("APIDocs status = %d", response.Code)
}
body := response.Body.String()
header := body[:strings.Index(body, "<main")]
if !strings.Contains(header, "Dashboard") || strings.Contains(header, "Sign in") || strings.Contains(header, "Health") {
t.Fatalf("api header did not reflect logged-in state: %s", body)
}
}
func TestAPIDocsHeaderReflectsLoggedOutUser(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
request := httptest.NewRequest(http.MethodGet, "/api", nil)
response := httptest.NewRecorder()
app.APIDocs(response, request)
if response.Code != http.StatusOK {
t.Fatalf("APIDocs status = %d", response.Code)
}
body := response.Body.String()
header := body[:strings.Index(body, "<main")]
if !strings.Contains(header, "Sign in") || !strings.Contains(header, ">API<") || strings.Contains(header, "Health") || strings.Contains(header, "Dashboard") {
t.Fatalf("api header did not reflect logged-out state: %s", body)
}
}
func TestAdminStorageProviderPagesOnlyRenderRelevantFields(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
request := httptest.NewRequest(http.MethodGet, "/admin/storage/new/sftp", nil)
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
response := httptest.NewRecorder()
app.AdminNewStorageProvider(response, request)
if response.Code != http.StatusOK {
t.Fatalf("AdminNewStorageProvider status = %d, body = %s", response.Code, response.Body.String())
}
body := response.Body.String()
if !strings.Contains(body, "Private key") || !strings.Contains(body, "SSH host key") {
t.Fatalf("sftp page did not render sftp fields: %s", body)
}
for _, unwanted := range []string{"Bucket display name", "WebDAV URL", "Share</span>", "Access key"} {
if strings.Contains(body, unwanted) {
t.Fatalf("sftp page rendered irrelevant field %q: %s", unwanted, body)
}
}
cfg, err := app.uploadService.Storage().CreateBackend(services.StorageBackendConfig{
Provider: services.StorageProviderSFTP,
Name: "NAS",
Host: "files.example.test",
Username: "warpbox",
Password: "secret",
})
if err != nil {
t.Fatalf("CreateBackend returned error: %v", err)
}
editRequest := httptest.NewRequest(http.MethodGet, "/admin/storage/"+cfg.ID+"/edit", nil)
editRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
editRequest.SetPathValue("backendID", cfg.ID)
editResponse := httptest.NewRecorder()
app.AdminEditStorageForm(editResponse, editRequest)
if editResponse.Code != http.StatusOK {
t.Fatalf("AdminEditStorageForm status = %d, body = %s", editResponse.Code, editResponse.Body.String())
}
editBody := editResponse.Body.String()
if !strings.Contains(editBody, "Immutable provider") || strings.Contains(editBody, "Bucket display name") || strings.Contains(editBody, "WebDAV URL") {
t.Fatalf("edit page did not stay provider-specific: %s", editBody)
}
}
func TestAdminStorageEditRejectsProviderMutation(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
cfg, err := app.uploadService.Storage().CreateBackend(services.StorageBackendConfig{
Provider: services.StorageProviderSFTP,
Name: "NAS",
Host: "files.example.test",
Username: "warpbox",
Password: "secret",
})
if err != nil {
t.Fatalf("CreateBackend returned error: %v", err)
}
form := strings.NewReader("provider=s3&name=Changed&endpoint=https://s3.example.test&bucket=bucket&access_key=access&secret_key=secret&use_ssl=on&csrf_token=test-csrf")
request := httptest.NewRequest(http.MethodPost, "/admin/storage/"+cfg.ID+"/edit", form)
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
request.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
request.SetPathValue("backendID", cfg.ID)
response := httptest.NewRecorder()
app.AdminEditStorage(response, request)
if response.Code != http.StatusSeeOther {
t.Fatalf("AdminEditStorage status = %d, body = %s", response.Code, response.Body.String())
}
stored, err := app.uploadService.Storage().BackendConfig(cfg.ID)
if err != nil {
t.Fatalf("BackendConfig returned error: %v", err)
}
if stored.Provider != services.StorageProviderSFTP || stored.Type != services.StorageBackendSFTP || stored.Name != "NAS" {
t.Fatalf("storage backend mutated despite rejected provider change: %+v", stored)
}
}
func TestAdminStorageJobRoutesRequireAdminAndCSRF(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
unauthorized := httptest.NewRecorder()
app.AdminRunStorageCleanup(unauthorized, httptest.NewRequest(http.MethodPost, "/admin/storage/jobs/cleanup", nil))
if unauthorized.Code != http.StatusSeeOther {
t.Fatalf("unauthorized cleanup status = %d", unauthorized.Code)
}
adminToken := createAdminSession(t, app)
missingCSRFRequest := httptest.NewRequest(http.MethodPost, "/admin/storage/jobs/cleanup", nil)
missingCSRFRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
missingCSRFResponse := httptest.NewRecorder()
app.AdminRunStorageCleanup(missingCSRFResponse, missingCSRFRequest)
if missingCSRFResponse.Code != http.StatusForbidden {
t.Fatalf("missing csrf cleanup status = %d", missingCSRFResponse.Code)
}
request := httptest.NewRequest(http.MethodPost, "/admin/storage/jobs/cleanup", strings.NewReader("csrf_token=test-csrf"))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
request.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
response := httptest.NewRecorder()
app.AdminRunStorageCleanup(response, request)
if response.Code != http.StatusSeeOther {
t.Fatalf("authorized cleanup status = %d, body = %s", response.Code, response.Body.String())
}
}
func TestAdminStorageSpeedTestStartsBackgroundJob(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
if _, err := app.uploadService.Storage().TestBackend(services.StorageBackendLocal); err != nil {
t.Fatalf("TestBackend returned error: %v", err)
}
request := httptest.NewRequest(http.MethodPost, "/admin/storage/local/speed-test", strings.NewReader("mode=custom&custom_file_count=2&custom_file_size_mb=0.001&csrf_token=test-csrf"))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
request.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
request.SetPathValue("backendID", services.StorageBackendLocal)
response := httptest.NewRecorder()
app.AdminStartStorageSpeedTest(response, request)
if response.Code != http.StatusSeeOther {
t.Fatalf("AdminStartStorageSpeedTest status = %d, body = %s", response.Code, response.Body.String())
}
tests, err := app.uploadService.Storage().ListSpeedTests(services.StorageBackendLocal, 10)
if err != nil {
t.Fatalf("ListSpeedTests returned error: %v", err)
}
if len(tests) != 1 {
t.Fatalf("speed tests len = %d, want 1", len(tests))
}
if tests[0].Mode != services.StorageSpeedModeCustom || tests[0].CustomFileCount != 2 || tests[0].CustomFileSizeMB != 0.001 {
t.Fatalf("custom speed test options were not stored: %+v", tests[0])
}
location := response.Header().Get("Location")
if !strings.Contains(location, "/admin/storage/local/tests") {
t.Fatalf("speed test redirect location = %q", location)
}
}
func TestAdminStorageTestingPageRendersHistory(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
if _, err := app.uploadService.Storage().TestBackend(services.StorageBackendLocal); err != nil {
t.Fatalf("TestBackend returned error: %v", err)
}
test, err := app.uploadService.Storage().StartSpeedTest(services.StorageBackendLocal, services.StorageSpeedModeSmall)
if err != nil {
t.Fatalf("StartSpeedTest returned error: %v", err)
}
app.uploadService.Storage().RunSpeedTest(context.Background(), test.ID)
request := httptest.NewRequest(http.MethodGet, "/admin/storage/local/tests", nil)
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
request.SetPathValue("backendID", services.StorageBackendLocal)
response := httptest.NewRecorder()
app.AdminStorageTests(response, request)
if response.Code != http.StatusOK {
t.Fatalf("AdminStorageTests status = %d, body = %s", response.Code, response.Body.String())
}
body := response.Body.String()
if !strings.Contains(body, "New Test") || !strings.Contains(body, "Many small files") || strings.Contains(body, "storage-test-menu") {
t.Fatalf("testing page missing expected page-based controls: %s", body)
}
jsonRequest := httptest.NewRequest(http.MethodGet, "/admin/storage/local/tests.json", nil)
jsonRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
jsonRequest.SetPathValue("backendID", services.StorageBackendLocal)
jsonResponse := httptest.NewRecorder()
app.AdminStorageTestsJSON(jsonResponse, jsonRequest)
if jsonResponse.Code != http.StatusOK {
t.Fatalf("AdminStorageTestsJSON status = %d, body = %s", jsonResponse.Code, jsonResponse.Body.String())
}
if !strings.Contains(jsonResponse.Body.String(), `"progress":100`) || !strings.Contains(jsonResponse.Body.String(), `"stage":"complete"`) {
t.Fatalf("tests json missing progress fields: %s", jsonResponse.Body.String())
}
}
func TestAdminLogsAndBansPagesRender(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
logDir := filepath.Join(app.cfg.DataDir, "logs")
if err := os.MkdirAll(logDir, 0o755); err != nil {
t.Fatalf("MkdirAll returned error: %v", err)
}
logPath := filepath.Join(logDir, "2026-05-31.log")
line := `{"date":"2026-05-31","time":"12:34:56","source":"user-upload","severity":"user_activity","code":2001,"log":"upload response sent","ip":"127.0.0.1","box_id":"box123"}` + "\n"
if err := os.WriteFile(logPath, []byte(line), 0o644); err != nil {
t.Fatalf("WriteFile returned error: %v", err)
}
logsRequest := httptest.NewRequest(http.MethodGet, "/admin/logs?q=box123", nil)
logsRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
logsResponse := httptest.NewRecorder()
app.AdminLogs(logsResponse, logsRequest)
if logsResponse.Code != http.StatusOK {
t.Fatalf("AdminLogs status = %d, body = %s", logsResponse.Code, logsResponse.Body.String())
}
logsBody := logsResponse.Body.String()
if !strings.Contains(logsBody, "upload response sent") || !strings.Contains(logsBody, "box123") {
t.Fatalf("AdminLogs missing expected log entry: %s", logsBody)
}
bansRequest := httptest.NewRequest(http.MethodGet, "/admin/bans", nil)
bansRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
bansResponse := httptest.NewRecorder()
app.AdminBans(bansResponse, bansRequest)
if bansResponse.Code != http.StatusOK {
t.Fatalf("AdminBans status = %d, body = %s", bansResponse.Code, bansResponse.Body.String())
}
if !strings.Contains(bansResponse.Body.String(), "Manual ban") || !strings.Contains(bansResponse.Body.String(), "Auto-ban settings") {
t.Fatalf("AdminBans missing ban controls: %s", bansResponse.Body.String())
}
}
func TestAdminCanCreateAndUnbanIPBan(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
expiresAt := time.Now().Add(24 * time.Hour).Format("2006-01-02T15:04")
request := httptest.NewRequest(http.MethodPost, "/admin/bans", strings.NewReader("target=203.0.113.90&reason=test&expires_at="+expiresAt+"&csrf_token=test-csrf"))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
request.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
response := httptest.NewRecorder()
app.AdminCreateBan(response, request)
if response.Code != http.StatusSeeOther {
t.Fatalf("AdminCreateBan status = %d, body = %s", response.Code, response.Body.String())
}
records, err := app.banService.ListBans()
if err != nil {
t.Fatalf("ListBans returned error: %v", err)
}
if len(records) != 1 || records[0].Normalized != "203.0.113.90" {
t.Fatalf("records = %+v", records)
}
unbanRequest := httptest.NewRequest(http.MethodPost, "/admin/bans/"+records[0].ID+"/unban", strings.NewReader("csrf_token=test-csrf"))
unbanRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
unbanRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
unbanRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
unbanRequest.SetPathValue("banID", records[0].ID)
unbanResponse := httptest.NewRecorder()
app.AdminUnban(unbanResponse, unbanRequest)
if unbanResponse.Code != http.StatusSeeOther {
t.Fatalf("AdminUnban status = %d, body = %s", unbanResponse.Code, unbanResponse.Body.String())
}
if _, ok, err := app.banService.Match("203.0.113.90", time.Now().UTC()); err != nil || ok {
t.Fatalf("unbanned Match = %v, %v", ok, err)
}
}
func TestAdminCanUpdateBanSettingsAndRules(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
adminToken := createAdminSession(t, app)
settingsRequest := httptest.NewRequest(http.MethodPost, "/admin/bans/settings", strings.NewReader("auto_ban_enabled=on&auto_ban_duration_hours=48&abuse_window_hours=12&malicious_path_threshold=2&admin_login_failure_threshold=4&user_login_failure_threshold=5&csrf_token=test-csrf"))
settingsRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
settingsRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
settingsRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
settingsResponse := httptest.NewRecorder()
app.AdminBanSettingsPost(settingsResponse, settingsRequest)
if settingsResponse.Code != http.StatusSeeOther {
t.Fatalf("AdminBanSettingsPost status = %d, body = %s", settingsResponse.Code, settingsResponse.Body.String())
}
settings, err := app.banService.Settings()
if err != nil {
t.Fatalf("Settings returned error: %v", err)
}
if !settings.AutoBanEnabled || settings.AutoBanDurationHours != 48 || settings.MaliciousPathThreshold != 2 {
t.Fatalf("settings = %+v", settings)
}
rulesRequest := httptest.NewRequest(http.MethodPost, "/admin/bans/rules", strings.NewReader("patterns=%2Fcustom-one%0A%2Fcustom-two&csrf_token=test-csrf"))
rulesRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rulesRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
rulesRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
rulesResponse := httptest.NewRecorder()
app.AdminBanRulesPost(rulesResponse, rulesRequest)
if rulesResponse.Code != http.StatusSeeOther {
t.Fatalf("AdminBanRulesPost status = %d, body = %s", rulesResponse.Code, rulesResponse.Body.String())
}
if pattern, err := app.banService.MaliciousPattern("/x/custom-two"); err != nil || pattern != "/custom-two" {
t.Fatalf("MaliciousPattern = %q, %v", pattern, err)
}
}
func TestLoginFailuresCreateAutoBanWhenEnabled(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
settings, err := app.banService.Settings()
if err != nil {
t.Fatalf("Settings returned error: %v", err)
}
settings.AutoBanEnabled = true
settings.UserLoginFailureThreshold = 2
if err := app.banService.UpdateSettings(settings); err != nil {
t.Fatalf("UpdateSettings returned error: %v", err)
}
for i := 0; i < 2; i++ {
request := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader("email=admin@example.test&password=wrong"))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.RemoteAddr = "203.0.113.91:1234"
response := httptest.NewRecorder()
app.LoginPost(response, request)
if response.Code != http.StatusUnauthorized {
t.Fatalf("LoginPost status = %d", response.Code)
}
}
if _, ok, err := app.banService.Match("203.0.113.91", time.Now().UTC()); err != nil || !ok {
t.Fatalf("Match after login failures = %v, %v", ok, err)
}
}
func TestAdminLoginFailuresCreateAutoBanWhenEnabled(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
settings, err := app.banService.Settings()
if err != nil {
t.Fatalf("Settings returned error: %v", err)
}
settings.AutoBanEnabled = true
settings.AdminLoginFailureThreshold = 2
if err := app.banService.UpdateSettings(settings); err != nil {
t.Fatalf("UpdateSettings returned error: %v", err)
}
app.cfg.AdminToken = "correct-token"
for i := 0; i < 2; i++ {
request := httptest.NewRequest(http.MethodPost, "/admin/login", strings.NewReader("token=wrong"))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.RemoteAddr = "203.0.113.92:1234"
response := httptest.NewRecorder()
app.AdminLoginPost(response, request)
if response.Code != http.StatusUnauthorized {
t.Fatalf("AdminLoginPost status = %d", response.Code)
}
}
if _, ok, err := app.banService.Match("203.0.113.92", time.Now().UTC()); err != nil || !ok {
t.Fatalf("Match after admin login failures = %v, %v", ok, err)
}
}
func createOwnedBoxThroughApp(t *testing.T, app *App, userID string) services.UploadResult {
t.Helper()
user, err := app.authService.UserByID(userID)
if err != nil {
t.Fatalf("UserByID returned error: %v", err)
}
_, token, err := app.authService.Login(user.Email, "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "owned.txt", "owned")
request.Header.Set("Accept", "application/json")
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusCreated {
t.Fatalf("upload status = %d, body = %s", response.Code, response.Body.String())
}
var payload services.UploadResult
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
t.Fatalf("json.Unmarshal returned error: %v", err)
}
return payload
}
func createAdminSession(t *testing.T, app *App) string {
t.Helper()
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
if err != nil && !strings.Contains(err.Error(), "registration is closed") {
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
_, token, err := app.authService.Login("admin@example.test", "password123")
if err != nil {
t.Fatalf("Login returned error: %v", err)
}
return token
}
func testPolicy(t *testing.T, app *App) services.UploadPolicySettings {
t.Helper()
policy, err := app.settingsService.UploadPolicy()
if err != nil {
t.Fatalf("UploadPolicy returned error: %v", err)
}
return policy
}