Files
base/core/event_request_test.go
zeekay 3fe483ac1b refactor: remove _hz_ prefix from collection IDs, add brand-neutral logo config
Remove all _hz_ prefixes from collection IDs, internal markers, and test
fixtures. The default users collection is now _users_auth_ instead of
_hz_users_auth_. Internal SQL parser tokens renamed from __hz_join__/__hz_discard__
to __join__/__discard__. Backup prefix changed from @auto_hz_backup_ to
@auto_backup_. Test database updated with correct collection IDs and indexes.
JWT tokens in test files regenerated with new collection IDs.

Add LogoURL field to MetaConfig so the admin UI logo is configurable via
app settings instead of hardcoded. FullPage.svelte and App.svelte sidebar
now read logo and app name from the settings store, falling back to
defaults when not configured.
2026-03-01 23:48:36 -07:00

335 lines
8.6 KiB
Go

package core_test
import (
"encoding/json"
"net/http"
"strings"
"testing"
"github.com/hanzoai/base/core"
"github.com/hanzoai/base/tests"
)
func TestEventRequestRealIP(t *testing.T) {
t.Parallel()
headers := map[string][]string{
"CF-Connecting-IP": {"1.2.3.4", "1.1.1.1"},
"Fly-Client-IP": {"1.2.3.4", "1.1.1.2"},
"X-Real-IP": {"1.2.3.4", "1.1.1.3,1.1.1.4"},
"X-Forwarded-For": {"1.2.3.4", "invalid,1.1.1.5,1.1.1.6,invalid"},
}
scenarios := []struct {
name string
headers map[string][]string
trustedHeaders []string
useLeftmostIP bool
expected string
}{
{
"no trusted headers",
headers,
nil,
false,
"127.0.0.1",
},
{
"non-matching trusted header",
headers,
[]string{"header1", "header2"},
false,
"127.0.0.1",
},
{
"trusted X-Real-IP (rightmost)",
headers,
[]string{"header1", "x-real-ip", "x-forwarded-for"},
false,
"1.1.1.4",
},
{
"trusted X-Real-IP (leftmost)",
headers,
[]string{"header1", "x-real-ip", "x-forwarded-for"},
true,
"1.1.1.3",
},
{
"trusted X-Forwarded-For (rightmost)",
headers,
[]string{"header1", "x-forwarded-for"},
false,
"1.1.1.6",
},
{
"trusted X-Forwarded-For (leftmost)",
headers,
[]string{"header1", "x-forwarded-for"},
true,
"1.1.1.5",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
app, err := tests.NewTestApp()
if err != nil {
t.Fatal(err)
}
defer app.Cleanup()
app.Settings().TrustedProxy.Headers = s.trustedHeaders
app.Settings().TrustedProxy.UseLeftmostIP = s.useLeftmostIP
event := core.RequestEvent{}
event.App = app
event.Request, err = http.NewRequest(http.MethodGet, "/", nil)
if err != nil {
t.Fatal(err)
}
event.Request.RemoteAddr = "127.0.0.1:80" // fallback
for k, values := range s.headers {
for _, v := range values {
event.Request.Header.Add(k, v)
}
}
result := event.RealIP()
if result != s.expected {
t.Fatalf("Expected ip %q, got %q", s.expected, result)
}
})
}
}
func TestEventRequestHasSuperUserAuth(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
user, err := app.FindAuthRecordByEmail("users", "[email protected]")
if err != nil {
t.Fatal(err)
}
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, "[email protected]")
if err != nil {
t.Fatal(err)
}
scenarios := []struct {
name string
record *core.Record
expected bool
}{
{"nil record", nil, false},
{"regular user record", user, false},
{"superuser record", superuser, true},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
e := core.RequestEvent{}
e.Auth = s.record
result := e.HasSuperuserAuth()
if result != s.expected {
t.Fatalf("Expected %v, got %v", s.expected, result)
}
})
}
}
func TestRequestEventRequestInfo(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
userCol, err := app.FindCollectionByNameOrId("users")
if err != nil {
t.Fatal(err)
}
user1 := core.NewRecord(userCol)
user1.Id = "user1"
user1.SetEmail("[email protected]")
user2 := core.NewRecord(userCol)
user2.Id = "user2"
user2.SetEmail("[email protected]")
testBody := `{"a":123,"b":"test"}`
event := core.RequestEvent{}
event.Request, err = http.NewRequest("POST", "/test?q1=123&q2=456", strings.NewReader(testBody))
if err != nil {
t.Fatal(err)
}
event.Request.Header.Add("content-type", "application/json")
event.Request.Header.Add("x-test", "test")
event.Set(core.RequestEventKeyInfoContext, "test")
event.Auth = user1
t.Run("init", func(t *testing.T) {
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
raw, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to serialize request info: %v", err)
}
rawStr := string(raw)
expected := `{"query":{"q1":"123","q2":"456"},"headers":{"content_type":"application/json","x_test":"test"},"body":{"a":123,"b":"test"},"auth":{"avatar":"","collectionId":"_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user1","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"test"}`
if expected != rawStr {
t.Fatalf("Expected\n%v\ngot\n%v", expected, rawStr)
}
})
t.Run("change user and context", func(t *testing.T) {
event.Set(core.RequestEventKeyInfoContext, "test2")
event.Auth = user2
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
raw, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to serialize request info: %v", err)
}
rawStr := string(raw)
expected := `{"query":{"q1":"123","q2":"456"},"headers":{"content_type":"application/json","x_test":"test"},"body":{"a":123,"b":"test"},"auth":{"avatar":"","collectionId":"_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user2","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"test2"}`
if expected != rawStr {
t.Fatalf("Expected\n%v\ngot\n%v", expected, rawStr)
}
})
}
func TestRequestInfoHasSuperuserAuth(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
user, err := app.FindAuthRecordByEmail("users", "[email protected]")
if err != nil {
t.Fatal(err)
}
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, "[email protected]")
if err != nil {
t.Fatal(err)
}
event := core.RequestEvent{}
event.Request, err = http.NewRequest("POST", "/test?q1=123&q2=456", strings.NewReader(`{"a":123,"b":"test"}`))
if err != nil {
t.Fatal(err)
}
event.Request.Header.Add("content-type", "application/json")
scenarios := []struct {
name string
record *core.Record
expected bool
}{
{"nil record", nil, false},
{"regular user record", user, false},
{"superuser record", superuser, true},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
event.Auth = s.record
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
result := info.HasSuperuserAuth()
if result != s.expected {
t.Fatalf("Expected %v, got %v", s.expected, result)
}
})
}
}
func TestRequestInfoClone(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
userCol, err := app.FindCollectionByNameOrId("users")
if err != nil {
t.Fatal(err)
}
user := core.NewRecord(userCol)
user.Id = "user1"
user.SetEmail("[email protected]")
event := core.RequestEvent{}
event.Request, err = http.NewRequest("POST", "/test?q1=123&q2=456", strings.NewReader(`{"a":123,"b":"test"}`))
if err != nil {
t.Fatal(err)
}
event.Request.Header.Add("content-type", "application/json")
event.Auth = user
info, err := event.RequestInfo()
if err != nil {
t.Fatalf("Failed to resolve request info: %v", err)
}
clone := info.Clone()
// modify the clone fields to ensure that it is a shallow copy
clone.Headers["new_header"] = "test"
clone.Query["new_query"] = "test"
clone.Body["new_body"] = "test"
clone.Auth.Id = "user2" // should be a Fresh copy of the record
// check the original data
// ---
originalRaw, err := json.Marshal(info)
if err != nil {
t.Fatalf("Failed to serialize original request info: %v", err)
}
originalRawStr := string(originalRaw)
expectedRawStr := `{"query":{"q1":"123","q2":"456"},"headers":{"content_type":"application/json"},"body":{"a":123,"b":"test"},"auth":{"avatar":"","collectionId":"_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user1","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"default"}`
if expectedRawStr != originalRawStr {
t.Fatalf("Expected original info\n%v\ngot\n%v", expectedRawStr, originalRawStr)
}
// check the clone data
// ---
cloneRaw, err := json.Marshal(clone)
if err != nil {
t.Fatalf("Failed to serialize clone request info: %v", err)
}
cloneRawStr := string(cloneRaw)
expectedCloneStr := `{"query":{"new_query":"test","q1":"123","q2":"456"},"headers":{"content_type":"application/json","new_header":"test"},"body":{"a":123,"b":"test","new_body":"test"},"auth":{"avatar":"","collectionId":"_users_auth_","collectionName":"users","created":"","emailVisibility":false,"file":[],"id":"user2","name":"","rel":"","updated":"","username":"","verified":false},"method":"POST","context":"default"}`
if expectedCloneStr != cloneRawStr {
t.Fatalf("Expected clone info\n%v\ngot\n%v", expectedCloneStr, cloneRawStr)
}
}