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))
 | |
| 		}
 | |
| 	}
 | |
| }
 |