1111 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1111 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Go
		
	
	
	
package apis_test
 | 
						|
 | 
						|
import (
 | 
						|
	"net/http"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/labstack/echo/v5"
 | 
						|
	"github.com/pocketbase/pocketbase/models"
 | 
						|
	"github.com/pocketbase/pocketbase/models/schema"
 | 
						|
	"github.com/pocketbase/pocketbase/tests"
 | 
						|
	"github.com/pocketbase/pocketbase/tools/list"
 | 
						|
)
 | 
						|
 | 
						|
func TestCollectionsList(t *testing.T) {
 | 
						|
	scenarios := []tests.ApiScenario{
 | 
						|
		{
 | 
						|
			Name:            "unauthorized",
 | 
						|
			Method:          http.MethodGet,
 | 
						|
			Url:             "/api/collections",
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as user",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"page":1`,
 | 
						|
				`"perPage":30`,
 | 
						|
				`"totalItems":8`,
 | 
						|
				`"items":[{`,
 | 
						|
				`"id":"_pb_users_auth_"`,
 | 
						|
				`"id":"v851q4r790rhknl"`,
 | 
						|
				`"id":"kpv709sk2lqbqk8"`,
 | 
						|
				`"id":"wsmn24bux7wo113"`,
 | 
						|
				`"id":"sz5l5z67tg7gku0"`,
 | 
						|
				`"id":"wzlqyes4orhoygb"`,
 | 
						|
				`"id":"4d1blo5cuycfaca"`,
 | 
						|
				`"id":"9n89pl5vkct6330"`,
 | 
						|
				`"type":"auth"`,
 | 
						|
				`"type":"base"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsListRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + paging and sorting",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections?page=2&perPage=2&sort=-created",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"page":2`,
 | 
						|
				`"perPage":2`,
 | 
						|
				`"totalItems":8`,
 | 
						|
				`"items":[{`,
 | 
						|
				`"id":"v851q4r790rhknl"`,
 | 
						|
				`"id":"4d1blo5cuycfaca"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsListRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + invalid filter",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections?filter=invalidfield~'demo2'",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  400,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + valid filter",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections?filter=name~'demo'",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"page":1`,
 | 
						|
				`"perPage":30`,
 | 
						|
				`"totalItems":5`,
 | 
						|
				`"items":[{`,
 | 
						|
				`"id":"wsmn24bux7wo113"`,
 | 
						|
				`"id":"sz5l5z67tg7gku0"`,
 | 
						|
				`"id":"wzlqyes4orhoygb"`,
 | 
						|
				`"id":"4d1blo5cuycfaca"`,
 | 
						|
				`"id":"9n89pl5vkct6330"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsListRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, scenario := range scenarios {
 | 
						|
		scenario.Test(t)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCollectionView(t *testing.T) {
 | 
						|
	scenarios := []tests.ApiScenario{
 | 
						|
		{
 | 
						|
			Name:            "unauthorized",
 | 
						|
			Method:          http.MethodGet,
 | 
						|
			Url:             "/api/collections/demo1",
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as user",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + nonexisting collection identifier",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections/missing",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  404,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + using the collection name",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"id":"wsmn24bux7wo113"`,
 | 
						|
				`"name":"demo1"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionViewRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + using the collection id",
 | 
						|
			Method: http.MethodGet,
 | 
						|
			Url:    "/api/collections/wsmn24bux7wo113",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"id":"wsmn24bux7wo113"`,
 | 
						|
				`"name":"demo1"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionViewRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, scenario := range scenarios {
 | 
						|
		scenario.Test(t)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCollectionDelete(t *testing.T) {
 | 
						|
	ensureDeletedFiles := func(app *tests.TestApp, collectionId string) {
 | 
						|
		storageDir := filepath.Join(app.DataDir(), "storage", collectionId)
 | 
						|
 | 
						|
		entries, _ := os.ReadDir(storageDir)
 | 
						|
		if len(entries) != 0 {
 | 
						|
			t.Errorf("Expected empty/deleted dir, found %d", len(entries))
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	scenarios := []tests.ApiScenario{
 | 
						|
		{
 | 
						|
			Name:            "unauthorized",
 | 
						|
			Method:          http.MethodDelete,
 | 
						|
			Url:             "/api/collections/demo1",
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as user",
 | 
						|
			Method: http.MethodDelete,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + nonexisting collection identifier",
 | 
						|
			Method: http.MethodDelete,
 | 
						|
			Url:    "/api/collections/missing",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  404,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + using the collection name",
 | 
						|
			Method: http.MethodDelete,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			Delay:          100 * time.Millisecond,
 | 
						|
			ExpectedStatus: 204,
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeDelete":             1,
 | 
						|
				"OnModelAfterDelete":              1,
 | 
						|
				"OnCollectionBeforeDeleteRequest": 1,
 | 
						|
				"OnCollectionAfterDeleteRequest":  1,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				ensureDeletedFiles(app, "wsmn24bux7wo113")
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + using the collection id",
 | 
						|
			Method: http.MethodDelete,
 | 
						|
			Url:    "/api/collections/wsmn24bux7wo113",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			Delay:          100 * time.Millisecond,
 | 
						|
			ExpectedStatus: 204,
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeDelete":             1,
 | 
						|
				"OnModelAfterDelete":              1,
 | 
						|
				"OnCollectionBeforeDeleteRequest": 1,
 | 
						|
				"OnCollectionAfterDeleteRequest":  1,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				ensureDeletedFiles(app, "wsmn24bux7wo113")
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + trying to delete a system collection",
 | 
						|
			Method: http.MethodDelete,
 | 
						|
			Url:    "/api/collections/nologin",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  400,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionBeforeDeleteRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + trying to delete a referenced collection",
 | 
						|
			Method: http.MethodDelete,
 | 
						|
			Url:    "/api/collections/demo2",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  400,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionBeforeDeleteRequest": 1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, scenario := range scenarios {
 | 
						|
		scenario.Test(t)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCollectionCreate(t *testing.T) {
 | 
						|
	scenarios := []tests.ApiScenario{
 | 
						|
		{
 | 
						|
			Name:            "unauthorized",
 | 
						|
			Method:          http.MethodPost,
 | 
						|
			Url:             "/api/collections",
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as user",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + empty data",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body:   strings.NewReader(``),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"name":{"code":"validation_required"`,
 | 
						|
				`"schema":{"code":"validation_required"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + invalid data (eg. existing name)",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body:   strings.NewReader(`{"name":"demo1","type":"base","schema":[{"type":"text","name":""}]}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"name":{"code":"validation_collection_name_exists"`,
 | 
						|
				`"schema":{"0":{"name":{"code":"validation_required"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + valid data",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body:   strings.NewReader(`{"name":"new","type":"base","schema":[{"type":"text","id":"12345789","name":"test"}]}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"id":`,
 | 
						|
				`"name":"new"`,
 | 
						|
				`"type":"base"`,
 | 
						|
				`"system":false`,
 | 
						|
				`"schema":[{"system":false,"id":"12345789","name":"test","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
 | 
						|
				`"options":{}`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeCreate":             1,
 | 
						|
				"OnModelAfterCreate":              1,
 | 
						|
				"OnCollectionBeforeCreateRequest": 1,
 | 
						|
				"OnCollectionAfterCreateRequest":  1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "creating auth collection without specified options",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body:   strings.NewReader(`{"name":"new","type":"auth","schema":[{"type":"text","id":"12345789","name":"test"}]}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"id":`,
 | 
						|
				`"name":"new"`,
 | 
						|
				`"type":"auth"`,
 | 
						|
				`"system":false`,
 | 
						|
				`"schema":[{"system":false,"id":"12345789","name":"test","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
 | 
						|
				`"options":{"allowEmailAuth":false,"allowOAuth2Auth":false,"allowUsernameAuth":false,"exceptEmailDomains":null,"manageRule":null,"minPasswordLength":0,"onlyEmailDomains":null,"requireEmail":false}`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeCreate":             1,
 | 
						|
				"OnModelAfterCreate":              1,
 | 
						|
				"OnCollectionBeforeCreateRequest": 1,
 | 
						|
				"OnCollectionAfterCreateRequest":  1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "trying to create auth collection with reserved auth fields",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"name":"new",
 | 
						|
				"type":"auth",
 | 
						|
				"schema":[
 | 
						|
					{"type":"text","name":"email"},
 | 
						|
					{"type":"text","name":"username"},
 | 
						|
					{"type":"text","name":"verified"},
 | 
						|
					{"type":"text","name":"emailVisibility"},
 | 
						|
					{"type":"text","name":"lastResetSentAt"},
 | 
						|
					{"type":"text","name":"lastVerificationSentAt"},
 | 
						|
					{"type":"text","name":"tokenKey"},
 | 
						|
					{"type":"text","name":"passwordHash"},
 | 
						|
					{"type":"text","name":"password"},
 | 
						|
					{"type":"text","name":"passwordConfirm"},
 | 
						|
					{"type":"text","name":"oldPassword"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{"schema":{`,
 | 
						|
				`"0":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"1":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"2":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"3":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"4":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"5":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"6":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"7":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"8":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "creating base collection with reserved auth fields",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"name":"new",
 | 
						|
				"type":"base",
 | 
						|
				"schema":[
 | 
						|
					{"type":"text","name":"email"},
 | 
						|
					{"type":"text","name":"username"},
 | 
						|
					{"type":"text","name":"verified"},
 | 
						|
					{"type":"text","name":"emailVisibility"},
 | 
						|
					{"type":"text","name":"lastResetSentAt"},
 | 
						|
					{"type":"text","name":"lastVerificationSentAt"},
 | 
						|
					{"type":"text","name":"tokenKey"},
 | 
						|
					{"type":"text","name":"passwordHash"},
 | 
						|
					{"type":"text","name":"password"},
 | 
						|
					{"type":"text","name":"passwordConfirm"},
 | 
						|
					{"type":"text","name":"oldPassword"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"name":"new"`,
 | 
						|
				`"type":"base"`,
 | 
						|
				`"schema":[{`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeCreate":             1,
 | 
						|
				"OnModelAfterCreate":              1,
 | 
						|
				"OnCollectionBeforeCreateRequest": 1,
 | 
						|
				"OnCollectionAfterCreateRequest":  1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "trying to create base collection with reserved base fields",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"name":"new",
 | 
						|
				"type":"base",
 | 
						|
				"schema":[
 | 
						|
					{"type":"text","name":"id"},
 | 
						|
					{"type":"text","name":"created"},
 | 
						|
					{"type":"text","name":"updated"},
 | 
						|
					{"type":"text","name":"expand"},
 | 
						|
					{"type":"text","name":"collectionId"},
 | 
						|
					{"type":"text","name":"collectionName"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{"schema":{`,
 | 
						|
				`"0":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"1":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"2":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"3":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"4":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"5":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "trying to create auth collection with invalid options",
 | 
						|
			Method: http.MethodPost,
 | 
						|
			Url:    "/api/collections",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"name":"new",
 | 
						|
				"type":"auth",
 | 
						|
				"schema":[{"type":"text","id":"12345789","name":"test"}],
 | 
						|
				"options":{"allowUsernameAuth": true}
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"options":{"minPasswordLength":{"code":"validation_required"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, scenario := range scenarios {
 | 
						|
		scenario.Test(t)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCollectionUpdate(t *testing.T) {
 | 
						|
	scenarios := []tests.ApiScenario{
 | 
						|
		{
 | 
						|
			Name:            "unauthorized",
 | 
						|
			Method:          http.MethodPatch,
 | 
						|
			Url:             "/api/collections/demo1",
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as user",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + missing collection",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/missing",
 | 
						|
			Body:   strings.NewReader(`{}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  404,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + empty body",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			Body:   strings.NewReader(`{}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"id":"wsmn24bux7wo113"`,
 | 
						|
				`"name":"demo1"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionAfterUpdateRequest":  1,
 | 
						|
				"OnCollectionBeforeUpdateRequest": 1,
 | 
						|
				"OnModelAfterUpdate":              1,
 | 
						|
				"OnModelBeforeUpdate":             1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + invalid data (eg. existing name)",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"name":"demo2",
 | 
						|
				"type":"auth"
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"name":{"code":"validation_collection_name_exists"`,
 | 
						|
				`"type":{"code":"validation_collection_type_change"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + valid data",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			Body:   strings.NewReader(`{"name":"new"}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"id":`,
 | 
						|
				`"name":"new"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeUpdate":             1,
 | 
						|
				"OnModelAfterUpdate":              1,
 | 
						|
				"OnCollectionBeforeUpdateRequest": 1,
 | 
						|
				"OnCollectionAfterUpdateRequest":  1,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				// check if the record table was renamed
 | 
						|
				if !app.Dao().HasTable("new") {
 | 
						|
					t.Fatal("Couldn't find record table 'new'.")
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "trying to update auth collection with reserved auth fields",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/users",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"schema":[
 | 
						|
					{"type":"text","name":"email"},
 | 
						|
					{"type":"text","name":"username"},
 | 
						|
					{"type":"text","name":"verified"},
 | 
						|
					{"type":"text","name":"emailVisibility"},
 | 
						|
					{"type":"text","name":"lastResetSentAt"},
 | 
						|
					{"type":"text","name":"lastVerificationSentAt"},
 | 
						|
					{"type":"text","name":"tokenKey"},
 | 
						|
					{"type":"text","name":"passwordHash"},
 | 
						|
					{"type":"text","name":"password"},
 | 
						|
					{"type":"text","name":"passwordConfirm"},
 | 
						|
					{"type":"text","name":"oldPassword"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{"schema":{`,
 | 
						|
				`"0":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"1":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"2":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"3":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"4":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"5":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"6":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"7":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
				`"8":{"name":{"code":"validation_reserved_auth_field_name"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "updating base collection with reserved auth fields",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"schema":[
 | 
						|
					{"type":"text","name":"email"},
 | 
						|
					{"type":"text","name":"username"},
 | 
						|
					{"type":"text","name":"verified"},
 | 
						|
					{"type":"text","name":"emailVisibility"},
 | 
						|
					{"type":"text","name":"lastResetSentAt"},
 | 
						|
					{"type":"text","name":"lastVerificationSentAt"},
 | 
						|
					{"type":"text","name":"tokenKey"},
 | 
						|
					{"type":"text","name":"passwordHash"},
 | 
						|
					{"type":"text","name":"password"},
 | 
						|
					{"type":"text","name":"passwordConfirm"},
 | 
						|
					{"type":"text","name":"oldPassword"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"name":"demo1"`,
 | 
						|
				`"type":"base"`,
 | 
						|
				`"schema":[{`,
 | 
						|
				`"email"`,
 | 
						|
				`"username"`,
 | 
						|
				`"verified"`,
 | 
						|
				`"emailVisibility"`,
 | 
						|
				`"lastResetSentAt"`,
 | 
						|
				`"lastVerificationSentAt"`,
 | 
						|
				`"tokenKey"`,
 | 
						|
				`"passwordHash"`,
 | 
						|
				`"password"`,
 | 
						|
				`"passwordConfirm"`,
 | 
						|
				`"oldPassword"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeUpdate":             1,
 | 
						|
				"OnModelAfterUpdate":              1,
 | 
						|
				"OnCollectionBeforeUpdateRequest": 1,
 | 
						|
				"OnCollectionAfterUpdateRequest":  1,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "trying to update base collection with reserved base fields",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo1",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"name":"new",
 | 
						|
				"type":"base",
 | 
						|
				"schema":[
 | 
						|
					{"type":"text","name":"id"},
 | 
						|
					{"type":"text","name":"created"},
 | 
						|
					{"type":"text","name":"updated"},
 | 
						|
					{"type":"text","name":"expand"},
 | 
						|
					{"type":"text","name":"collectionId"},
 | 
						|
					{"type":"text","name":"collectionName"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{"schema":{`,
 | 
						|
				`"0":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"1":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"2":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"3":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"4":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
				`"5":{"name":{"code":"validation_not_in_invalid`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "trying to update auth collection with invalid options",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/users",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"options":{"minPasswordLength": 4}
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"options":{"minPasswordLength":{"code":"validation_min_greater_equal_than_required"`,
 | 
						|
			},
 | 
						|
		},
 | 
						|
 | 
						|
		// rel field change displayFields propagation
 | 
						|
		{
 | 
						|
			Name:   "renaming a display field should also update the referenced displayFields value",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo3",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"schema":[
 | 
						|
					{
 | 
						|
						"id": "w5z2x0nq",
 | 
						|
						"type": "text",
 | 
						|
						"name": "title_change"
 | 
						|
					}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"name":"title_change"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeUpdate":             2,
 | 
						|
				"OnModelAfterUpdate":              2,
 | 
						|
				"OnCollectionBeforeUpdateRequest": 1,
 | 
						|
				"OnCollectionAfterUpdateRequest":  1,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collection, err := app.Dao().FindCollectionByNameOrId("demo4")
 | 
						|
				if err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
 | 
						|
				relField := collection.Schema.GetFieldByName("rel_many_no_cascade_required")
 | 
						|
				options := relField.Options.(*schema.RelationOptions)
 | 
						|
				expectedDisplayFields := []string{"title_change", "id"}
 | 
						|
				if len(list.SubtractSlice(options.DisplayFields, expectedDisplayFields)) != 0 {
 | 
						|
					t.Fatalf("Expected displayFields %v, got %v", expectedDisplayFields, options.DisplayFields)
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "deleting a display field should also update the referenced displayFields value",
 | 
						|
			Method: http.MethodPatch,
 | 
						|
			Url:    "/api/collections/demo3",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"schema":[
 | 
						|
					{
 | 
						|
						"type": "text",
 | 
						|
						"name": "new_field"
 | 
						|
					}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 200,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"name":"new_field"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnModelBeforeUpdate":             2,
 | 
						|
				"OnModelAfterUpdate":              2,
 | 
						|
				"OnCollectionBeforeUpdateRequest": 1,
 | 
						|
				"OnCollectionAfterUpdateRequest":  1,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collection, err := app.Dao().FindCollectionByNameOrId("demo4")
 | 
						|
				if err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
 | 
						|
				relField := collection.Schema.GetFieldByName("rel_many_no_cascade_required")
 | 
						|
				options := relField.Options.(*schema.RelationOptions)
 | 
						|
				expectedDisplayFields := []string{"id"}
 | 
						|
				if len(list.SubtractSlice(options.DisplayFields, expectedDisplayFields)) != 0 {
 | 
						|
					t.Fatalf("Expected displayFields %v, got %v", expectedDisplayFields, options.DisplayFields)
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, scenario := range scenarios {
 | 
						|
		scenario.Test(t)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCollectionImport(t *testing.T) {
 | 
						|
	scenarios := []tests.ApiScenario{
 | 
						|
		{
 | 
						|
			Name:            "unauthorized",
 | 
						|
			Method:          http.MethodPut,
 | 
						|
			Url:             "/api/collections/import",
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as user",
 | 
						|
			Method: http.MethodPut,
 | 
						|
			Url:    "/api/collections/import",
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
 | 
						|
			},
 | 
						|
			ExpectedStatus:  401,
 | 
						|
			ExpectedContent: []string{`"data":{}`},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + empty collections",
 | 
						|
			Method: http.MethodPut,
 | 
						|
			Url:    "/api/collections/import",
 | 
						|
			Body:   strings.NewReader(`{"collections":[]}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"collections":{"code":"validation_required"`,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collections := []*models.Collection{}
 | 
						|
				if err := app.Dao().CollectionQuery().All(&collections); err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
				expected := 8
 | 
						|
				if len(collections) != expected {
 | 
						|
					t.Fatalf("Expected %d collections, got %d", expected, len(collections))
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + trying to delete system collections",
 | 
						|
			Method: http.MethodPut,
 | 
						|
			Url:    "/api/collections/import",
 | 
						|
			Body:   strings.NewReader(`{"deleteMissing": true, "collections":[{"name": "test123"}]}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"collections":{"code":"collections_import_failure"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsBeforeImportRequest": 1,
 | 
						|
				"OnModelBeforeDelete":              6,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collections := []*models.Collection{}
 | 
						|
				if err := app.Dao().CollectionQuery().All(&collections); err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
				expected := 8
 | 
						|
				if len(collections) != expected {
 | 
						|
					t.Fatalf("Expected %d collections, got %d", expected, len(collections))
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + collections validator failure",
 | 
						|
			Method: http.MethodPut,
 | 
						|
			Url:    "/api/collections/import",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"collections":[
 | 
						|
					{
 | 
						|
						"name": "import1",
 | 
						|
						"schema": [
 | 
						|
							{
 | 
						|
							  "id": "koih1lqx",
 | 
						|
							  "name": "test",
 | 
						|
							  "type": "text"
 | 
						|
							}
 | 
						|
						]
 | 
						|
					},
 | 
						|
					{"name": "import2"}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 400,
 | 
						|
			ExpectedContent: []string{
 | 
						|
				`"data":{`,
 | 
						|
				`"collections":{"code":"collections_import_validate_failure"`,
 | 
						|
			},
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsBeforeImportRequest": 1,
 | 
						|
				"OnModelBeforeCreate":              2,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collections := []*models.Collection{}
 | 
						|
				if err := app.Dao().CollectionQuery().All(&collections); err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
				expected := 8
 | 
						|
				if len(collections) != expected {
 | 
						|
					t.Fatalf("Expected %d collections, got %d", expected, len(collections))
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + successful collections save",
 | 
						|
			Method: http.MethodPut,
 | 
						|
			Url:    "/api/collections/import",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"collections":[
 | 
						|
					{
 | 
						|
						"name": "import1",
 | 
						|
						"schema": [
 | 
						|
							{
 | 
						|
							  "id": "koih1lqx",
 | 
						|
							  "name": "test",
 | 
						|
							  "type": "text"
 | 
						|
							}
 | 
						|
						]
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"name": "import2",
 | 
						|
						"schema": [
 | 
						|
							{
 | 
						|
							  "id": "koih1lqx",
 | 
						|
							  "name": "test",
 | 
						|
							  "type": "text"
 | 
						|
							}
 | 
						|
						]
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"name": "auth_without_schema",
 | 
						|
						"type": "auth"
 | 
						|
					}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 204,
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsBeforeImportRequest": 1,
 | 
						|
				"OnCollectionsAfterImportRequest":  1,
 | 
						|
				"OnModelBeforeCreate":              3,
 | 
						|
				"OnModelAfterCreate":               3,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collections := []*models.Collection{}
 | 
						|
				if err := app.Dao().CollectionQuery().All(&collections); err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
				expected := 11
 | 
						|
				if len(collections) != expected {
 | 
						|
					t.Fatalf("Expected %d collections, got %d", expected, len(collections))
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			Name:   "authorized as admin + successful collections save and old non-system collections deletion",
 | 
						|
			Method: http.MethodPut,
 | 
						|
			Url:    "/api/collections/import",
 | 
						|
			Body: strings.NewReader(`{
 | 
						|
				"deleteMissing": true,
 | 
						|
				"collections":[
 | 
						|
					{
 | 
						|
						"name": "new_import",
 | 
						|
						"schema": [
 | 
						|
							{
 | 
						|
							  "id": "koih1lqx",
 | 
						|
							  "name": "test",
 | 
						|
							  "type": "text"
 | 
						|
							}
 | 
						|
						]
 | 
						|
					},
 | 
						|
					{
 | 
						|
					    "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": [
 | 
						|
					        {
 | 
						|
					            "id": "x8zzktwe",
 | 
						|
					            "name": "name",
 | 
						|
					            "type": "text",
 | 
						|
					            "system": false,
 | 
						|
					            "required": false,
 | 
						|
					            "unique": false,
 | 
						|
					            "options": {
 | 
						|
					                "min": null,
 | 
						|
					                "max": null,
 | 
						|
					                "pattern": ""
 | 
						|
					            }
 | 
						|
					        }
 | 
						|
					    ]
 | 
						|
					},
 | 
						|
					{
 | 
						|
						"id":"wsmn24bux7wo113",
 | 
						|
						"name":"demo1",
 | 
						|
						"schema":[
 | 
						|
							{
 | 
						|
								"id":"_2hlxbmp",
 | 
						|
								"name":"title",
 | 
						|
								"type":"text",
 | 
						|
								"system":false,
 | 
						|
								"required":true,
 | 
						|
								"unique":false,
 | 
						|
								"options":{
 | 
						|
									"min":3,
 | 
						|
									"max":null,
 | 
						|
									"pattern":""
 | 
						|
								}
 | 
						|
							}
 | 
						|
						]
 | 
						|
					}
 | 
						|
				]
 | 
						|
			}`),
 | 
						|
			RequestHeaders: map[string]string{
 | 
						|
				"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
 | 
						|
			},
 | 
						|
			ExpectedStatus: 204,
 | 
						|
			ExpectedEvents: map[string]int{
 | 
						|
				"OnCollectionsAfterImportRequest":  1,
 | 
						|
				"OnCollectionsBeforeImportRequest": 1,
 | 
						|
				"OnModelBeforeDelete":              6,
 | 
						|
				"OnModelAfterDelete":               6,
 | 
						|
				"OnModelBeforeUpdate":              2,
 | 
						|
				"OnModelAfterUpdate":               2,
 | 
						|
				"OnModelBeforeCreate":              1,
 | 
						|
				"OnModelAfterCreate":               1,
 | 
						|
			},
 | 
						|
			AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
 | 
						|
				collections := []*models.Collection{}
 | 
						|
				if err := app.Dao().CollectionQuery().All(&collections); err != nil {
 | 
						|
					t.Fatal(err)
 | 
						|
				}
 | 
						|
				expected := 3
 | 
						|
				if len(collections) != expected {
 | 
						|
					t.Fatalf("Expected %d collections, got %d", expected, len(collections))
 | 
						|
				}
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, scenario := range scenarios {
 | 
						|
		scenario.Test(t)
 | 
						|
	}
 | 
						|
}
 |