2022-08-08 18:16:33 +02:00
|
|
|
package forms_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/pocketbase/pocketbase/forms"
|
|
|
|
"github.com/pocketbase/pocketbase/models"
|
|
|
|
"github.com/pocketbase/pocketbase/tests"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestCollectionsImportValidate(t *testing.T) {
|
|
|
|
app, _ := tests.NewTestApp()
|
|
|
|
defer app.Cleanup()
|
|
|
|
|
|
|
|
form := forms.NewCollectionsImport(app)
|
|
|
|
|
|
|
|
scenarios := []struct {
|
|
|
|
collections []*models.Collection
|
|
|
|
expectError bool
|
|
|
|
}{
|
|
|
|
{nil, true},
|
|
|
|
{[]*models.Collection{}, true},
|
|
|
|
{[]*models.Collection{{}}, false},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, s := range scenarios {
|
|
|
|
form.Collections = s.collections
|
|
|
|
|
|
|
|
err := form.Validate()
|
|
|
|
|
|
|
|
hasErr := err != nil
|
|
|
|
if hasErr != s.expectError {
|
|
|
|
t.Errorf("(%d) Expected hasErr to be %v, got %v (%v)", i, s.expectError, hasErr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCollectionsImportSubmit(t *testing.T) {
|
2023-02-18 19:33:42 +02:00
|
|
|
totalCollections := 10
|
|
|
|
|
2022-08-08 18:16:33 +02:00
|
|
|
scenarios := []struct {
|
|
|
|
name string
|
|
|
|
jsonData string
|
|
|
|
expectError bool
|
|
|
|
expectCollectionsCount int
|
|
|
|
expectEvents map[string]int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty collections",
|
|
|
|
jsonData: `{
|
|
|
|
"deleteMissing": true,
|
|
|
|
"collections": []
|
|
|
|
}`,
|
|
|
|
expectError: true,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections,
|
2022-08-08 18:16:33 +02:00
|
|
|
expectEvents: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "one of the collections has invalid data",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
|
|
|
"name": "import1",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import 2",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: true,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections,
|
2022-08-08 18:16:33 +02:00
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeCreate": 2,
|
|
|
|
},
|
|
|
|
},
|
2022-11-16 15:13:04 +02:00
|
|
|
{
|
|
|
|
name: "test empty base collection schema",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
|
|
|
"name": "import1"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import2",
|
|
|
|
"type": "auth"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: true,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections,
|
2022-11-16 15:13:04 +02:00
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeCreate": 2,
|
|
|
|
},
|
|
|
|
},
|
2022-08-08 18:16:33 +02:00
|
|
|
{
|
|
|
|
name: "all imported collections has valid data",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
|
|
|
"name": "import1",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import2",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
2022-11-16 15:13:04 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import3",
|
|
|
|
"type": "auth"
|
2022-08-08 18:16:33 +02:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: false,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections + 3,
|
2022-08-08 18:16:33 +02:00
|
|
|
expectEvents: map[string]int{
|
2022-11-16 15:13:04 +02:00
|
|
|
"OnModelBeforeCreate": 3,
|
|
|
|
"OnModelAfterCreate": 3,
|
2022-08-08 18:16:33 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "new collection with existing name",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
|
|
|
"name": "demo2",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: true,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections,
|
2022-08-08 18:16:33 +02:00
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeCreate": 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "delete system + modified + new collection",
|
|
|
|
jsonData: `{
|
|
|
|
"deleteMissing": true,
|
|
|
|
"collections": [
|
|
|
|
{
|
2022-10-30 10:28:14 +02:00
|
|
|
"id":"sz5l5z67tg7gku0",
|
|
|
|
"name":"demo2",
|
2022-08-08 18:16:33 +02:00
|
|
|
"schema":[
|
|
|
|
{
|
|
|
|
"id":"_2hlxbmp",
|
|
|
|
"name":"title",
|
|
|
|
"type":"text",
|
|
|
|
"system":false,
|
|
|
|
"required":true,
|
|
|
|
"unique":false,
|
|
|
|
"options":{
|
|
|
|
"min":3,
|
|
|
|
"max":null,
|
|
|
|
"pattern":""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import1",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: true,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections,
|
2022-10-30 10:28:14 +02:00
|
|
|
expectEvents: map[string]int{
|
2023-04-04 19:33:35 +02:00
|
|
|
"OnModelBeforeDelete": 4,
|
2022-10-30 10:28:14 +02:00
|
|
|
},
|
2022-08-08 18:16:33 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "modified + new collection",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
2022-10-30 10:28:14 +02:00
|
|
|
"id":"sz5l5z67tg7gku0",
|
2023-03-14 01:31:30 +02:00
|
|
|
"name":"demo2_rename",
|
2022-08-08 18:16:33 +02:00
|
|
|
"schema":[
|
|
|
|
{
|
|
|
|
"id":"_2hlxbmp",
|
2022-10-30 10:28:14 +02:00
|
|
|
"name":"title_new",
|
2022-08-08 18:16:33 +02:00
|
|
|
"type":"text",
|
|
|
|
"system":false,
|
|
|
|
"required":true,
|
|
|
|
"unique":false,
|
|
|
|
"options":{
|
|
|
|
"min":3,
|
|
|
|
"max":null,
|
|
|
|
"pattern":""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import1",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "import2",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: false,
|
2023-02-18 19:33:42 +02:00
|
|
|
expectCollectionsCount: totalCollections + 2,
|
2022-08-08 18:16:33 +02:00
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeUpdate": 1,
|
|
|
|
"OnModelAfterUpdate": 1,
|
|
|
|
"OnModelBeforeCreate": 2,
|
|
|
|
"OnModelAfterCreate": 2,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "delete non-system + modified + new collection",
|
|
|
|
jsonData: `{
|
|
|
|
"deleteMissing": true,
|
|
|
|
"collections": [
|
|
|
|
{
|
2022-10-30 10:28:14 +02:00
|
|
|
"id": "kpv709sk2lqbqk8",
|
|
|
|
"system": true,
|
|
|
|
"name": "nologin",
|
|
|
|
"type": "auth",
|
|
|
|
"options": {
|
|
|
|
"allowEmailAuth": false,
|
|
|
|
"allowOAuth2Auth": false,
|
|
|
|
"allowUsernameAuth": false,
|
|
|
|
"exceptEmailDomains": [],
|
|
|
|
"manageRule": "@request.auth.collectionName = 'users'",
|
|
|
|
"minPasswordLength": 8,
|
|
|
|
"onlyEmailDomains": [],
|
|
|
|
"requireEmail": true
|
|
|
|
},
|
|
|
|
"listRule": "",
|
|
|
|
"viewRule": "",
|
|
|
|
"createRule": "",
|
|
|
|
"updateRule": "",
|
|
|
|
"deleteRule": "",
|
|
|
|
"schema": [
|
2022-08-08 18:16:33 +02:00
|
|
|
{
|
2022-10-30 10:28:14 +02:00
|
|
|
"id": "x8zzktwe",
|
|
|
|
"name": "name",
|
|
|
|
"type": "text",
|
|
|
|
"system": false,
|
|
|
|
"required": false,
|
|
|
|
"unique": false,
|
|
|
|
"options": {
|
|
|
|
"min": null,
|
|
|
|
"max": null,
|
|
|
|
"pattern": ""
|
2022-08-08 18:16:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
2022-10-30 10:28:14 +02:00
|
|
|
"id":"sz5l5z67tg7gku0",
|
|
|
|
"name":"demo2",
|
2022-08-08 18:16:33 +02:00
|
|
|
"schema":[
|
|
|
|
{
|
|
|
|
"id":"_2hlxbmp",
|
|
|
|
"name":"title",
|
|
|
|
"type":"text",
|
|
|
|
"system":false,
|
|
|
|
"required":true,
|
|
|
|
"unique":false,
|
|
|
|
"options":{
|
|
|
|
"min":3,
|
|
|
|
"max":null,
|
|
|
|
"pattern":""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"id": "test_deleted_collection_name_reuse",
|
2022-10-30 10:28:14 +02:00
|
|
|
"name": "demo1",
|
2022-08-08 18:16:33 +02:00
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: false,
|
|
|
|
expectCollectionsCount: 3,
|
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeUpdate": 2,
|
|
|
|
"OnModelAfterUpdate": 2,
|
|
|
|
"OnModelBeforeCreate": 1,
|
|
|
|
"OnModelAfterCreate": 1,
|
2023-02-18 19:33:42 +02:00
|
|
|
"OnModelBeforeDelete": totalCollections - 2,
|
|
|
|
"OnModelAfterDelete": totalCollections - 2,
|
2022-08-08 18:16:33 +02:00
|
|
|
},
|
|
|
|
},
|
2023-03-14 01:31:30 +02:00
|
|
|
{
|
|
|
|
name: "lazy system table name error",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
|
|
|
"name": "_admins",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: true,
|
|
|
|
expectCollectionsCount: totalCollections,
|
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeCreate": 1,
|
|
|
|
},
|
|
|
|
},
|
2023-03-12 16:43:27 +02:00
|
|
|
{
|
|
|
|
name: "lazy view evaluation",
|
|
|
|
jsonData: `{
|
|
|
|
"collections": [
|
|
|
|
{
|
|
|
|
"name": "view_before",
|
|
|
|
"type": "view",
|
|
|
|
"options": {
|
|
|
|
"query": "select id, active from base_test"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "base_test",
|
|
|
|
"schema": [
|
|
|
|
{
|
|
|
|
"id":"fz6iql2m",
|
|
|
|
"name":"active",
|
|
|
|
"type":"bool"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "view_after_new",
|
|
|
|
"type": "view",
|
|
|
|
"options": {
|
|
|
|
"query": "select id, active from base_test"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "view_after_old",
|
|
|
|
"type": "view",
|
|
|
|
"options": {
|
|
|
|
"query": "select id from demo1"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}`,
|
|
|
|
expectError: false,
|
|
|
|
expectCollectionsCount: totalCollections + 4,
|
|
|
|
expectEvents: map[string]int{
|
|
|
|
"OnModelBeforeUpdate": 3,
|
|
|
|
"OnModelAfterUpdate": 3,
|
|
|
|
"OnModelBeforeCreate": 4,
|
|
|
|
"OnModelAfterCreate": 4,
|
|
|
|
},
|
|
|
|
},
|
2022-08-08 18:16:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range scenarios {
|
|
|
|
testApp, _ := tests.NewTestApp()
|
|
|
|
defer testApp.Cleanup()
|
|
|
|
|
|
|
|
form := forms.NewCollectionsImport(testApp)
|
|
|
|
|
|
|
|
// load data
|
|
|
|
loadErr := json.Unmarshal([]byte(s.jsonData), form)
|
|
|
|
if loadErr != nil {
|
|
|
|
t.Errorf("[%s] Failed to load form data: %v", s.name, loadErr)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
err := form.Submit()
|
|
|
|
|
|
|
|
hasErr := err != nil
|
|
|
|
if hasErr != s.expectError {
|
|
|
|
t.Errorf("[%s] Expected hasErr to be %v, got %v (%v)", s.name, s.expectError, hasErr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check collections count
|
|
|
|
collections := []*models.Collection{}
|
|
|
|
if err := testApp.Dao().CollectionQuery().All(&collections); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(collections) != s.expectCollectionsCount {
|
|
|
|
t.Errorf("[%s] Expected %d collections, got %d", s.name, s.expectCollectionsCount, len(collections))
|
|
|
|
}
|
|
|
|
|
|
|
|
// check events
|
|
|
|
if len(testApp.EventCalls) > len(s.expectEvents) {
|
|
|
|
t.Errorf("[%s] Expected events %v, got %v", s.name, s.expectEvents, testApp.EventCalls)
|
|
|
|
}
|
|
|
|
for event, expectedCalls := range s.expectEvents {
|
|
|
|
actualCalls := testApp.EventCalls[event]
|
|
|
|
if actualCalls != expectedCalls {
|
|
|
|
t.Errorf("[%s] Expected event %s to be called %d, got %d", s.name, event, expectedCalls, actualCalls)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCollectionsImportSubmitInterceptors(t *testing.T) {
|
|
|
|
app, _ := tests.NewTestApp()
|
|
|
|
defer app.Cleanup()
|
|
|
|
|
|
|
|
collections := []*models.Collection{}
|
|
|
|
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
form := forms.NewCollectionsImport(app)
|
|
|
|
form.Collections = collections
|
|
|
|
|
|
|
|
testErr := errors.New("test_error")
|
|
|
|
|
|
|
|
interceptor1Called := false
|
2023-01-15 17:00:28 +02:00
|
|
|
interceptor1 := func(next forms.InterceptorNextFunc[[]*models.Collection]) forms.InterceptorNextFunc[[]*models.Collection] {
|
|
|
|
return func(imports []*models.Collection) error {
|
2022-08-08 18:16:33 +02:00
|
|
|
interceptor1Called = true
|
2023-01-15 17:00:28 +02:00
|
|
|
return next(imports)
|
2022-08-08 18:16:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interceptor2Called := false
|
2023-01-15 17:00:28 +02:00
|
|
|
interceptor2 := func(next forms.InterceptorNextFunc[[]*models.Collection]) forms.InterceptorNextFunc[[]*models.Collection] {
|
|
|
|
return func(imports []*models.Collection) error {
|
2022-08-08 18:16:33 +02:00
|
|
|
interceptor2Called = true
|
|
|
|
return testErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
submitErr := form.Submit(interceptor1, interceptor2)
|
|
|
|
if submitErr != testErr {
|
|
|
|
t.Fatalf("Expected submitError %v, got %v", testErr, submitErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !interceptor1Called {
|
|
|
|
t.Fatalf("Expected interceptor1 to be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !interceptor2Called {
|
|
|
|
t.Fatalf("Expected interceptor2 to be called")
|
|
|
|
}
|
|
|
|
}
|