Files
warpbox/lib/server/account_boxes_test.go
Daniel Legt e103829870 feat(models): add alert and box models
Adds comprehensive data structures for Alert and Box functionality across models.
2026-04-30 19:45:22 +03:00

221 lines
8.2 KiB
Go

package server
import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"
"warpbox/lib/boxstore"
"warpbox/lib/metastore"
"warpbox/lib/models"
)
func TestAccountBoxesAdminListsBoxes(t *testing.T) {
app, user := setupAccountTestApp(t)
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
createIndexedBox(t, app, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "", "", 10, false)
response := getAccountBoxes(router, session, "/account/boxes")
if response.Code != http.StatusOK {
t.Fatalf("expected boxes page, got %d body=%s", response.Code, response.Body.String())
}
if !strings.Contains(response.Body.String(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {
t.Fatal("expected indexed box in admin list")
}
}
func TestAccountBoxesRegularUserSeesOnlyOwnBoxes(t *testing.T) {
app, _ := setupAccountTestApp(t)
user, err := app.store.CreateUserWithPassword("box-user", "box-user@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
createIndexedBox(t, app, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", user.ID, user.Username, 10, false)
createIndexedBox(t, app, "cccccccccccccccccccccccccccccccc", "other", "other", 20, false)
response := getAccountBoxes(router, session, "/account/boxes")
if response.Code != http.StatusOK {
t.Fatalf("expected boxes page, got %d", response.Code)
}
body := response.Body.String()
if !strings.Contains(body, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") {
t.Fatal("expected own box")
}
if strings.Contains(body, "cccccccccccccccccccccccccccccccc") {
t.Fatal("did not expect other user's box")
}
}
func TestAccountBoxesFiltersSortAndPagination(t *testing.T) {
app, user := setupAccountTestApp(t)
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
createIndexedBox(t, app, "11111111111111111111111111111111", "", "", 10, false)
createIndexedBox(t, app, "22222222222222222222222222222222", "", "", 1000, true)
createIndexedBox(t, app, "33333333333333333333333333333333", "", "", 500, false)
response := getAccountBoxes(router, session, "/account/boxes?flag=password&sort=largest&page_size=25")
if response.Code != http.StatusOK {
t.Fatalf("expected boxes page, got %d", response.Code)
}
body := response.Body.String()
if !strings.Contains(body, "22222222222222222222222222222222") {
t.Fatal("expected password filtered box")
}
if strings.Contains(body, "11111111111111111111111111111111") {
t.Fatal("did not expect unfiltered box")
}
page, err := app.store.ListBoxRecords(metastore.BoxFilters{Sort: "largest"}, metastore.BoxPageRequest{Page: 1, PageSize: 25})
if err != nil {
t.Fatalf("ListBoxRecords returned error: %v", err)
}
if len(page.Rows) != 3 || page.Rows[0].ID != "22222222222222222222222222222222" {
t.Fatalf("expected largest sort first, got %#v", page.Rows)
}
}
func TestAccountBoxesBulkExpireAndDelete(t *testing.T) {
app, user := setupAccountTestApp(t)
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "dddddddddddddddddddddddddddddddd"
createIndexedBox(t, app, id, "", "", 10, false)
values := url.Values{"box_ids": []string{id}}
response := postAccountBoxesForm(router, session, "/account/boxes/bulk/expire", values)
if response.Code != http.StatusSeeOther {
t.Fatalf("expected expire redirect, got %d", response.Code)
}
record, ok, err := app.store.GetBoxRecord(id)
if err != nil || !ok {
t.Fatalf("GetBoxRecord returned ok=%v err=%v", ok, err)
}
if record.ExpiresAt.After(time.Now().UTC()) {
t.Fatal("expected box to be expired")
}
response = postAccountBoxesForm(router, session, "/account/boxes/bulk/delete", values)
if response.Code != http.StatusSeeOther {
t.Fatalf("expected delete redirect, got %d", response.Code)
}
if _, ok, err := app.store.GetBoxRecord(id); err != nil || ok {
t.Fatalf("expected deleted record, ok=%v err=%v", ok, err)
}
if _, err := os.Stat(boxstore.BoxPath(id)); !os.IsNotExist(err) {
t.Fatalf("expected box directory deleted, stat err=%v", err)
}
}
func TestAccountBoxesBulkDeletePermissionDenied(t *testing.T) {
app, _ := setupAccountTestApp(t)
user, err := app.store.CreateUserWithPassword("box-limited", "box-limited@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
createIndexedBox(t, app, id, "other", "other", 10, false)
response := postAccountBoxesForm(router, session, "/account/boxes/bulk/delete", url.Values{"box_ids": []string{id}})
if response.Code != http.StatusForbidden {
t.Fatalf("expected permission denied, got %d", response.Code)
}
}
func TestAccountBoxesBumpExpiryPolicyRejection(t *testing.T) {
app, user := setupAccountTestApp(t)
app.config.BoxOwnerRefreshEnabled = false
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "ffffffffffffffffffffffffffffffff"
createIndexedBox(t, app, id, "", "", 10, false)
response := postAccountBoxesForm(router, session, "/account/boxes/bulk/bump-expiry", url.Values{"box_ids": []string{id}, "bump_seconds": []string{"60"}})
if response.Code != http.StatusForbidden {
t.Fatalf("expected policy rejection, got %d", response.Code)
}
}
func TestAccountBoxesDeleteLargest(t *testing.T) {
app, user := setupAccountTestApp(t)
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
small := "12345123451234512345123451234512"
large := "99999999999999999999999999999999"
createIndexedBox(t, app, small, "", "", 10, false)
createIndexedBox(t, app, large, "", "", 1000, false)
response := postAccountBoxesForm(router, session, "/account/boxes/delete-largest", nil)
if response.Code != http.StatusSeeOther {
t.Fatalf("expected delete-largest redirect, got %d", response.Code)
}
if _, ok, err := app.store.GetBoxRecord(large); err != nil || ok {
t.Fatalf("expected largest deleted, ok=%v err=%v", ok, err)
}
}
func createIndexedBox(t *testing.T, app *App, id string, ownerID string, ownerUsername string, size int64, password bool) {
t.Helper()
if err := os.MkdirAll(boxstore.BoxPath(id), 0755); err != nil {
t.Fatalf("MkdirAll returned error: %v", err)
}
filename := "file-" + id[:4] + ".txt"
if err := os.WriteFile(filepath.Join(boxstore.BoxPath(id), filename), []byte(strings.Repeat("x", int(size))), 0644); err != nil {
t.Fatalf("WriteFile returned error: %v", err)
}
manifest := models.BoxManifest{
OwnerID: ownerID,
OwnerUsername: ownerUsername,
Files: []models.BoxFile{{
ID: "abcdabcdabcdabcd",
Name: filename,
Size: size,
Status: models.FileStatusReady,
}},
CreatedAt: time.Now().UTC().Add(-time.Duration(size) * time.Second),
ExpiresAt: time.Now().UTC().Add(time.Hour),
RetentionSecs: 3600,
}
if password {
manifest.PasswordHash = "hash"
manifest.AuthToken = "token"
}
if err := boxstore.WriteManifest(id, manifest); err != nil {
t.Fatalf("WriteManifest returned error: %v", err)
}
if err := app.store.UpsertBoxRecord(boxRecordFromManifest(id, manifest)); err != nil {
t.Fatalf("UpsertBoxRecord returned error: %v", err)
}
}
func getAccountBoxes(router http.Handler, session metastore.Session, path string) *httptest.ResponseRecorder {
request := httptest.NewRequest(http.MethodGet, path, nil)
request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token})
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
return response
}
func postAccountBoxesForm(router http.Handler, session metastore.Session, path string, values url.Values) *httptest.ResponseRecorder {
if values == nil {
values = url.Values{}
}
values.Set("csrf_token", session.CSRFToken)
request := httptest.NewRequest(http.MethodPost, path, strings.NewReader(values.Encode()))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token})
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
return response
}