1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-01-10 00:43:36 +02:00
pocketbase/models/schema/schema_field_test.go

1351 lines
31 KiB
Go

package schema_test
import (
"encoding/json"
"fmt"
"testing"
"time"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/pocketbase/pocketbase/models/schema"
"github.com/pocketbase/pocketbase/tools/types"
)
func TestReservedFieldNames(t *testing.T) {
result := schema.ReservedFieldNames()
if len(result) != 3 {
t.Fatalf("Expected %d names, got %d (%v)", 3, len(result), result)
}
}
func TestFieldTypes(t *testing.T) {
result := schema.FieldTypes()
if len(result) != 11 {
t.Fatalf("Expected %d types, got %d (%v)", 3, len(result), result)
}
}
func TestArraybleFieldTypes(t *testing.T) {
result := schema.ArraybleFieldTypes()
if len(result) != 4 {
t.Fatalf("Expected %d types, got %d (%v)", 3, len(result), result)
}
}
func TestSchemaFieldColDefinition(t *testing.T) {
scenarios := []struct {
field schema.SchemaField
expected string
}{
{
schema.SchemaField{Type: schema.FieldTypeText, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeNumber, Name: "test"},
"REAL DEFAULT 0",
},
{
schema.SchemaField{Type: schema.FieldTypeBool, Name: "test"},
"Boolean DEFAULT FALSE",
},
{
schema.SchemaField{Type: schema.FieldTypeEmail, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeUrl, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeDate, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeJson, Name: "test"},
"JSON DEFAULT NULL",
},
{
schema.SchemaField{Type: schema.FieldTypeFile, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeRelation, Name: "test"},
"TEXT DEFAULT ''",
},
{
schema.SchemaField{Type: schema.FieldTypeUser, Name: "test"},
"TEXT DEFAULT ''",
},
}
for i, s := range scenarios {
def := s.field.ColDefinition()
if def != s.expected {
t.Errorf("(%d) Expected definition %q, got %q", i, s.expected, def)
}
}
}
func TestSchemaFieldString(t *testing.T) {
f := schema.SchemaField{
Id: "abc",
Name: "test",
Type: schema.FieldTypeText,
Required: true,
Unique: false,
System: true,
Options: &schema.TextOptions{
Pattern: "test",
},
}
result := f.String()
expected := `{"system":true,"id":"abc","name":"test","type":"text","required":true,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`
if result != expected {
t.Errorf("Expected \n%v, got \n%v", expected, result)
}
}
func TestSchemaFieldMarshalJSON(t *testing.T) {
scenarios := []struct {
field schema.SchemaField
expected string
}{
// empty
{
schema.SchemaField{},
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
},
// without defined options
{
schema.SchemaField{
Id: "abc",
Name: "test",
Type: schema.FieldTypeText,
Required: true,
Unique: false,
System: true,
},
`{"system":true,"id":"abc","name":"test","type":"text","required":true,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
},
// with defined options
{
schema.SchemaField{
Name: "test",
Type: schema.FieldTypeText,
Required: true,
Unique: false,
System: true,
Options: &schema.TextOptions{
Pattern: "test",
},
},
`{"system":true,"id":"","name":"test","type":"text","required":true,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
},
}
for i, s := range scenarios {
result, err := s.field.MarshalJSON()
if err != nil {
t.Fatalf("(%d) %v", i, err)
}
if string(result) != s.expected {
t.Errorf("(%d), Expected \n%v, got \n%v", i, s.expected, string(result))
}
}
}
func TestSchemaFieldUnmarshalJSON(t *testing.T) {
scenarios := []struct {
data []byte
expectError bool
expectJson string
}{
{
nil,
true,
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
},
{
[]byte{},
true,
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
},
{
[]byte(`{"system": true}`),
true,
`{"system":true,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
},
{
[]byte(`{"invalid"`),
true,
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
},
{
[]byte(`{"type":"text","system":true}`),
false,
`{"system":true,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
},
{
[]byte(`{"type":"text","options":{"pattern":"test"}}`),
false,
`{"system":false,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
},
}
for i, s := range scenarios {
f := schema.SchemaField{}
err := f.UnmarshalJSON(s.data)
hasErr := err != nil
if hasErr != s.expectError {
t.Errorf("(%d) Expected hasErr %v, got %v (%v)", i, s.expectError, hasErr, err)
}
if f.String() != s.expectJson {
t.Errorf("(%d), Expected json \n%v, got \n%v", i, s.expectJson, f.String())
}
}
}
func TestSchemaFieldValidate(t *testing.T) {
scenarios := []struct {
name string
field schema.SchemaField
expectedErrors []string
}{
{
"empty field",
schema.SchemaField{},
[]string{"id", "options", "name", "type"},
},
{
"missing id",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "",
Name: "test",
},
[]string{"id"},
},
{
"invalid id length check",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234",
Name: "test",
},
[]string{"id"},
},
{
"valid id length check",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "12345",
Name: "test",
},
[]string{},
},
{
"invalid name format",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: "test!@#",
},
[]string{"name"},
},
{
"reserved name (null)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: "null",
},
[]string{"name"},
},
{
"reserved name (true)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: "null",
},
[]string{"name"},
},
{
"reserved name (false)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: "false",
},
[]string{"name"},
},
{
"reserved name (id)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: schema.ReservedFieldNameId,
},
[]string{"name"},
},
{
"reserved name (created)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: schema.ReservedFieldNameCreated,
},
[]string{"name"},
},
{
"reserved name (updated)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: schema.ReservedFieldNameUpdated,
},
[]string{"name"},
},
{
"valid name",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: "test",
},
[]string{},
},
{
"unique check for type file",
schema.SchemaField{
Type: schema.FieldTypeFile,
Id: "1234567890",
Name: "test",
Unique: true,
Options: &schema.FileOptions{MaxSelect: 1, MaxSize: 1},
},
[]string{"unique"},
},
{
"trigger options validator (auto init)",
schema.SchemaField{
Type: schema.FieldTypeFile,
Id: "1234567890",
Name: "test",
},
[]string{"options"},
},
{
"trigger options validator (invalid option field value)",
schema.SchemaField{
Type: schema.FieldTypeFile,
Id: "1234567890",
Name: "test",
Options: &schema.FileOptions{MaxSelect: 0, MaxSize: 0},
},
[]string{"options"},
},
{
"trigger options validator (valid option field value)",
schema.SchemaField{
Type: schema.FieldTypeFile,
Id: "1234567890",
Name: "test",
Options: &schema.FileOptions{MaxSelect: 1, MaxSize: 1},
},
[]string{},
},
}
for _, s := range scenarios {
result := s.field.Validate()
// parse errors
errs, ok := result.(validation.Errors)
if !ok && result != nil {
t.Errorf("[%s] Failed to parse errors %v", s.name, result)
continue
}
// check errors
if len(errs) > len(s.expectedErrors) {
t.Errorf("[%s] Expected error keys %v, got %v", s.name, s.expectedErrors, errs)
}
for _, k := range s.expectedErrors {
if _, ok := errs[k]; !ok {
t.Errorf("[%s] Missing expected error key %q in %v", s.name, k, errs)
}
}
}
}
func TestSchemaFieldInitOptions(t *testing.T) {
scenarios := []struct {
field schema.SchemaField
expectError bool
expectJson string
}{
{
schema.SchemaField{},
true,
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
},
{
schema.SchemaField{Type: "unknown"},
true,
`{"system":false,"id":"","name":"","type":"unknown","required":false,"unique":false,"options":null}`,
},
{
schema.SchemaField{Type: schema.FieldTypeText},
false,
`{"system":false,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeNumber},
false,
`{"system":false,"id":"","name":"","type":"number","required":false,"unique":false,"options":{"min":null,"max":null}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeBool},
false,
`{"system":false,"id":"","name":"","type":"bool","required":false,"unique":false,"options":{}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeEmail},
false,
`{"system":false,"id":"","name":"","type":"email","required":false,"unique":false,"options":{"exceptDomains":null,"onlyDomains":null}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeUrl},
false,
`{"system":false,"id":"","name":"","type":"url","required":false,"unique":false,"options":{"exceptDomains":null,"onlyDomains":null}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeDate},
false,
`{"system":false,"id":"","name":"","type":"date","required":false,"unique":false,"options":{"min":"","max":""}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeSelect},
false,
`{"system":false,"id":"","name":"","type":"select","required":false,"unique":false,"options":{"maxSelect":0,"values":null}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeJson},
false,
`{"system":false,"id":"","name":"","type":"json","required":false,"unique":false,"options":{}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeFile},
false,
`{"system":false,"id":"","name":"","type":"file","required":false,"unique":false,"options":{"maxSelect":0,"maxSize":0,"mimeTypes":null,"thumbs":null}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeRelation},
false,
`{"system":false,"id":"","name":"","type":"relation","required":false,"unique":false,"options":{"maxSelect":0,"collectionId":"","cascadeDelete":false}}`,
},
{
schema.SchemaField{Type: schema.FieldTypeUser},
false,
`{"system":false,"id":"","name":"","type":"user","required":false,"unique":false,"options":{"maxSelect":0,"cascadeDelete":false}}`,
},
{
schema.SchemaField{
Type: schema.FieldTypeText,
Options: &schema.TextOptions{Pattern: "test"},
},
false,
`{"system":false,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
},
}
for i, s := range scenarios {
err := s.field.InitOptions()
hasErr := err != nil
if hasErr != s.expectError {
t.Errorf("(%d) Expected %v, got %v (%v)", i, s.expectError, hasErr, err)
}
if s.field.String() != s.expectJson {
t.Errorf("(%d), Expected %v, got %v", i, s.expectJson, s.field.String())
}
}
}
func TestSchemaFieldPrepareValue(t *testing.T) {
scenarios := []struct {
field schema.SchemaField
value any
expectJson string
}{
{schema.SchemaField{Type: "unknown"}, "test", `"test"`},
{schema.SchemaField{Type: "unknown"}, 123, "123"},
{schema.SchemaField{Type: "unknown"}, []int{1, 2, 1}, "[1,2,1]"},
// text
{schema.SchemaField{Type: schema.FieldTypeText}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeText}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeText}, []int{1, 2}, `""`},
{schema.SchemaField{Type: schema.FieldTypeText}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeText}, 123, `"123"`},
// email
{schema.SchemaField{Type: schema.FieldTypeEmail}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeEmail}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeEmail}, []int{1, 2}, `""`},
{schema.SchemaField{Type: schema.FieldTypeEmail}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeEmail}, 123, `"123"`},
// url
{schema.SchemaField{Type: schema.FieldTypeUrl}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeUrl}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeUrl}, []int{1, 2}, `""`},
{schema.SchemaField{Type: schema.FieldTypeUrl}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeUrl}, 123, `"123"`},
// json
{schema.SchemaField{Type: schema.FieldTypeJson}, nil, "null"},
{schema.SchemaField{Type: schema.FieldTypeJson}, 123, "123"},
{schema.SchemaField{Type: schema.FieldTypeJson}, `"test"`, `"test"`},
{schema.SchemaField{Type: schema.FieldTypeJson}, map[string]int{"test": 123}, `{"test":123}`},
{schema.SchemaField{Type: schema.FieldTypeJson}, []int{1, 2, 1}, `[1,2,1]`},
// number
{schema.SchemaField{Type: schema.FieldTypeNumber}, nil, "0"},
{schema.SchemaField{Type: schema.FieldTypeNumber}, "", "0"},
{schema.SchemaField{Type: schema.FieldTypeNumber}, "test", "0"},
{schema.SchemaField{Type: schema.FieldTypeNumber}, 1, "1"},
{schema.SchemaField{Type: schema.FieldTypeNumber}, 1.5, "1.5"},
{schema.SchemaField{Type: schema.FieldTypeNumber}, "1.5", "1.5"},
// bool
{schema.SchemaField{Type: schema.FieldTypeBool}, nil, "false"},
{schema.SchemaField{Type: schema.FieldTypeBool}, 1, "true"},
{schema.SchemaField{Type: schema.FieldTypeBool}, 0, "false"},
{schema.SchemaField{Type: schema.FieldTypeBool}, "", "false"},
{schema.SchemaField{Type: schema.FieldTypeBool}, "test", "false"},
{schema.SchemaField{Type: schema.FieldTypeBool}, "false", "false"},
{schema.SchemaField{Type: schema.FieldTypeBool}, "true", "true"},
{schema.SchemaField{Type: schema.FieldTypeBool}, false, "false"},
{schema.SchemaField{Type: schema.FieldTypeBool}, true, "true"},
// date
{schema.SchemaField{Type: schema.FieldTypeDate}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeDate}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeDate}, "test", `""`},
{schema.SchemaField{Type: schema.FieldTypeDate}, 1641024040, `"2022-01-01 08:00:40.000"`},
{schema.SchemaField{Type: schema.FieldTypeDate}, "2022-01-01 11:27:10.123", `"2022-01-01 11:27:10.123"`},
{schema.SchemaField{Type: schema.FieldTypeDate}, types.DateTime{}, `""`},
{schema.SchemaField{Type: schema.FieldTypeDate}, time.Time{}, `""`},
// select (single)
{schema.SchemaField{Type: schema.FieldTypeSelect}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, 123, `"123"`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeSelect}, []string{"test1", "test2"}, `"test1"`},
{
// no values validation/filtering
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{
Values: []string{"test1", "test2"},
},
},
"test",
`"test"`,
},
// select (multiple)
{
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
nil,
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
"",
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
[]string{},
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
123,
`["123"]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
"test",
`["test"]`,
},
{
// no values validation
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
[]string{"test1", "test2", "test3"},
`["test1","test2","test3"]`,
},
{
// duplicated values
schema.SchemaField{
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
[]string{"test1", "test2", "test1"},
`["test1","test2"]`,
},
// file (single)
{schema.SchemaField{Type: schema.FieldTypeFile}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeFile}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeFile}, 123, `"123"`},
{schema.SchemaField{Type: schema.FieldTypeFile}, "test", `"test"`},
{schema.SchemaField{Type: schema.FieldTypeFile}, []string{"test1", "test2"}, `"test1"`},
// file (multiple)
{
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
nil,
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
"",
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
[]string{},
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
123,
`["123"]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
"test",
`["test"]`,
},
{
// no values validation
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
[]string{"test1", "test2", "test3"},
`["test1","test2","test3"]`,
},
{
// duplicated values
schema.SchemaField{
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 2},
},
[]string{"test1", "test2", "test1"},
`["test1","test2"]`,
},
// relation (single)
{schema.SchemaField{Type: schema.FieldTypeRelation}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeRelation}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeRelation}, 123, `"123"`},
{schema.SchemaField{Type: schema.FieldTypeRelation}, "abc", `"abc"`},
{schema.SchemaField{Type: schema.FieldTypeRelation}, "1ba88b4f-e9da-42f0-9764-9a55c953e724", `"1ba88b4f-e9da-42f0-9764-9a55c953e724"`},
{
schema.SchemaField{Type: schema.FieldTypeRelation},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724"},
`"1ba88b4f-e9da-42f0-9764-9a55c953e724"`,
},
// relation (multiple)
{
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
nil,
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
"",
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
[]string{},
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
123,
`["123"]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
[]string{"", "abc"},
`["abc"]`,
},
{
// no values validation
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724"},
`["1ba88b4f-e9da-42f0-9764-9a55c953e724","2ba88b4f-e9da-42f0-9764-9a55c953e724"]`,
},
{
// duplicated values
schema.SchemaField{
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: 2},
},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724", "1ba88b4f-e9da-42f0-9764-9a55c953e724"},
`["1ba88b4f-e9da-42f0-9764-9a55c953e724","2ba88b4f-e9da-42f0-9764-9a55c953e724"]`,
},
// user (single)
{schema.SchemaField{Type: schema.FieldTypeUser}, nil, `""`},
{schema.SchemaField{Type: schema.FieldTypeUser}, "", `""`},
{schema.SchemaField{Type: schema.FieldTypeUser}, 123, `"123"`},
{schema.SchemaField{Type: schema.FieldTypeUser}, "1ba88b4f-e9da-42f0-9764-9a55c953e724", `"1ba88b4f-e9da-42f0-9764-9a55c953e724"`},
{
schema.SchemaField{Type: schema.FieldTypeUser},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724"},
`"1ba88b4f-e9da-42f0-9764-9a55c953e724"`,
},
// user (multiple)
{
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
nil,
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
"",
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
[]string{},
`[]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
123,
`["123"]`,
},
{
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
[]string{"", "abc"},
`["abc"]`,
},
{
// no values validation
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724"},
`["1ba88b4f-e9da-42f0-9764-9a55c953e724","2ba88b4f-e9da-42f0-9764-9a55c953e724"]`,
},
{
// duplicated values
schema.SchemaField{
Type: schema.FieldTypeUser,
Options: &schema.UserOptions{MaxSelect: 2},
},
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724", "1ba88b4f-e9da-42f0-9764-9a55c953e724"},
`["1ba88b4f-e9da-42f0-9764-9a55c953e724","2ba88b4f-e9da-42f0-9764-9a55c953e724"]`,
},
}
for i, s := range scenarios {
result := s.field.PrepareValue(s.value)
encoded, err := json.Marshal(result)
if err != nil {
t.Errorf("(%d) %v", i, err)
continue
}
if string(encoded) != s.expectJson {
t.Errorf("(%d), Expected %v, got %v", i, s.expectJson, string(encoded))
}
}
}
// -------------------------------------------------------------------
type fieldOptionsScenario struct {
name string
options schema.FieldOptions
expectedErrors []string
}
func checkFieldOptionsScenarios(t *testing.T, scenarios []fieldOptionsScenario) {
for i, s := range scenarios {
result := s.options.Validate()
prefix := fmt.Sprintf("%d", i)
if s.name != "" {
prefix = s.name
}
// parse errors
errs, ok := result.(validation.Errors)
if !ok && result != nil {
t.Errorf("[%s] Failed to parse errors %v", prefix, result)
continue
}
// check errors
if len(errs) > len(s.expectedErrors) {
t.Errorf("[%s] Expected error keys %v, got %v", prefix, s.expectedErrors, errs)
}
for _, k := range s.expectedErrors {
if _, ok := errs[k]; !ok {
t.Errorf("[%s] Missing expected error key %q in %v", prefix, k, errs)
}
}
}
}
func TestTextOptionsValidate(t *testing.T) {
minus := -1
number0 := 0
number1 := 10
number2 := 20
scenarios := []fieldOptionsScenario{
{
"empty",
schema.TextOptions{},
[]string{},
},
{
"min - failure",
schema.TextOptions{
Min: &minus,
},
[]string{"min"},
},
{
"min - success",
schema.TextOptions{
Min: &number0,
},
[]string{},
},
{
"max - failure without min",
schema.TextOptions{
Max: &minus,
},
[]string{"max"},
},
{
"max - failure with min",
schema.TextOptions{
Min: &number2,
Max: &number1,
},
[]string{"max"},
},
{
"max - success",
schema.TextOptions{
Min: &number1,
Max: &number2,
},
[]string{},
},
{
"pattern - failure",
schema.TextOptions{Pattern: "(test"},
[]string{"pattern"},
},
{
"pattern - success",
schema.TextOptions{Pattern: `^\#?\w+$`},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestNumberOptionsValidate(t *testing.T) {
number1 := 10.0
number2 := 20.0
scenarios := []fieldOptionsScenario{
{
"empty",
schema.NumberOptions{},
[]string{},
},
{
"max - without min",
schema.NumberOptions{
Max: &number1,
},
[]string{},
},
{
"max - failure with min",
schema.NumberOptions{
Min: &number2,
Max: &number1,
},
[]string{"max"},
},
{
"max - success with min",
schema.NumberOptions{
Min: &number1,
Max: &number2,
},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestBoolOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.BoolOptions{},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestEmailOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.EmailOptions{},
[]string{},
},
{
"ExceptDomains failure",
schema.EmailOptions{
ExceptDomains: []string{"invalid"},
},
[]string{"exceptDomains"},
},
{
"ExceptDomains success",
schema.EmailOptions{
ExceptDomains: []string{"example.com", "sub.example.com"},
},
[]string{},
},
{
"OnlyDomains check",
schema.EmailOptions{
OnlyDomains: []string{"invalid"},
},
[]string{"onlyDomains"},
},
{
"OnlyDomains success",
schema.EmailOptions{
OnlyDomains: []string{"example.com", "sub.example.com"},
},
[]string{},
},
{
"OnlyDomains + ExceptDomains at the same time",
schema.EmailOptions{
ExceptDomains: []string{"test1.com"},
OnlyDomains: []string{"test2.com"},
},
[]string{"exceptDomains", "onlyDomains"},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestUrlOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.UrlOptions{},
[]string{},
},
{
"ExceptDomains failure",
schema.UrlOptions{
ExceptDomains: []string{"invalid"},
},
[]string{"exceptDomains"},
},
{
"ExceptDomains success",
schema.UrlOptions{
ExceptDomains: []string{"example.com", "sub.example.com"},
},
[]string{},
},
{
"OnlyDomains check",
schema.UrlOptions{
OnlyDomains: []string{"invalid"},
},
[]string{"onlyDomains"},
},
{
"OnlyDomains success",
schema.UrlOptions{
OnlyDomains: []string{"example.com", "sub.example.com"},
},
[]string{},
},
{
"OnlyDomains + ExceptDomains at the same time",
schema.UrlOptions{
ExceptDomains: []string{"test1.com"},
OnlyDomains: []string{"test2.com"},
},
[]string{"exceptDomains", "onlyDomains"},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestDateOptionsValidate(t *testing.T) {
date1 := types.NowDateTime()
date2, _ := types.ParseDateTime(date1.Time().AddDate(1, 0, 0))
scenarios := []fieldOptionsScenario{
{
"empty",
schema.DateOptions{},
[]string{},
},
{
"min only",
schema.DateOptions{
Min: date1,
},
[]string{},
},
{
"max only",
schema.DateOptions{
Min: date1,
},
[]string{},
},
{
"zero min + max",
schema.DateOptions{
Min: types.DateTime{},
Max: date1,
},
[]string{},
},
{
"min + zero max",
schema.DateOptions{
Min: date1,
Max: types.DateTime{},
},
[]string{},
},
{
"min > max",
schema.DateOptions{
Min: date2,
Max: date1,
},
[]string{"max"},
},
{
"min == max",
schema.DateOptions{
Min: date1,
Max: date1,
},
[]string{"max"},
},
{
"min < max",
schema.DateOptions{
Min: date1,
Max: date2,
},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestSelectOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.SelectOptions{},
[]string{"values", "maxSelect"},
},
{
"MaxSelect <= 0",
schema.SelectOptions{
Values: []string{"test1", "test2"},
MaxSelect: 0,
},
[]string{"maxSelect"},
},
{
"MaxSelect > Values",
schema.SelectOptions{
Values: []string{"test1", "test2"},
MaxSelect: 3,
},
[]string{"maxSelect"},
},
{
"MaxSelect <= Values",
schema.SelectOptions{
Values: []string{"test1", "test2"},
MaxSelect: 2,
},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestJsonOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.JsonOptions{},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestFileOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.FileOptions{},
[]string{"maxSelect", "maxSize"},
},
{
"MaxSelect <= 0 && maxSize <= 0",
schema.FileOptions{
MaxSize: 0,
MaxSelect: 0,
},
[]string{"maxSelect", "maxSize"},
},
{
"MaxSelect > 0 && maxSize > 0",
schema.FileOptions{
MaxSize: 2,
MaxSelect: 1,
},
[]string{},
},
{
"invalid thumbs format",
schema.FileOptions{
MaxSize: 1,
MaxSelect: 2,
Thumbs: []string{"100", "200x100"},
},
[]string{"thumbs"},
},
{
"invalid thumbs format - zero width",
schema.FileOptions{
MaxSize: 1,
MaxSelect: 2,
Thumbs: []string{"0x100"},
},
[]string{"thumbs"},
},
{
"invalid thumbs format - zero height",
schema.FileOptions{
MaxSize: 1,
MaxSelect: 2,
Thumbs: []string{"100x0"},
},
[]string{"thumbs"},
},
{
"invalid thumbs format - zero with and height",
schema.FileOptions{
MaxSize: 1,
MaxSelect: 2,
Thumbs: []string{"0x0"},
},
[]string{"thumbs"},
},
{
"valid thumbs format",
schema.FileOptions{
MaxSize: 1,
MaxSelect: 2,
Thumbs: []string{"100x100", "200x100", "1x1"},
},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestRelationOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.RelationOptions{},
[]string{"maxSelect", "collectionId"},
},
{
"empty CollectionId",
schema.RelationOptions{
CollectionId: "",
MaxSelect: 1,
},
[]string{"collectionId"},
},
{
"MaxSelect <= 0",
schema.RelationOptions{
CollectionId: "abc",
MaxSelect: 0,
},
[]string{"maxSelect"},
},
{
"MaxSelect > 0 && non-empty CollectionId",
schema.RelationOptions{
CollectionId: "abc",
MaxSelect: 1,
},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}
func TestUserOptionsValidate(t *testing.T) {
scenarios := []fieldOptionsScenario{
{
"empty",
schema.UserOptions{},
[]string{"maxSelect"},
},
{
"MaxSelect <= 0",
schema.UserOptions{
MaxSelect: 0,
},
[]string{"maxSelect"},
},
{
"MaxSelect > 0",
schema.UserOptions{
MaxSelect: 1,
},
[]string{},
},
}
checkFieldOptionsScenarios(t, scenarios)
}