mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-02-05 10:45:09 +02:00
477 lines
13 KiB
Go
477 lines
13 KiB
Go
package core_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pocketbase/dbx"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
"github.com/pocketbase/pocketbase/tests"
|
|
)
|
|
|
|
func TestImportCollections(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testApp, _ := tests.NewTestApp()
|
|
defer testApp.Cleanup()
|
|
|
|
var regularCollections []*core.Collection
|
|
err := testApp.CollectionQuery().AndWhere(dbx.HashExp{"system": false}).All(®ularCollections)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var systemCollections []*core.Collection
|
|
err = testApp.CollectionQuery().AndWhere(dbx.HashExp{"system": true}).All(&systemCollections)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
totalRegularCollections := len(regularCollections)
|
|
totalSystemCollections := len(systemCollections)
|
|
totalCollections := totalRegularCollections + totalSystemCollections
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
data []map[string]any
|
|
deleteMissing bool
|
|
expectError bool
|
|
expectCollectionsCount int
|
|
afterTestFunc func(testApp *tests.TestApp, resultCollections []*core.Collection)
|
|
}{
|
|
{
|
|
name: "empty collections",
|
|
data: []map[string]any{},
|
|
expectError: true,
|
|
expectCollectionsCount: totalCollections,
|
|
},
|
|
{
|
|
name: "minimal collection import (with missing system fields)",
|
|
data: []map[string]any{
|
|
{"name": "import_test1", "type": "auth"},
|
|
{
|
|
"name": "import_test2", "fields": []map[string]any{
|
|
{"name": "test", "type": "text"},
|
|
},
|
|
},
|
|
},
|
|
deleteMissing: false,
|
|
expectError: false,
|
|
expectCollectionsCount: totalCollections + 2,
|
|
},
|
|
{
|
|
name: "minimal collection import (trigger collection model validations)",
|
|
data: []map[string]any{
|
|
{"name": ""},
|
|
{
|
|
"name": "import_test2", "fields": []map[string]any{
|
|
{"name": "test", "type": "text"},
|
|
},
|
|
},
|
|
},
|
|
deleteMissing: false,
|
|
expectError: true,
|
|
expectCollectionsCount: totalCollections,
|
|
},
|
|
{
|
|
name: "minimal collection import (trigger field settings validation)",
|
|
data: []map[string]any{
|
|
{"name": "import_test", "fields": []map[string]any{{"name": "test", "type": "text", "min": -1}}},
|
|
},
|
|
deleteMissing: false,
|
|
expectError: true,
|
|
expectCollectionsCount: totalCollections,
|
|
},
|
|
{
|
|
name: "new + update + delete (system collections delete should be ignored)",
|
|
data: []map[string]any{
|
|
{
|
|
"id": "wsmn24bux7wo113",
|
|
"name": "demo",
|
|
"fields": []map[string]any{
|
|
{
|
|
"id": "_2hlxbmp",
|
|
"name": "title",
|
|
"type": "text",
|
|
"system": false,
|
|
"required": true,
|
|
"min": 3,
|
|
"max": nil,
|
|
"pattern": "",
|
|
},
|
|
},
|
|
"indexes": []string{},
|
|
},
|
|
{
|
|
"name": "import1",
|
|
"fields": []map[string]any{
|
|
{
|
|
"name": "active",
|
|
"type": "bool",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
deleteMissing: true,
|
|
expectError: false,
|
|
expectCollectionsCount: totalSystemCollections + 2,
|
|
},
|
|
{
|
|
name: "test with deleteMissing: false",
|
|
data: []map[string]any{
|
|
{
|
|
// "id": "wsmn24bux7wo113", // test update with only name as identifier
|
|
"name": "demo1",
|
|
"fields": []map[string]any{
|
|
{
|
|
"id": "_2hlxbmp",
|
|
"name": "title",
|
|
"type": "text",
|
|
"system": false,
|
|
"required": true,
|
|
"min": 3,
|
|
"max": nil,
|
|
"pattern": "",
|
|
},
|
|
{
|
|
"id": "_2hlxbmp",
|
|
"name": "field_with_duplicate_id",
|
|
"type": "text",
|
|
"system": false,
|
|
"required": true,
|
|
"unique": false,
|
|
"min": 4,
|
|
"max": nil,
|
|
"pattern": "",
|
|
},
|
|
{
|
|
"id": "abcd_import",
|
|
"name": "new_field",
|
|
"type": "text",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"name": "new_import",
|
|
"fields": []map[string]any{
|
|
{
|
|
"id": "abcd_import",
|
|
"name": "active",
|
|
"type": "bool",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
deleteMissing: false,
|
|
expectError: false,
|
|
expectCollectionsCount: totalCollections + 1,
|
|
afterTestFunc: func(testApp *tests.TestApp, resultCollections []*core.Collection) {
|
|
expectedCollectionFields := map[string]int{
|
|
core.CollectionNameAuthOrigins: 6,
|
|
"nologin": 10,
|
|
"demo1": 18,
|
|
"demo2": 5,
|
|
"demo3": 5,
|
|
"demo4": 16,
|
|
"demo5": 9,
|
|
"new_import": 2,
|
|
}
|
|
for name, expectedCount := range expectedCollectionFields {
|
|
collection, err := testApp.FindCollectionByNameOrId(name)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if totalFields := len(collection.Fields); totalFields != expectedCount {
|
|
t.Errorf("Expected %d %q fields, got %d", expectedCount, collection.Name, totalFields)
|
|
}
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
testApp, _ := tests.NewTestApp()
|
|
defer testApp.Cleanup()
|
|
|
|
err := testApp.ImportCollections(s.data, s.deleteMissing)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
// check collections count
|
|
collections := []*core.Collection{}
|
|
if err := testApp.CollectionQuery().All(&collections); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(collections) != s.expectCollectionsCount {
|
|
t.Fatalf("Expected %d collections, got %d", s.expectCollectionsCount, len(collections))
|
|
}
|
|
|
|
if s.afterTestFunc != nil {
|
|
s.afterTestFunc(testApp, collections)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestImportCollectionsByMarshaledJSON(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testApp, _ := tests.NewTestApp()
|
|
defer testApp.Cleanup()
|
|
|
|
var regularCollections []*core.Collection
|
|
err := testApp.CollectionQuery().AndWhere(dbx.HashExp{"system": false}).All(®ularCollections)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var systemCollections []*core.Collection
|
|
err = testApp.CollectionQuery().AndWhere(dbx.HashExp{"system": true}).All(&systemCollections)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
totalRegularCollections := len(regularCollections)
|
|
totalSystemCollections := len(systemCollections)
|
|
totalCollections := totalRegularCollections + totalSystemCollections
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
data string
|
|
deleteMissing bool
|
|
expectError bool
|
|
expectCollectionsCount int
|
|
afterTestFunc func(testApp *tests.TestApp, resultCollections []*core.Collection)
|
|
}{
|
|
{
|
|
name: "invalid json array",
|
|
data: `{"test":123}`,
|
|
expectError: true,
|
|
expectCollectionsCount: totalCollections,
|
|
},
|
|
{
|
|
name: "new + update + delete (system collections delete should be ignored)",
|
|
data: `[
|
|
{
|
|
"id": "wsmn24bux7wo113",
|
|
"name": "demo",
|
|
"fields": [
|
|
{
|
|
"id": "_2hlxbmp",
|
|
"name": "title",
|
|
"type": "text",
|
|
"system": false,
|
|
"required": true,
|
|
"min": 3,
|
|
"max": null,
|
|
"pattern": ""
|
|
}
|
|
],
|
|
"indexes": []
|
|
},
|
|
{
|
|
"name": "import1",
|
|
"fields": [
|
|
{
|
|
"name": "active",
|
|
"type": "bool"
|
|
}
|
|
]
|
|
}
|
|
]`,
|
|
deleteMissing: true,
|
|
expectError: false,
|
|
expectCollectionsCount: totalSystemCollections + 2,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
testApp, _ := tests.NewTestApp()
|
|
defer testApp.Cleanup()
|
|
|
|
err := testApp.ImportCollectionsByMarshaledJSON([]byte(s.data), s.deleteMissing)
|
|
|
|
hasErr := err != nil
|
|
if hasErr != s.expectError {
|
|
t.Fatalf("Expected hasErr to be %v, got %v (%v)", s.expectError, hasErr, err)
|
|
}
|
|
|
|
// check collections count
|
|
collections := []*core.Collection{}
|
|
if err := testApp.CollectionQuery().All(&collections); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(collections) != s.expectCollectionsCount {
|
|
t.Fatalf("Expected %d collections, got %d", s.expectCollectionsCount, len(collections))
|
|
}
|
|
|
|
if s.afterTestFunc != nil {
|
|
s.afterTestFunc(testApp, collections)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestImportCollectionsUpdateRules(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
data map[string]any
|
|
deleteMissing bool
|
|
}{
|
|
{
|
|
"extend existing by name (without deleteMissing)",
|
|
map[string]any{"name": "clients", "authToken": map[string]any{"duration": 100}, "fields": []map[string]any{{"name": "test", "type": "text"}}},
|
|
false,
|
|
},
|
|
{
|
|
"extend existing by id (without deleteMissing)",
|
|
map[string]any{"id": "v851q4r790rhknl", "authToken": map[string]any{"duration": 100}, "fields": []map[string]any{{"name": "test", "type": "text"}}},
|
|
false,
|
|
},
|
|
{
|
|
"extend with delete missing",
|
|
map[string]any{
|
|
"id": "v851q4r790rhknl",
|
|
"authToken": map[string]any{"duration": 100},
|
|
"fields": []map[string]any{{"name": "test", "type": "text"}},
|
|
"passwordAuth": map[string]any{"identityFields": []string{"email"}},
|
|
"indexes": []string{
|
|
// min required system fields indexes
|
|
"CREATE UNIQUE INDEX `_v851q4r790rhknl_email_idx` ON `clients` (email) WHERE email != ''",
|
|
"CREATE UNIQUE INDEX `_v851q4r790rhknl_tokenKey_idx` ON `clients` (tokenKey)",
|
|
},
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
testApp, _ := tests.NewTestApp()
|
|
defer testApp.Cleanup()
|
|
|
|
beforeCollection, err := testApp.FindCollectionByNameOrId("clients")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = testApp.ImportCollections([]map[string]any{s.data}, s.deleteMissing)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
afterCollection, err := testApp.FindCollectionByNameOrId("clients")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if afterCollection.AuthToken.Duration != 100 {
|
|
t.Fatalf("Expected AuthToken duration to be %d, got %d", 100, afterCollection.AuthToken.Duration)
|
|
}
|
|
if beforeCollection.AuthToken.Secret != afterCollection.AuthToken.Secret {
|
|
t.Fatalf("Expected AuthToken secrets to remain the same, got\n%q\nVS\n%q", beforeCollection.AuthToken.Secret, afterCollection.AuthToken.Secret)
|
|
}
|
|
if beforeCollection.Name != afterCollection.Name {
|
|
t.Fatalf("Expected Name to remain the same, got\n%q\nVS\n%q", beforeCollection.Name, afterCollection.Name)
|
|
}
|
|
if beforeCollection.Id != afterCollection.Id {
|
|
t.Fatalf("Expected Id to remain the same, got\n%q\nVS\n%q", beforeCollection.Id, afterCollection.Id)
|
|
}
|
|
|
|
if !s.deleteMissing {
|
|
totalExpectedFields := len(beforeCollection.Fields) + 1
|
|
if v := len(afterCollection.Fields); v != totalExpectedFields {
|
|
t.Fatalf("Expected %d total fields, got %d", totalExpectedFields, v)
|
|
}
|
|
|
|
if afterCollection.Fields.GetByName("test") == nil {
|
|
t.Fatalf("Missing new field %q", "test")
|
|
}
|
|
|
|
// ensure that the old fields still exist
|
|
oldFields := beforeCollection.Fields.FieldNames()
|
|
for _, name := range oldFields {
|
|
if afterCollection.Fields.GetByName(name) == nil {
|
|
t.Fatalf("Missing expected old field %q", name)
|
|
}
|
|
}
|
|
} else {
|
|
totalExpectedFields := 1
|
|
for _, f := range beforeCollection.Fields {
|
|
if f.GetSystem() {
|
|
totalExpectedFields++
|
|
}
|
|
}
|
|
|
|
if v := len(afterCollection.Fields); v != totalExpectedFields {
|
|
t.Fatalf("Expected %d total fields, got %d", totalExpectedFields, v)
|
|
}
|
|
|
|
if afterCollection.Fields.GetByName("test") == nil {
|
|
t.Fatalf("Missing new field %q", "test")
|
|
}
|
|
|
|
// ensure that the old system fields still exist
|
|
for _, f := range beforeCollection.Fields {
|
|
if f.GetSystem() && afterCollection.Fields.GetByName(f.GetName()) == nil {
|
|
t.Fatalf("Missing expected old field %q", f.GetName())
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestImportCollectionsCreateRules(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testApp, _ := tests.NewTestApp()
|
|
defer testApp.Cleanup()
|
|
|
|
err := testApp.ImportCollections([]map[string]any{
|
|
{"name": "new_test", "type": "auth", "authToken": map[string]any{"duration": 123}, "fields": []map[string]any{{"name": "test", "type": "text"}}},
|
|
}, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
collection, err := testApp.FindCollectionByNameOrId("new_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
raw, err := json.Marshal(collection)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rawStr := string(raw)
|
|
|
|
expectedParts := []string{
|
|
`"name":"new_test"`,
|
|
`"fields":[`,
|
|
`"name":"id"`,
|
|
`"name":"email"`,
|
|
`"name":"tokenKey"`,
|
|
`"name":"password"`,
|
|
`"name":"test"`,
|
|
`"indexes":[`,
|
|
`CREATE UNIQUE INDEX`,
|
|
`"duration":123`,
|
|
}
|
|
|
|
for _, part := range expectedParts {
|
|
if !strings.Contains(rawStr, part) {
|
|
t.Errorf("Missing %q in\n%s", part, rawStr)
|
|
}
|
|
}
|
|
}
|