2022-12-12 19:19:31 +02:00
|
|
|
package daos_test
|
|
|
|
|
|
|
|
import (
|
2023-03-06 15:20:07 +02:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2022-12-12 19:19:31 +02:00
|
|
|
"testing"
|
|
|
|
|
2023-03-06 15:20:07 +02:00
|
|
|
"github.com/pocketbase/dbx"
|
2022-12-12 19:19:31 +02:00
|
|
|
"github.com/pocketbase/pocketbase/models"
|
|
|
|
"github.com/pocketbase/pocketbase/models/schema"
|
|
|
|
"github.com/pocketbase/pocketbase/tests"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/list"
|
2023-03-06 15:20:07 +02:00
|
|
|
"github.com/pocketbase/pocketbase/tools/types"
|
2022-12-12 19:19:31 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestSyncRecordTableSchema(t *testing.T) {
|
|
|
|
app, _ := tests.NewTestApp()
|
|
|
|
defer app.Cleanup()
|
|
|
|
|
|
|
|
oldCollection, err := app.Dao().FindCollectionByNameOrId("demo2")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
updatedCollection, err := app.Dao().FindCollectionByNameOrId("demo2")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
updatedCollection.Name = "demo_renamed"
|
|
|
|
updatedCollection.Schema.RemoveField(updatedCollection.Schema.GetFieldByName("active").Id)
|
|
|
|
updatedCollection.Schema.AddField(
|
|
|
|
&schema.SchemaField{
|
|
|
|
Name: "new_field",
|
|
|
|
Type: schema.FieldTypeEmail,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
updatedCollection.Schema.AddField(
|
|
|
|
&schema.SchemaField{
|
|
|
|
Id: updatedCollection.Schema.GetFieldByName("title").Id,
|
|
|
|
Name: "title_renamed",
|
|
|
|
Type: schema.FieldTypeEmail,
|
|
|
|
},
|
|
|
|
)
|
2023-03-22 17:12:44 +02:00
|
|
|
updatedCollection.Indexes = types.JsonArray[string]{"create index idx_title_renamed on anything (title_renamed)"}
|
2022-12-12 19:19:31 +02:00
|
|
|
|
|
|
|
scenarios := []struct {
|
2023-03-21 15:31:20 +02:00
|
|
|
name string
|
|
|
|
newCollection *models.Collection
|
|
|
|
oldCollection *models.Collection
|
|
|
|
expectedColumns []string
|
|
|
|
expectedIndexesCount int
|
2022-12-12 19:19:31 +02:00
|
|
|
}{
|
|
|
|
{
|
2023-03-21 15:31:20 +02:00
|
|
|
"new base collection",
|
2022-12-12 19:19:31 +02:00
|
|
|
&models.Collection{
|
|
|
|
Name: "new_table",
|
|
|
|
Schema: schema.NewSchema(
|
|
|
|
&schema.SchemaField{
|
|
|
|
Name: "test",
|
|
|
|
Type: schema.FieldTypeText,
|
|
|
|
},
|
|
|
|
),
|
|
|
|
},
|
|
|
|
nil,
|
|
|
|
[]string{"id", "created", "updated", "test"},
|
2023-03-21 15:31:20 +02:00
|
|
|
0,
|
2022-12-12 19:19:31 +02:00
|
|
|
},
|
|
|
|
{
|
2023-03-21 15:31:20 +02:00
|
|
|
"new auth collection",
|
2022-12-12 19:19:31 +02:00
|
|
|
&models.Collection{
|
|
|
|
Name: "new_table_auth",
|
|
|
|
Type: models.CollectionTypeAuth,
|
|
|
|
Schema: schema.NewSchema(
|
|
|
|
&schema.SchemaField{
|
|
|
|
Name: "test",
|
|
|
|
Type: schema.FieldTypeText,
|
|
|
|
},
|
|
|
|
),
|
2023-03-22 17:12:44 +02:00
|
|
|
Indexes: types.JsonArray[string]{"create index idx_auth_test on anything (email, username)"},
|
2022-12-12 19:19:31 +02:00
|
|
|
},
|
|
|
|
nil,
|
|
|
|
[]string{
|
|
|
|
"id", "created", "updated", "test",
|
|
|
|
"username", "email", "verified", "emailVisibility",
|
|
|
|
"tokenKey", "passwordHash", "lastResetSentAt", "lastVerificationSentAt",
|
|
|
|
},
|
2023-03-21 15:31:20 +02:00
|
|
|
4,
|
2022-12-12 19:19:31 +02:00
|
|
|
},
|
|
|
|
{
|
2023-03-21 15:31:20 +02:00
|
|
|
"no changes",
|
2022-12-12 19:19:31 +02:00
|
|
|
oldCollection,
|
|
|
|
oldCollection,
|
|
|
|
[]string{"id", "created", "updated", "title", "active"},
|
2023-03-21 15:31:20 +02:00
|
|
|
3,
|
2022-12-12 19:19:31 +02:00
|
|
|
},
|
|
|
|
{
|
2023-03-21 15:31:20 +02:00
|
|
|
"renamed table, deleted column, renamed columnd and new column",
|
2022-12-12 19:19:31 +02:00
|
|
|
updatedCollection,
|
|
|
|
oldCollection,
|
|
|
|
[]string{"id", "created", "updated", "title_renamed", "new_field"},
|
2023-03-21 15:31:20 +02:00
|
|
|
1,
|
2022-12-12 19:19:31 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-03-21 15:31:20 +02:00
|
|
|
for _, s := range scenarios {
|
|
|
|
err := app.Dao().SyncRecordTableSchema(s.newCollection, s.oldCollection)
|
2022-12-12 19:19:31 +02:00
|
|
|
if err != nil {
|
2023-03-21 15:31:20 +02:00
|
|
|
t.Errorf("[%s] %v", s.name, err)
|
2022-12-12 19:19:31 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-03-21 15:31:20 +02:00
|
|
|
if !app.Dao().HasTable(s.newCollection.Name) {
|
|
|
|
t.Errorf("[%s] Expected table %s to exist", s.name, s.newCollection.Name)
|
2022-12-12 19:19:31 +02:00
|
|
|
}
|
|
|
|
|
2023-03-22 17:15:17 +02:00
|
|
|
cols, _ := app.Dao().TableColumns(s.newCollection.Name)
|
2023-03-21 15:31:20 +02:00
|
|
|
if len(cols) != len(s.expectedColumns) {
|
|
|
|
t.Errorf("[%s] Expected columns %v, got %v", s.name, s.expectedColumns, cols)
|
2022-12-12 19:19:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cols {
|
2023-03-21 15:31:20 +02:00
|
|
|
if !list.ExistInSlice(c, s.expectedColumns) {
|
|
|
|
t.Errorf("[%s] Couldn't find column %s in %v", s.name, c, s.expectedColumns)
|
2022-12-12 19:19:31 +02:00
|
|
|
}
|
|
|
|
}
|
2023-03-21 15:31:20 +02:00
|
|
|
|
|
|
|
indexes, _ := app.Dao().TableIndexes(s.newCollection.Name)
|
|
|
|
|
|
|
|
if totalIndexes := len(indexes); totalIndexes != s.expectedIndexesCount {
|
|
|
|
t.Errorf("[%s] Expected %d indexes, got %d:\n%v", s.name, s.expectedIndexesCount, totalIndexes, indexes)
|
|
|
|
}
|
2022-12-12 19:19:31 +02:00
|
|
|
}
|
|
|
|
}
|
2023-03-06 15:20:07 +02:00
|
|
|
|
|
|
|
func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|
|
|
app, _ := tests.NewTestApp()
|
|
|
|
defer app.Cleanup()
|
|
|
|
|
|
|
|
collection, err := app.Dao().FindCollectionByNameOrId("demo1")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// mock field changes
|
|
|
|
{
|
|
|
|
selectOneField := collection.Schema.GetFieldByName("select_one")
|
|
|
|
opt := selectOneField.Options.(*schema.SelectOptions)
|
|
|
|
opt.MaxSelect = 2
|
|
|
|
}
|
|
|
|
{
|
|
|
|
selectManyField := collection.Schema.GetFieldByName("select_many")
|
|
|
|
opt := selectManyField.Options.(*schema.SelectOptions)
|
|
|
|
opt.MaxSelect = 1
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fileOneField := collection.Schema.GetFieldByName("file_one")
|
|
|
|
opt := fileOneField.Options.(*schema.FileOptions)
|
|
|
|
opt.MaxSelect = 2
|
|
|
|
}
|
|
|
|
{
|
|
|
|
fileManyField := collection.Schema.GetFieldByName("file_many")
|
|
|
|
opt := fileManyField.Options.(*schema.FileOptions)
|
|
|
|
opt.MaxSelect = 1
|
|
|
|
}
|
|
|
|
{
|
|
|
|
relOneField := collection.Schema.GetFieldByName("rel_one")
|
|
|
|
opt := relOneField.Options.(*schema.RelationOptions)
|
|
|
|
opt.MaxSelect = types.Pointer(2)
|
|
|
|
}
|
|
|
|
{
|
|
|
|
relManyField := collection.Schema.GetFieldByName("rel_many")
|
|
|
|
opt := relManyField.Options.(*schema.RelationOptions)
|
|
|
|
opt.MaxSelect = types.Pointer(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := app.Dao().SaveCollection(collection); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
type expectation struct {
|
|
|
|
SelectOne string `db:"select_one"`
|
|
|
|
SelectMany string `db:"select_many"`
|
|
|
|
FileOne string `db:"file_one"`
|
|
|
|
FileMany string `db:"file_many"`
|
|
|
|
RelOne string `db:"rel_one"`
|
|
|
|
RelMany string `db:"rel_many"`
|
|
|
|
}
|
|
|
|
|
|
|
|
scenarios := []struct {
|
|
|
|
recordId string
|
|
|
|
expected expectation
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"imy661ixudk5izi",
|
|
|
|
expectation{
|
|
|
|
SelectOne: `[]`,
|
|
|
|
SelectMany: ``,
|
|
|
|
FileOne: `[]`,
|
|
|
|
FileMany: ``,
|
|
|
|
RelOne: `[]`,
|
|
|
|
RelMany: ``,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"al1h9ijdeojtsjy",
|
|
|
|
expectation{
|
|
|
|
SelectOne: `["optionB"]`,
|
|
|
|
SelectMany: `optionB`,
|
|
|
|
FileOne: `["300_Jsjq7RdBgA.png"]`,
|
|
|
|
FileMany: ``,
|
|
|
|
RelOne: `["84nmscqy84lsi1t"]`,
|
|
|
|
RelMany: `oap640cot4yru2s`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"84nmscqy84lsi1t",
|
|
|
|
expectation{
|
|
|
|
SelectOne: `["optionB"]`,
|
|
|
|
SelectMany: `optionC`,
|
|
|
|
FileOne: `["test_d61b33QdDU.txt"]`,
|
|
|
|
FileMany: `test_tC1Yc87DfC.txt`,
|
|
|
|
RelOne: `[]`,
|
|
|
|
RelMany: `oap640cot4yru2s`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range scenarios {
|
|
|
|
result := new(expectation)
|
|
|
|
|
|
|
|
err := app.Dao().DB().Select(
|
|
|
|
"select_one",
|
|
|
|
"select_many",
|
|
|
|
"file_one",
|
|
|
|
"file_many",
|
|
|
|
"rel_one",
|
|
|
|
"rel_many",
|
|
|
|
).From(collection.Name).Where(dbx.HashExp{"id": s.recordId}).One(result)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("[%s] Failed to load record: %v", s.recordId, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedResult, err := json.Marshal(result)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("[%s] Failed to encode result: %v", s.recordId, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedExpectation, err := json.Marshal(s.expected)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("[%s] Failed to encode expectation: %v", s.recordId, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !bytes.EqualFold(encodedExpectation, encodedResult) {
|
|
|
|
t.Errorf("[%s] Expected \n%s, \ngot \n%s", s.recordId, encodedExpectation, encodedResult)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|