docs: expand configuration docs for admin and BadgerDB
Update README to explain startup config precedence (defaults/env/admin overrides), document admin/bootstrap and feature toggles, and clarify storage locations under WARPBOX_DATA_DIR including BadgerDB metadata. Also refresh project layout to include new config and metastore packages.docs: expand configuration docs for admin and BadgerDB Update README to explain startup config precedence (defaults/env/admin overrides), document admin/bootstrap and feature toggles, and clarify storage locations under WARPBOX_DATA_DIR including BadgerDB metadata. Also refresh project layout to include new config and metastore packages.
This commit is contained in:
222
lib/metastore/metastore_test.go
Normal file
222
lib/metastore/metastore_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package metastore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"warpbox/lib/config"
|
||||
)
|
||||
|
||||
func TestOpenClose(t *testing.T) {
|
||||
store, err := Open(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatalf("Open returned error: %v", err)
|
||||
}
|
||||
if err := store.Close(); err != nil {
|
||||
t.Fatalf("Close returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBootstrapAdminFromPassword(t *testing.T) {
|
||||
clearMetastoreConfigEnv(t)
|
||||
t.Setenv("WARPBOX_ADMIN_PASSWORD", "secret-pass")
|
||||
t.Setenv("WARPBOX_ADMIN_EMAIL", "admin@example.test")
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load returned error: %v", err)
|
||||
}
|
||||
store := openTestStore(t)
|
||||
|
||||
result, err := BootstrapAdmin(cfg, store)
|
||||
if err != nil {
|
||||
t.Fatalf("BootstrapAdmin returned error: %v", err)
|
||||
}
|
||||
if !result.AdminLoginEnabled {
|
||||
t.Fatal("expected admin login to be enabled")
|
||||
}
|
||||
if !result.AdminTag.Protected {
|
||||
t.Fatal("expected admin tag to be protected")
|
||||
}
|
||||
if result.AdminUser == nil {
|
||||
t.Fatal("expected bootstrap admin user")
|
||||
}
|
||||
if !hasString(result.AdminUser.TagIDs, result.AdminTag.ID) {
|
||||
t.Fatal("expected bootstrap admin to have admin tag")
|
||||
}
|
||||
if !VerifyPassword(result.AdminUser.PasswordHash, "secret-pass") {
|
||||
t.Fatal("expected bootstrap admin password to verify")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBootstrapAdminDisabledWithoutPassword(t *testing.T) {
|
||||
clearMetastoreConfigEnv(t)
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load returned error: %v", err)
|
||||
}
|
||||
store := openTestStore(t)
|
||||
|
||||
result, err := BootstrapAdmin(cfg, store)
|
||||
if err != nil {
|
||||
t.Fatalf("BootstrapAdmin returned error: %v", err)
|
||||
}
|
||||
if result.AdminLoginEnabled {
|
||||
t.Fatal("expected admin login to be disabled without password or existing admin")
|
||||
}
|
||||
if !result.AdminTag.Protected {
|
||||
t.Fatal("expected admin tag to still be created")
|
||||
}
|
||||
users, err := store.ListUsers()
|
||||
if err != nil {
|
||||
t.Fatalf("ListUsers returned error: %v", err)
|
||||
}
|
||||
if len(users) != 0 {
|
||||
t.Fatalf("expected no users, got %d", len(users))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDuplicateUsersAndTags(t *testing.T) {
|
||||
store := openTestStore(t)
|
||||
|
||||
if _, err := store.CreateUserWithPassword("alex", "alex@example.test", "secret", nil); err != nil {
|
||||
t.Fatalf("CreateUserWithPassword returned error: %v", err)
|
||||
}
|
||||
if _, err := store.CreateUserWithPassword("Alex", "other@example.test", "secret", nil); !errors.Is(err, ErrDuplicate) {
|
||||
t.Fatalf("expected duplicate username error, got %v", err)
|
||||
}
|
||||
if _, err := store.CreateUserWithPassword("other", "alex@example.test", "secret", nil); !errors.Is(err, ErrDuplicate) {
|
||||
t.Fatalf("expected duplicate email error, got %v", err)
|
||||
}
|
||||
|
||||
tag := Tag{Name: "staff"}
|
||||
if err := store.CreateTag(&tag); err != nil {
|
||||
t.Fatalf("CreateTag returned error: %v", err)
|
||||
}
|
||||
duplicate := Tag{Name: "Staff"}
|
||||
if err := store.CreateTag(&duplicate); !errors.Is(err, ErrDuplicate) {
|
||||
t.Fatalf("expected duplicate tag error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPermissionResolutionAndGlobalCaps(t *testing.T) {
|
||||
clearMetastoreConfigEnv(t)
|
||||
t.Setenv("WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_BYTES", "50")
|
||||
t.Setenv("WARPBOX_GLOBAL_MAX_FILE_SIZE_BYTES", "100")
|
||||
t.Setenv("WARPBOX_GLOBAL_MAX_BOX_SIZE_BYTES", "1000")
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load returned error: %v", err)
|
||||
}
|
||||
tagFileLimit := int64(80)
|
||||
tagBoxLimit := int64(2000)
|
||||
userFileLimit := int64(60)
|
||||
user := User{MaxFileSizeBytes: &userFileLimit}
|
||||
tags := []Tag{
|
||||
{
|
||||
Permissions: TagPermissions{
|
||||
UploadAllowed: true,
|
||||
AllowedExpirySeconds: []int64{3600, 600},
|
||||
MaxFileSizeBytes: &tagFileLimit,
|
||||
MaxBoxSizeBytes: &tagBoxLimit,
|
||||
ZipDownloadAllowed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
perms := ResolveUserPermissions(cfg, user, tags)
|
||||
if !perms.UploadAllowed || !perms.ZipDownloadAllowed {
|
||||
t.Fatal("expected tag booleans to grant permissions")
|
||||
}
|
||||
if perms.MaxFileSizeBytes != 80 {
|
||||
t.Fatalf("expected tag limit to beat user/default limit, got %d", perms.MaxFileSizeBytes)
|
||||
}
|
||||
if perms.MaxBoxSizeBytes != 1000 {
|
||||
t.Fatalf("expected global max box cap, got %d", perms.MaxBoxSizeBytes)
|
||||
}
|
||||
if len(perms.AllowedExpirySeconds) != 2 || perms.AllowedExpirySeconds[0] != 600 || perms.AllowedExpirySeconds[1] != 3600 {
|
||||
t.Fatalf("unexpected expiry durations: %#v", perms.AllowedExpirySeconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingsStorageAndPrecedence(t *testing.T) {
|
||||
clearMetastoreConfigEnv(t)
|
||||
t.Setenv("WARPBOX_API_ENABLED", "true")
|
||||
|
||||
store := openTestStore(t)
|
||||
if err := store.SetSetting(config.SettingAPIEnabled, "false"); err != nil {
|
||||
t.Fatalf("SetSetting returned error: %v", err)
|
||||
}
|
||||
overrides, err := store.ListSettings()
|
||||
if err != nil {
|
||||
t.Fatalf("ListSettings returned error: %v", err)
|
||||
}
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load returned error: %v", err)
|
||||
}
|
||||
if err := cfg.ApplyOverrides(overrides); err != nil {
|
||||
t.Fatalf("ApplyOverrides returned error: %v", err)
|
||||
}
|
||||
if cfg.APIEnabled {
|
||||
t.Fatal("expected stored DB override to beat env")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionExpiry(t *testing.T) {
|
||||
store := openTestStore(t)
|
||||
session, err := store.CreateSession("user-id", time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateSession returned error: %v", err)
|
||||
}
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
if _, ok, err := store.GetSession(session.Token); err != nil || ok {
|
||||
t.Fatalf("expected expired session to be invalid, ok=%v err=%v", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
func openTestStore(t *testing.T) *Store {
|
||||
t.Helper()
|
||||
store, err := Open(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatalf("Open returned error: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = store.Close()
|
||||
})
|
||||
return store
|
||||
}
|
||||
|
||||
func clearMetastoreConfigEnv(t *testing.T) {
|
||||
t.Helper()
|
||||
for _, name := range []string{
|
||||
"WARPBOX_DATA_DIR",
|
||||
"WARPBOX_ADMIN_PASSWORD",
|
||||
"WARPBOX_ADMIN_USERNAME",
|
||||
"WARPBOX_ADMIN_EMAIL",
|
||||
"WARPBOX_ADMIN_ENABLED",
|
||||
"WARPBOX_ALLOW_ADMIN_SETTINGS_OVERRIDE",
|
||||
"WARPBOX_ADMIN_COOKIE_SECURE",
|
||||
"WARPBOX_GUEST_UPLOADS_ENABLED",
|
||||
"WARPBOX_API_ENABLED",
|
||||
"WARPBOX_ZIP_DOWNLOADS_ENABLED",
|
||||
"WARPBOX_ONE_TIME_DOWNLOADS_ENABLED",
|
||||
"WARPBOX_RENEW_ON_ACCESS_ENABLED",
|
||||
"WARPBOX_RENEW_ON_DOWNLOAD_ENABLED",
|
||||
"WARPBOX_DEFAULT_GUEST_EXPIRY_SECONDS",
|
||||
"WARPBOX_MAX_GUEST_EXPIRY_SECONDS",
|
||||
"WARPBOX_GLOBAL_MAX_FILE_SIZE_BYTES",
|
||||
"WARPBOX_GLOBAL_MAX_BOX_SIZE_BYTES",
|
||||
"WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_BYTES",
|
||||
"WARPBOX_DEFAULT_USER_MAX_BOX_SIZE_BYTES",
|
||||
"WARPBOX_SESSION_TTL_SECONDS",
|
||||
"WARPBOX_BOX_POLL_INTERVAL_MS",
|
||||
"WARPBOX_THUMBNAIL_BATCH_SIZE",
|
||||
"WARPBOX_THUMBNAIL_INTERVAL_SECONDS",
|
||||
} {
|
||||
t.Setenv(name, "")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user