415 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
package schema_test
 | 
						|
 | 
						|
import (
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/pocketbase/pocketbase/models/schema"
 | 
						|
)
 | 
						|
 | 
						|
func TestNewSchemaAndFields(t *testing.T) {
 | 
						|
	testSchema := schema.NewSchema(
 | 
						|
		&schema.SchemaField{Id: "id1", Name: "test1"},
 | 
						|
		&schema.SchemaField{Name: "test2"},
 | 
						|
		&schema.SchemaField{Id: "id1", Name: "test1_new"}, // should replace the original id1 field
 | 
						|
	)
 | 
						|
 | 
						|
	fields := testSchema.Fields()
 | 
						|
 | 
						|
	if len(fields) != 2 {
 | 
						|
		t.Fatalf("Expected 2 fields, got %d (%v)", len(fields), fields)
 | 
						|
	}
 | 
						|
 | 
						|
	for _, f := range fields {
 | 
						|
		if f.Id == "" {
 | 
						|
			t.Fatalf("Expected field id to be set, found empty id for field %v", f)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if fields[0].Name != "test1_new" {
 | 
						|
		t.Fatalf("Expected field with name test1_new, got %s", fields[0].Name)
 | 
						|
	}
 | 
						|
 | 
						|
	if fields[1].Name != "test2" {
 | 
						|
		t.Fatalf("Expected field with name test2, got %s", fields[1].Name)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaInitFieldsOptions(t *testing.T) {
 | 
						|
	f0 := &schema.SchemaField{Name: "test1", Type: "unknown"}
 | 
						|
	schema0 := schema.NewSchema(f0)
 | 
						|
 | 
						|
	err0 := schema0.InitFieldsOptions()
 | 
						|
	if err0 == nil {
 | 
						|
		t.Fatalf("Expected unknown field schema to fail, got nil")
 | 
						|
	}
 | 
						|
 | 
						|
	// ---
 | 
						|
 | 
						|
	f1 := &schema.SchemaField{Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Name: "test2", Type: schema.FieldTypeEmail}
 | 
						|
	schema1 := schema.NewSchema(f1, f2)
 | 
						|
 | 
						|
	err1 := schema1.InitFieldsOptions()
 | 
						|
	if err1 != nil {
 | 
						|
		t.Fatal(err1)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, ok := f1.Options.(*schema.TextOptions); !ok {
 | 
						|
		t.Fatalf("Failed to init f1 options")
 | 
						|
	}
 | 
						|
 | 
						|
	if _, ok := f2.Options.(*schema.EmailOptions); !ok {
 | 
						|
		t.Fatalf("Failed to init f2 options")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaClone(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Name: "test2", Type: schema.FieldTypeEmail}
 | 
						|
	s1 := schema.NewSchema(f1, f2)
 | 
						|
 | 
						|
	s2, err := s1.Clone()
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	s1Encoded, _ := s1.MarshalJSON()
 | 
						|
	s2Encoded, _ := s2.MarshalJSON()
 | 
						|
 | 
						|
	if string(s1Encoded) != string(s2Encoded) {
 | 
						|
		t.Fatalf("Expected the cloned schema to be equal, got %v VS\n %v", s1, s2)
 | 
						|
	}
 | 
						|
 | 
						|
	// change in one schema shouldn't result to change in the other
 | 
						|
	// (aka. check if it is a deep clone)
 | 
						|
	s1.Fields()[0].Name = "test1_update"
 | 
						|
	if s2.Fields()[0].Name != "test1" {
 | 
						|
		t.Fatalf("Expected s2 field name to not change, got %q", s2.Fields()[0].Name)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaAsMap(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Name: "test2", Type: schema.FieldTypeEmail}
 | 
						|
	testSchema := schema.NewSchema(f1, f2)
 | 
						|
 | 
						|
	result := testSchema.AsMap()
 | 
						|
 | 
						|
	if len(result) != 2 {
 | 
						|
		t.Fatalf("Expected 2 map elements, got %d (%v)", len(result), result)
 | 
						|
	}
 | 
						|
 | 
						|
	expectedIndexes := []string{f1.Name, f2.Name}
 | 
						|
 | 
						|
	for _, index := range expectedIndexes {
 | 
						|
		if _, ok := result[index]; !ok {
 | 
						|
			t.Fatalf("Missing index %q", index)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaGetFieldByName(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Name: "test2", Type: schema.FieldTypeText}
 | 
						|
	testSchema := schema.NewSchema(f1, f2)
 | 
						|
 | 
						|
	// missing field
 | 
						|
	result1 := testSchema.GetFieldByName("missing")
 | 
						|
	if result1 != nil {
 | 
						|
		t.Fatalf("Found unexpected field %v", result1)
 | 
						|
	}
 | 
						|
 | 
						|
	// existing field
 | 
						|
	result2 := testSchema.GetFieldByName("test1")
 | 
						|
	if result2 == nil || result2.Name != "test1" {
 | 
						|
		t.Fatalf("Cannot find field with Name 'test1', got %v ", result2)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaGetFieldById(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Id: "id1", Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Id: "id2", Name: "test2", Type: schema.FieldTypeText}
 | 
						|
	testSchema := schema.NewSchema(f1, f2)
 | 
						|
 | 
						|
	// missing field id
 | 
						|
	result1 := testSchema.GetFieldById("test1")
 | 
						|
	if result1 != nil {
 | 
						|
		t.Fatalf("Found unexpected field %v", result1)
 | 
						|
	}
 | 
						|
 | 
						|
	// existing field id
 | 
						|
	result2 := testSchema.GetFieldById("id2")
 | 
						|
	if result2 == nil || result2.Id != "id2" {
 | 
						|
		t.Fatalf("Cannot find field with id 'id2', got %v ", result2)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaRemoveField(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Id: "id1", Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Id: "id2", Name: "test2", Type: schema.FieldTypeText}
 | 
						|
	f3 := &schema.SchemaField{Id: "id3", Name: "test3", Type: schema.FieldTypeText}
 | 
						|
	testSchema := schema.NewSchema(f1, f2, f3)
 | 
						|
 | 
						|
	testSchema.RemoveField("id2")
 | 
						|
	testSchema.RemoveField("test3") // should do nothing
 | 
						|
 | 
						|
	expected := []string{"test1", "test3"}
 | 
						|
 | 
						|
	if len(testSchema.Fields()) != len(expected) {
 | 
						|
		t.Fatalf("Expected %d, got %d (%v)", len(expected), len(testSchema.Fields()), testSchema)
 | 
						|
	}
 | 
						|
 | 
						|
	for _, name := range expected {
 | 
						|
		if f := testSchema.GetFieldByName(name); f == nil {
 | 
						|
			t.Fatalf("Missing field %q", name)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaAddField(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{Id: "f2Id", Name: "test2", Type: schema.FieldTypeText}
 | 
						|
	f3 := &schema.SchemaField{Id: "f3Id", Name: "test3", Type: schema.FieldTypeText}
 | 
						|
	testSchema := schema.NewSchema(f1, f2, f3)
 | 
						|
 | 
						|
	f2New := &schema.SchemaField{Id: "f2Id", Name: "test2_new", Type: schema.FieldTypeEmail}
 | 
						|
	f4 := &schema.SchemaField{Name: "test4", Type: schema.FieldTypeUrl}
 | 
						|
 | 
						|
	testSchema.AddField(f2New)
 | 
						|
	testSchema.AddField(f4)
 | 
						|
 | 
						|
	if len(testSchema.Fields()) != 4 {
 | 
						|
		t.Fatalf("Expected %d, got %d (%v)", 4, len(testSchema.Fields()), testSchema)
 | 
						|
	}
 | 
						|
 | 
						|
	// check if each field has id
 | 
						|
	for _, f := range testSchema.Fields() {
 | 
						|
		if f.Id == "" {
 | 
						|
			t.Fatalf("Expected field id to be set, found empty id for field %v", f)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// check if f2 field was replaced
 | 
						|
	if f := testSchema.GetFieldById("f2Id"); f == nil || f.Type != schema.FieldTypeEmail {
 | 
						|
		t.Fatalf("Expected f2 field to be replaced, found %v", f)
 | 
						|
	}
 | 
						|
 | 
						|
	// check if f4 was added
 | 
						|
	if f := testSchema.GetFieldByName("test4"); f == nil || f.Name != "test4" {
 | 
						|
		t.Fatalf("Expected f4 field to be added, found %v", f)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaValidate(t *testing.T) {
 | 
						|
	// emulate duplicated field ids
 | 
						|
	duplicatedIdsSchema := schema.NewSchema(
 | 
						|
		&schema.SchemaField{Id: "id1", Name: "test1", Type: schema.FieldTypeText},
 | 
						|
		&schema.SchemaField{Id: "id2", Name: "test2", Type: schema.FieldTypeText},
 | 
						|
	)
 | 
						|
	duplicatedIdsSchema.Fields()[1].Id = "id1" // manually set existing id
 | 
						|
 | 
						|
	scenarios := []struct {
 | 
						|
		schema      schema.Schema
 | 
						|
		expectError bool
 | 
						|
	}{
 | 
						|
		// no fields
 | 
						|
		{
 | 
						|
			schema.NewSchema(),
 | 
						|
			false,
 | 
						|
		},
 | 
						|
		// duplicated field ids
 | 
						|
		{
 | 
						|
			duplicatedIdsSchema,
 | 
						|
			true,
 | 
						|
		},
 | 
						|
		// duplicated field names (case insensitive)
 | 
						|
		{
 | 
						|
			schema.NewSchema(
 | 
						|
				&schema.SchemaField{Name: "test", Type: schema.FieldTypeText},
 | 
						|
				&schema.SchemaField{Name: "TeSt", Type: schema.FieldTypeText},
 | 
						|
			),
 | 
						|
			true,
 | 
						|
		},
 | 
						|
		// failure - base individual fields validation
 | 
						|
		{
 | 
						|
			schema.NewSchema(
 | 
						|
				&schema.SchemaField{Name: "", Type: schema.FieldTypeText},
 | 
						|
			),
 | 
						|
			true,
 | 
						|
		},
 | 
						|
		// success - base individual fields validation
 | 
						|
		{
 | 
						|
			schema.NewSchema(
 | 
						|
				&schema.SchemaField{Name: "test", Type: schema.FieldTypeText},
 | 
						|
			),
 | 
						|
			false,
 | 
						|
		},
 | 
						|
		// failure - individual field options validation
 | 
						|
		{
 | 
						|
			schema.NewSchema(
 | 
						|
				&schema.SchemaField{Name: "test", Type: schema.FieldTypeFile},
 | 
						|
			),
 | 
						|
			true,
 | 
						|
		},
 | 
						|
		// success - individual field options validation
 | 
						|
		{
 | 
						|
			schema.NewSchema(
 | 
						|
				&schema.SchemaField{Name: "test", Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1, MaxSize: 1}},
 | 
						|
			),
 | 
						|
			false,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for i, s := range scenarios {
 | 
						|
		err := s.schema.Validate()
 | 
						|
 | 
						|
		hasErr := err != nil
 | 
						|
		if hasErr != s.expectError {
 | 
						|
			t.Errorf("(%d) Expected %v, got %v (%v)", i, s.expectError, hasErr, err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaMarshalJSON(t *testing.T) {
 | 
						|
	f1 := &schema.SchemaField{Id: "f1id", Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	f2 := &schema.SchemaField{
 | 
						|
		Id:      "f2id",
 | 
						|
		Name:    "test2",
 | 
						|
		Type:    schema.FieldTypeText,
 | 
						|
		Options: &schema.TextOptions{Pattern: "test"},
 | 
						|
	}
 | 
						|
	testSchema := schema.NewSchema(f1, f2)
 | 
						|
 | 
						|
	result, err := testSchema.MarshalJSON()
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	expected := `[{"system":false,"id":"f1id","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}},{"system":false,"id":"f2id","name":"test2","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`
 | 
						|
 | 
						|
	if string(result) != expected {
 | 
						|
		t.Fatalf("Expected %s, got %s", expected, string(result))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaUnmarshalJSON(t *testing.T) {
 | 
						|
	encoded := `[{"system":false,"id":"fid1", "name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}},{"system":false,"name":"test2","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`
 | 
						|
	testSchema := schema.Schema{}
 | 
						|
	testSchema.AddField(&schema.SchemaField{Name: "tempField", Type: schema.FieldTypeUrl})
 | 
						|
	err := testSchema.UnmarshalJSON([]byte(encoded))
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	fields := testSchema.Fields()
 | 
						|
	if len(fields) != 2 {
 | 
						|
		t.Fatalf("Expected 2 fields, found %v", fields)
 | 
						|
	}
 | 
						|
 | 
						|
	f1 := testSchema.GetFieldByName("test1")
 | 
						|
	if f1 == nil {
 | 
						|
		t.Fatal("Expected to find field 'test1', got nil")
 | 
						|
	}
 | 
						|
	if f1.Id != "fid1" {
 | 
						|
		t.Fatalf("Expected fid1 id, got %s", f1.Id)
 | 
						|
	}
 | 
						|
	_, ok := f1.Options.(*schema.TextOptions)
 | 
						|
	if !ok {
 | 
						|
		t.Fatal("'test1' field options are not inited.")
 | 
						|
	}
 | 
						|
 | 
						|
	f2 := testSchema.GetFieldByName("test2")
 | 
						|
	if f2 == nil {
 | 
						|
		t.Fatal("Expected to find field 'test2', got nil")
 | 
						|
	}
 | 
						|
	if f2.Id == "" {
 | 
						|
		t.Fatal("Expected f2 id to be set, got empty string")
 | 
						|
	}
 | 
						|
	o2, ok := f2.Options.(*schema.TextOptions)
 | 
						|
	if !ok {
 | 
						|
		t.Fatal("'test2' field options are not inited.")
 | 
						|
	}
 | 
						|
	if o2.Pattern != "test" {
 | 
						|
		t.Fatalf("Expected pattern to be %q, got %q", "test", o2.Pattern)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaValue(t *testing.T) {
 | 
						|
	// empty schema
 | 
						|
	s1 := schema.Schema{}
 | 
						|
	v1, err := s1.Value()
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	if v1 != "[]" {
 | 
						|
		t.Fatalf("Expected nil, got %v", v1)
 | 
						|
	}
 | 
						|
 | 
						|
	// schema with fields
 | 
						|
	f1 := &schema.SchemaField{Id: "f1id", Name: "test1", Type: schema.FieldTypeText}
 | 
						|
	s2 := schema.NewSchema(f1)
 | 
						|
 | 
						|
	v2, err := s2.Value()
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	expected := `[{"system":false,"id":"f1id","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`
 | 
						|
 | 
						|
	if v2 != expected {
 | 
						|
		t.Fatalf("Expected %v, got %v", expected, v2)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSchemaScan(t *testing.T) {
 | 
						|
	scenarios := []struct {
 | 
						|
		data        any
 | 
						|
		expectError bool
 | 
						|
		expectJson  string
 | 
						|
	}{
 | 
						|
		{nil, false, "[]"},
 | 
						|
		{"", false, "[]"},
 | 
						|
		{[]byte{}, false, "[]"},
 | 
						|
		{"[]", false, "[]"},
 | 
						|
		{"invalid", true, "[]"},
 | 
						|
		{123, true, "[]"},
 | 
						|
		// no field type
 | 
						|
		{`[{}]`, true, `[]`},
 | 
						|
		// unknown field type
 | 
						|
		{
 | 
						|
			`[{"system":false,"id":"123","name":"test1","type":"unknown","required":false,"unique":false}]`,
 | 
						|
			true,
 | 
						|
			`[]`,
 | 
						|
		},
 | 
						|
		// without options
 | 
						|
		{
 | 
						|
			`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false}]`,
 | 
						|
			false,
 | 
						|
			`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
 | 
						|
		},
 | 
						|
		// with options
 | 
						|
		{
 | 
						|
			`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`,
 | 
						|
			false,
 | 
						|
			`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for i, s := range scenarios {
 | 
						|
		testSchema := schema.Schema{}
 | 
						|
 | 
						|
		err := testSchema.Scan(s.data)
 | 
						|
 | 
						|
		hasErr := err != nil
 | 
						|
		if hasErr != s.expectError {
 | 
						|
			t.Errorf("(%d) Expected %v, got %v (%v)", i, s.expectError, hasErr, err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		json, _ := testSchema.MarshalJSON()
 | 
						|
		if string(json) != s.expectJson {
 | 
						|
			t.Errorf("(%d) Expected json %v, got %v", i, s.expectJson, string(json))
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |