mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-02-16 01:19:46 +02:00
1144 lines
22 KiB
Go
1144 lines
22 KiB
Go
package core_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pocketbase/dbx"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
"github.com/pocketbase/pocketbase/tests"
|
|
"github.com/pocketbase/pocketbase/tools/types"
|
|
)
|
|
|
|
func TestRecordQueryWithDifferentCollectionValues(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
collection, err := app.FindCollectionByNameOrId("demo1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
collection any
|
|
expectedTotal int
|
|
expectError bool
|
|
}{
|
|
{"with nil value", nil, 0, true},
|
|
{"with invalid or missing collection id/name", "missing", 0, true},
|
|
{"with pointer model", collection, 3, false},
|
|
{"with value model", *collection, 3, false},
|
|
{"with name", "demo1", 3, false},
|
|
{"with id", "wsmn24bux7wo113", 3, false},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
var records []*core.Record
|
|
err := app.RecordQuery(s.collection).All(&records)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasError %v, got %v", s.expectError, hasErr)
|
|
}
|
|
|
|
if total := len(records); total != s.expectedTotal {
|
|
t.Fatalf("Expected %d records, got %d", s.expectedTotal, total)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRecordQueryOne(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
collection string
|
|
recordId string
|
|
model core.Model
|
|
}{
|
|
{
|
|
"record model",
|
|
"demo1",
|
|
"84nmscqy84lsi1t",
|
|
&core.Record{},
|
|
},
|
|
{
|
|
"record proxy",
|
|
"demo1",
|
|
"84nmscqy84lsi1t",
|
|
&struct {
|
|
core.BaseRecordProxy
|
|
}{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
collection, err := app.FindCollectionByNameOrId(s.collection)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
q := app.RecordQuery(collection).
|
|
Where(dbx.HashExp{"id": s.recordId})
|
|
|
|
if err := q.One(s.model); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if s.model.PK() != s.recordId {
|
|
t.Fatalf("Expected record with id %q, got %q", s.recordId, s.model.PK())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRecordQueryAll(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
type mockRecordProxy struct {
|
|
core.BaseRecordProxy
|
|
}
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
collection string
|
|
recordIds []any
|
|
result any
|
|
}{
|
|
{
|
|
"slice of Record models",
|
|
"demo1",
|
|
[]any{"84nmscqy84lsi1t", "al1h9ijdeojtsjy"},
|
|
&[]core.Record{},
|
|
},
|
|
{
|
|
"slice of pointer Record models",
|
|
"demo1",
|
|
[]any{"84nmscqy84lsi1t", "al1h9ijdeojtsjy"},
|
|
&[]*core.Record{},
|
|
},
|
|
{
|
|
"slice of Record proxies",
|
|
"demo1",
|
|
[]any{"84nmscqy84lsi1t", "al1h9ijdeojtsjy"},
|
|
&[]mockRecordProxy{},
|
|
},
|
|
{
|
|
"slice of pointer Record proxies",
|
|
"demo1",
|
|
[]any{"84nmscqy84lsi1t", "al1h9ijdeojtsjy"},
|
|
&[]mockRecordProxy{},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
collection, err := app.FindCollectionByNameOrId(s.collection)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
q := app.RecordQuery(collection).
|
|
Where(dbx.HashExp{"id": s.recordIds})
|
|
|
|
if err := q.All(s.result); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
raw, err := json.Marshal(s.result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rawStr := string(raw)
|
|
|
|
sliceOfMaps := []any{}
|
|
if err := json.Unmarshal(raw, &sliceOfMaps); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(sliceOfMaps) != len(s.recordIds) {
|
|
t.Fatalf("Expected %d items, got %d", len(s.recordIds), len(sliceOfMaps))
|
|
}
|
|
|
|
for _, id := range s.recordIds {
|
|
if !strings.Contains(rawStr, fmt.Sprintf(`"id":%q`, id)) {
|
|
t.Fatalf("Missing id %q in\n%s", id, rawStr)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindRecordById(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
collectionIdOrName string
|
|
id string
|
|
filters []func(q *dbx.SelectQuery) error
|
|
expectError bool
|
|
}{
|
|
{"demo2", "missing", nil, true},
|
|
{"missing", "0yxhwia2amd8gec", nil, true},
|
|
{"demo2", "0yxhwia2amd8gec", nil, false},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{}, false},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{nil, nil}, false},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
nil,
|
|
func(q *dbx.SelectQuery) error { return nil },
|
|
}, false},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"title": "missing"})
|
|
return nil
|
|
},
|
|
}, true},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
return errors.New("test error")
|
|
},
|
|
}, true},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"title": "test3"})
|
|
return nil
|
|
},
|
|
}, false},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"title": "test3"})
|
|
return nil
|
|
},
|
|
nil,
|
|
}, false},
|
|
{"demo2", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"title": "test3"})
|
|
return nil
|
|
},
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"active": false})
|
|
return nil
|
|
},
|
|
}, true},
|
|
{"sz5l5z67tg7gku0", "0yxhwia2amd8gec", []func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"title": "test3"})
|
|
return nil
|
|
},
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"active": true})
|
|
return nil
|
|
},
|
|
}, false},
|
|
}
|
|
|
|
for i, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%d_%s_%s_%d", i, s.collectionIdOrName, s.id, len(s.filters)), func(t *testing.T) {
|
|
record, err := app.FindRecordById(
|
|
s.collectionIdOrName,
|
|
s.id,
|
|
s.filters...,
|
|
)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if record != nil && record.Id != s.id {
|
|
t.Fatalf("Expected record with id %s, got %s", s.id, record.Id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindRecordsByIds(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
collectionIdOrName string
|
|
ids []string
|
|
filters []func(q *dbx.SelectQuery) error
|
|
expectTotal int
|
|
expectError bool
|
|
}{
|
|
{"demo2", []string{}, nil, 0, false},
|
|
{"demo2", []string{""}, nil, 0, false},
|
|
{"demo2", []string{"missing"}, nil, 0, false},
|
|
{"missing", []string{"0yxhwia2amd8gec"}, nil, 0, true},
|
|
{"demo2", []string{"0yxhwia2amd8gec"}, nil, 1, false},
|
|
{"sz5l5z67tg7gku0", []string{"0yxhwia2amd8gec"}, nil, 1, false},
|
|
{
|
|
"demo2",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
nil,
|
|
2,
|
|
false,
|
|
},
|
|
{
|
|
"demo2",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
[]func(q *dbx.SelectQuery) error{},
|
|
2,
|
|
false,
|
|
},
|
|
{
|
|
"demo2",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
[]func(q *dbx.SelectQuery) error{nil, nil},
|
|
2,
|
|
false,
|
|
},
|
|
{
|
|
"demo2",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
[]func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
return nil // empty filter
|
|
},
|
|
},
|
|
2,
|
|
false,
|
|
},
|
|
{
|
|
"demo2",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
[]func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
return nil // empty filter
|
|
},
|
|
func(q *dbx.SelectQuery) error {
|
|
return errors.New("test error")
|
|
},
|
|
},
|
|
0,
|
|
true,
|
|
},
|
|
{
|
|
"demo2",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
[]func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"active": true})
|
|
return nil
|
|
},
|
|
nil,
|
|
},
|
|
1,
|
|
false,
|
|
},
|
|
{
|
|
"sz5l5z67tg7gku0",
|
|
[]string{"0yxhwia2amd8gec", "llvuca81nly1qls"},
|
|
[]func(q *dbx.SelectQuery) error{
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.HashExp{"active": true})
|
|
return nil
|
|
},
|
|
func(q *dbx.SelectQuery) error {
|
|
q.AndWhere(dbx.Not(dbx.HashExp{"title": ""}))
|
|
return nil
|
|
},
|
|
},
|
|
1,
|
|
false,
|
|
},
|
|
}
|
|
|
|
for i, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%d_%s_%v_%d", i, s.collectionIdOrName, s.ids, len(s.filters)), func(t *testing.T) {
|
|
records, err := app.FindRecordsByIds(
|
|
s.collectionIdOrName,
|
|
s.ids,
|
|
s.filters...,
|
|
)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if len(records) != s.expectTotal {
|
|
t.Fatalf("Expected %d records, got %d", s.expectTotal, len(records))
|
|
}
|
|
|
|
for _, r := range records {
|
|
if !slices.Contains(s.ids, r.Id) {
|
|
t.Fatalf("Couldn't find id %s in %v", r.Id, s.ids)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindAllRecords(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
collectionIdOrName string
|
|
expressions []dbx.Expression
|
|
expectIds []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
"missing",
|
|
nil,
|
|
[]string{},
|
|
true,
|
|
},
|
|
{
|
|
"demo2",
|
|
nil,
|
|
[]string{
|
|
"achvryl401bhse3",
|
|
"llvuca81nly1qls",
|
|
"0yxhwia2amd8gec",
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"demo2",
|
|
[]dbx.Expression{
|
|
nil,
|
|
dbx.HashExp{"id": "123"},
|
|
},
|
|
[]string{},
|
|
false,
|
|
},
|
|
{
|
|
"sz5l5z67tg7gku0",
|
|
[]dbx.Expression{
|
|
dbx.Like("title", "test").Match(true, true),
|
|
dbx.HashExp{"active": true},
|
|
},
|
|
[]string{
|
|
"achvryl401bhse3",
|
|
"0yxhwia2amd8gec",
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
|
|
for i, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%d_%s", i, s.collectionIdOrName), func(t *testing.T) {
|
|
records, err := app.FindAllRecords(s.collectionIdOrName, s.expressions...)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if len(records) != len(s.expectIds) {
|
|
t.Fatalf("Expected %d records, got %d", len(s.expectIds), len(records))
|
|
}
|
|
|
|
for _, r := range records {
|
|
if !slices.Contains(s.expectIds, r.Id) {
|
|
t.Fatalf("Couldn't find id %s in %v", r.Id, s.expectIds)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindFirstRecordByData(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
collectionIdOrName string
|
|
key string
|
|
value any
|
|
expectId string
|
|
expectError bool
|
|
}{
|
|
{
|
|
"missing",
|
|
"id",
|
|
"llvuca81nly1qls",
|
|
"llvuca81nly1qls",
|
|
true,
|
|
},
|
|
{
|
|
"demo2",
|
|
"",
|
|
"llvuca81nly1qls",
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"demo2",
|
|
"id",
|
|
"invalid",
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"demo2",
|
|
"id",
|
|
"llvuca81nly1qls",
|
|
"llvuca81nly1qls",
|
|
false,
|
|
},
|
|
{
|
|
"sz5l5z67tg7gku0",
|
|
"title",
|
|
"test3",
|
|
"0yxhwia2amd8gec",
|
|
false,
|
|
},
|
|
}
|
|
|
|
for i, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%d_%s_%s_%v", i, s.collectionIdOrName, s.key, s.value), func(t *testing.T) {
|
|
record, err := app.FindFirstRecordByData(s.collectionIdOrName, s.key, s.value)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if !s.expectError && record.Id != s.expectId {
|
|
t.Fatalf("Expected record with id %s, got %v", s.expectId, record.Id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindRecordsByFilter(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
collectionIdOrName string
|
|
filter string
|
|
sort string
|
|
limit int
|
|
offset int
|
|
params []dbx.Params
|
|
expectError bool
|
|
expectRecordIds []string
|
|
}{
|
|
{
|
|
"missing collection",
|
|
"missing",
|
|
"id != ''",
|
|
"",
|
|
0,
|
|
0,
|
|
nil,
|
|
true,
|
|
nil,
|
|
},
|
|
{
|
|
"invalid filter",
|
|
"demo2",
|
|
"someMissingField > 1",
|
|
"",
|
|
0,
|
|
0,
|
|
nil,
|
|
true,
|
|
nil,
|
|
},
|
|
{
|
|
"empty filter",
|
|
"demo2",
|
|
"",
|
|
"",
|
|
0,
|
|
0,
|
|
nil,
|
|
false,
|
|
[]string{
|
|
"llvuca81nly1qls",
|
|
"achvryl401bhse3",
|
|
"0yxhwia2amd8gec",
|
|
},
|
|
},
|
|
{
|
|
"simple filter",
|
|
"demo2",
|
|
"id != ''",
|
|
"",
|
|
0,
|
|
0,
|
|
nil,
|
|
false,
|
|
[]string{
|
|
"llvuca81nly1qls",
|
|
"achvryl401bhse3",
|
|
"0yxhwia2amd8gec",
|
|
},
|
|
},
|
|
{
|
|
"multi-condition filter with sort",
|
|
"demo2",
|
|
"id != '' && active=true",
|
|
"-created,title",
|
|
-1, // should behave the same as 0
|
|
0,
|
|
nil,
|
|
false,
|
|
[]string{
|
|
"0yxhwia2amd8gec",
|
|
"achvryl401bhse3",
|
|
},
|
|
},
|
|
{
|
|
"with limit and offset",
|
|
"sz5l5z67tg7gku0",
|
|
"id != ''",
|
|
"title",
|
|
2,
|
|
1,
|
|
nil,
|
|
false,
|
|
[]string{
|
|
"achvryl401bhse3",
|
|
"0yxhwia2amd8gec",
|
|
},
|
|
},
|
|
{
|
|
"with placeholder params",
|
|
"demo2",
|
|
"active = {:active}",
|
|
"",
|
|
10,
|
|
0,
|
|
[]dbx.Params{{"active": false}},
|
|
false,
|
|
[]string{
|
|
"llvuca81nly1qls",
|
|
},
|
|
},
|
|
{
|
|
"with json filter and sort",
|
|
"demo4",
|
|
"json_object != null && json_object.a.b = 'test'",
|
|
"-json_object.a",
|
|
10,
|
|
0,
|
|
[]dbx.Params{{"active": false}},
|
|
false,
|
|
[]string{
|
|
"i9naidtvr6qsgb4",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
records, err := app.FindRecordsByFilter(
|
|
s.collectionIdOrName,
|
|
s.filter,
|
|
s.sort,
|
|
s.limit,
|
|
s.offset,
|
|
s.params...,
|
|
)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if hasErr {
|
|
return
|
|
}
|
|
|
|
if len(records) != len(s.expectRecordIds) {
|
|
t.Fatalf("Expected %d records, got %d", len(s.expectRecordIds), len(records))
|
|
}
|
|
|
|
for i, id := range s.expectRecordIds {
|
|
if id != records[i].Id {
|
|
t.Fatalf("Expected record with id %q, got %q at index %d", id, records[i].Id, i)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindFirstRecordByFilter(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
collectionIdOrName string
|
|
filter string
|
|
params []dbx.Params
|
|
expectError bool
|
|
expectRecordId string
|
|
}{
|
|
{
|
|
"missing collection",
|
|
"missing",
|
|
"id != ''",
|
|
nil,
|
|
true,
|
|
"",
|
|
},
|
|
{
|
|
"invalid filter",
|
|
"demo2",
|
|
"someMissingField > 1",
|
|
nil,
|
|
true,
|
|
"",
|
|
},
|
|
{
|
|
"empty filter",
|
|
"demo2",
|
|
"",
|
|
nil,
|
|
false,
|
|
"llvuca81nly1qls",
|
|
},
|
|
{
|
|
"valid filter but no matches",
|
|
"demo2",
|
|
"id = 'test'",
|
|
nil,
|
|
true,
|
|
"",
|
|
},
|
|
{
|
|
"valid filter and multiple matches",
|
|
"sz5l5z67tg7gku0",
|
|
"id != ''",
|
|
nil,
|
|
false,
|
|
"llvuca81nly1qls",
|
|
},
|
|
{
|
|
"with placeholder params",
|
|
"demo2",
|
|
"active = {:active}",
|
|
[]dbx.Params{{"active": false}},
|
|
false,
|
|
"llvuca81nly1qls",
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
record, err := app.FindFirstRecordByFilter(s.collectionIdOrName, s.filter, s.params...)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if hasErr {
|
|
return
|
|
}
|
|
|
|
if record.Id != s.expectRecordId {
|
|
t.Fatalf("Expected record with id %q, got %q", s.expectRecordId, record.Id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCountRecords(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
collectionIdOrName string
|
|
expressions []dbx.Expression
|
|
expectTotal int64
|
|
expectError bool
|
|
}{
|
|
{
|
|
"missing collection",
|
|
"missing",
|
|
nil,
|
|
0,
|
|
true,
|
|
},
|
|
{
|
|
"valid collection name",
|
|
"demo2",
|
|
nil,
|
|
3,
|
|
false,
|
|
},
|
|
{
|
|
"valid collection id",
|
|
"sz5l5z67tg7gku0",
|
|
nil,
|
|
3,
|
|
false,
|
|
},
|
|
{
|
|
"nil expression",
|
|
"demo2",
|
|
[]dbx.Expression{nil},
|
|
3,
|
|
false,
|
|
},
|
|
{
|
|
"no matches",
|
|
"demo2",
|
|
[]dbx.Expression{
|
|
nil,
|
|
dbx.Like("title", "missing"),
|
|
dbx.HashExp{"active": true},
|
|
},
|
|
0,
|
|
false,
|
|
},
|
|
{
|
|
"with matches",
|
|
"demo2",
|
|
[]dbx.Expression{
|
|
nil,
|
|
dbx.Like("title", "test"),
|
|
dbx.HashExp{"active": true},
|
|
},
|
|
2,
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
total, err := app.CountRecords(s.collectionIdOrName, s.expressions...)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if total != s.expectTotal {
|
|
t.Fatalf("Expected total %d, got %d", s.expectTotal, total)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindAuthRecordByToken(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
token string
|
|
types []string
|
|
expectedId string
|
|
}{
|
|
{
|
|
"empty token",
|
|
"",
|
|
nil,
|
|
"",
|
|
},
|
|
{
|
|
"invalid token",
|
|
"invalid",
|
|
nil,
|
|
"",
|
|
},
|
|
{
|
|
"expired token",
|
|
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoxNjQwOTkxNjYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.2D3tmqPn3vc5LoqqCz8V-iCDVXo9soYiH0d32G7FQT4",
|
|
nil,
|
|
"",
|
|
},
|
|
{
|
|
"valid auth token",
|
|
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.ZT3F0Z3iM-xbGgSG3LEKiEzHrPHr8t8IuHLZGGNuxLo",
|
|
nil,
|
|
"4q1xlclmfloku33",
|
|
},
|
|
{
|
|
"valid verification token",
|
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRjNDlrNmpnZWpuNDBoMyIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6InZlcmlmaWNhdGlvbiIsImNvbGxlY3Rpb25JZCI6ImtwdjcwOXNrMmxxYnFrOCIsImVtYWlsIjoidGVzdEBleGFtcGxlLmNvbSJ9.5GmuZr4vmwk3Cb_3ZZWNxwbE75KZC-j71xxIPR9AsVw",
|
|
nil,
|
|
"dc49k6jgejn40h3",
|
|
},
|
|
{
|
|
"auth token with file type only check",
|
|
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.ZT3F0Z3iM-xbGgSG3LEKiEzHrPHr8t8IuHLZGGNuxLo",
|
|
[]string{core.TokenTypeFile},
|
|
"",
|
|
},
|
|
{
|
|
"auth token with file and auth type check",
|
|
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.ZT3F0Z3iM-xbGgSG3LEKiEzHrPHr8t8IuHLZGGNuxLo",
|
|
[]string{core.TokenTypeFile, core.TokenTypeAuth},
|
|
"4q1xlclmfloku33",
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
record, err := app.FindAuthRecordByToken(s.token, s.types...)
|
|
|
|
hasErr := err != nil
|
|
expectErr := s.expectedId == ""
|
|
if hasErr != expectErr {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", expectErr, hasErr, err)
|
|
}
|
|
|
|
if hasErr {
|
|
return
|
|
}
|
|
|
|
if record.Id != s.expectedId {
|
|
t.Fatalf("Expected record with id %q, got %q", s.expectedId, record.Id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFindAuthRecordByEmail(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
scenarios := []struct {
|
|
collectionIdOrName string
|
|
email string
|
|
expectError bool
|
|
}{
|
|
{"missing", "test@example.com", true},
|
|
{"demo2", "test@example.com", true},
|
|
{"users", "missing@example.com", true},
|
|
{"users", "test@example.com", false},
|
|
{"clients", "test2@example.com", false},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(fmt.Sprintf("%s_%s", s.collectionIdOrName, s.email), func(t *testing.T) {
|
|
record, err := app.FindAuthRecordByEmail(s.collectionIdOrName, s.email)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
if hasErr {
|
|
return
|
|
}
|
|
|
|
if record.Email() != s.email {
|
|
t.Fatalf("Expected record with email %s, got %s", s.email, record.Email())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCanAccessRecord(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app, _ := tests.NewTestApp()
|
|
defer app.Cleanup()
|
|
|
|
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, "test@example.com")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
record, err := app.FindRecordById("demo1", "imy661ixudk5izi")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
record *core.Record
|
|
requestInfo *core.RequestInfo
|
|
rule *string
|
|
expected bool
|
|
expectError bool
|
|
}{
|
|
{
|
|
"as superuser with nil rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: superuser,
|
|
},
|
|
nil,
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"as superuser with non-empty rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: superuser,
|
|
},
|
|
types.Pointer("id = ''"), // the filter rule should be ignored
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"as superuser with invalid rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: superuser,
|
|
},
|
|
types.Pointer("id ?!@ 1"), // the filter rule should be ignored
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"as guest with nil rule",
|
|
record,
|
|
&core.RequestInfo{},
|
|
nil,
|
|
false,
|
|
false,
|
|
},
|
|
{
|
|
"as guest with empty rule",
|
|
record,
|
|
&core.RequestInfo{},
|
|
types.Pointer(""),
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"as guest with invalid rule",
|
|
record,
|
|
&core.RequestInfo{},
|
|
types.Pointer("id ?!@ 1"),
|
|
false,
|
|
true,
|
|
},
|
|
{
|
|
"as guest with mismatched rule",
|
|
record,
|
|
&core.RequestInfo{},
|
|
types.Pointer("@request.auth.id != ''"),
|
|
false,
|
|
false,
|
|
},
|
|
{
|
|
"as guest with matched rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Body: map[string]any{"test": 1},
|
|
},
|
|
types.Pointer("@request.auth.id != '' || @request.body.test = 1"),
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"as auth record with nil rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: user,
|
|
},
|
|
nil,
|
|
false,
|
|
false,
|
|
},
|
|
{
|
|
"as auth record with empty rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: user,
|
|
},
|
|
types.Pointer(""),
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"as auth record with invalid rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: user,
|
|
},
|
|
types.Pointer("id ?!@ 1"),
|
|
false,
|
|
true,
|
|
},
|
|
{
|
|
"as auth record with mismatched rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: user,
|
|
Body: map[string]any{"test": 1},
|
|
},
|
|
types.Pointer("@request.auth.id != '' && @request.body.test > 1"),
|
|
false,
|
|
false,
|
|
},
|
|
{
|
|
"as auth record with matched rule",
|
|
record,
|
|
&core.RequestInfo{
|
|
Auth: user,
|
|
Body: map[string]any{"test": 2},
|
|
},
|
|
types.Pointer("@request.auth.id != '' && @request.body.test > 1"),
|
|
true,
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
result, err := app.CanAccessRecord(s.record, s.requestInfo, s.rule)
|
|
|
|
if result != s.expected {
|
|
t.Fatalf("Expected %v, got %v", s.expected, result)
|
|
}
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
})
|
|
}
|
|
}
|