1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2024-11-28 10:03:42 +02:00
pocketbase/models/record_test.go

2154 lines
57 KiB
Go

package models_test
import (
"bytes"
"database/sql"
"encoding/json"
"testing"
"time"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/models/schema"
"github.com/pocketbase/pocketbase/tools/list"
"github.com/pocketbase/pocketbase/tools/types"
)
func TestNewRecord(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "test_collection",
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "test",
Type: schema.FieldTypeText,
},
),
}
m := models.NewRecord(collection)
if m.Collection().Name != collection.Name {
t.Fatalf("Expected collection with name %q, got %q", collection.Id, m.Collection().Id)
}
if len(m.SchemaData()) != 0 {
t.Fatalf("Expected empty schema data, got %v", m.SchemaData())
}
}
func TestNewRecordFromNullStringMap(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "test",
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field3",
Type: schema.FieldTypeBool,
},
&schema.SchemaField{
Name: "field4",
Type: schema.FieldTypeNumber,
},
&schema.SchemaField{
Name: "field5",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{
Values: []string{"test1", "test2"},
MaxSelect: 1,
},
},
&schema.SchemaField{
Name: "field6",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{
MaxSelect: 2,
MaxSize: 1,
},
},
),
}
data := dbx.NullStringMap{
"id": sql.NullString{
String: "test_id",
Valid: true,
},
"created": sql.NullString{
String: "2022-01-01 10:00:00.123Z",
Valid: true,
},
"updated": sql.NullString{
String: "2022-01-01 10:00:00.456Z",
Valid: true,
},
// auth collection specific fields
"username": sql.NullString{
String: "test_username",
Valid: true,
},
"email": sql.NullString{
String: "test_email",
Valid: true,
},
"emailVisibility": sql.NullString{
String: "true",
Valid: true,
},
"verified": sql.NullString{
String: "",
Valid: false,
},
"tokenKey": sql.NullString{
String: "test_tokenKey",
Valid: true,
},
"passwordHash": sql.NullString{
String: "test_passwordHash",
Valid: true,
},
"lastResetSentAt": sql.NullString{
String: "2022-01-02 10:00:00.123Z",
Valid: true,
},
"lastVerificationSentAt": sql.NullString{
String: "2022-02-03 10:00:00.456Z",
Valid: true,
},
// custom schema fields
"field1": sql.NullString{
String: "test",
Valid: true,
},
"field2": sql.NullString{
String: "test",
Valid: false, // test invalid db serialization
},
"field3": sql.NullString{
String: "true",
Valid: true,
},
"field4": sql.NullString{
String: "123.123",
Valid: true,
},
"field5": sql.NullString{
String: `["test1","test2"]`, // will select only the last elem
Valid: true,
},
"field6": sql.NullString{
String: "test", // will be converted to slice
Valid: true,
},
"unknown": sql.NullString{
String: "test",
Valid: true,
},
}
scenarios := []struct {
collectionType string
expectedJson string
}{
{
models.CollectionTypeBase,
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test2","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z"}`,
},
{
models.CollectionTypeAuth,
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test2","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z","username":"test_username","verified":false}`,
},
}
for i, s := range scenarios {
collection.Type = s.collectionType
m := models.NewRecordFromNullStringMap(collection, data)
m.IgnoreEmailVisibility(true)
encoded, err := m.MarshalJSON()
if err != nil {
t.Errorf("(%d) Unexpected error: %v", i, err)
continue
}
if string(encoded) != s.expectedJson {
t.Errorf("(%d) Expected \n%v \ngot \n%v", i, s.expectedJson, string(encoded))
}
// additional data checks
if collection.IsAuth() {
if v := m.GetString(schema.FieldNamePasswordHash); v != "test_passwordHash" {
t.Errorf("(%d) Expected %q, got %q", i, "test_passwordHash", v)
}
if v := m.GetString(schema.FieldNameTokenKey); v != "test_tokenKey" {
t.Errorf("(%d) Expected %q, got %q", i, "test_tokenKey", v)
}
if v := m.GetString(schema.FieldNameLastResetSentAt); v != "2022-01-02 10:00:00.123Z" {
t.Errorf("(%d) Expected %q, got %q", i, "2022-01-02 10:00:00.123Z", v)
}
if v := m.GetString(schema.FieldNameLastVerificationSentAt); v != "2022-02-03 10:00:00.456Z" {
t.Errorf("(%d) Expected %q, got %q", i, "2022-01-02 10:00:00.123Z", v)
}
}
}
}
func TestNewRecordsFromNullStringMaps(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "test",
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeNumber,
},
&schema.SchemaField{
Name: "field3",
Type: schema.FieldTypeUrl,
},
),
}
data := []dbx.NullStringMap{
{
"id": sql.NullString{
String: "test_id1",
Valid: true,
},
"created": sql.NullString{
String: "2022-01-01 10:00:00.123Z",
Valid: true,
},
"updated": sql.NullString{
String: "2022-01-01 10:00:00.456Z",
Valid: true,
},
// partial auth fields
"email": sql.NullString{
String: "test_email",
Valid: true,
},
"tokenKey": sql.NullString{
String: "test_tokenKey",
Valid: true,
},
"emailVisibility": sql.NullString{
String: "true",
Valid: true,
},
// custom schema fields
"field1": sql.NullString{
String: "test",
Valid: true,
},
"field2": sql.NullString{
String: "123.123",
Valid: true,
},
"field3": sql.NullString{
String: "test",
Valid: false, // should force resolving to empty string
},
"unknown": sql.NullString{
String: "test",
Valid: true,
},
},
{
"field3": sql.NullString{
String: "test",
Valid: true,
},
"email": sql.NullString{
String: "test_email",
Valid: true,
},
"emailVisibility": sql.NullString{
String: "false",
Valid: true,
},
},
}
scenarios := []struct {
collectionType string
expectedJson string
}{
{
models.CollectionTypeBase,
`[{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","field1":"test","field2":123.123,"field3":"","id":"test_id1","updated":"2022-01-01 10:00:00.456Z"},{"collectionId":"","collectionName":"test","created":"","field1":"","field2":0,"field3":"test","id":"","updated":""}]`,
},
{
models.CollectionTypeAuth,
`[{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":123.123,"field3":"","id":"test_id1","updated":"2022-01-01 10:00:00.456Z","username":"","verified":false},{"collectionId":"","collectionName":"test","created":"","emailVisibility":false,"field1":"","field2":0,"field3":"test","id":"","updated":"","username":"","verified":false}]`,
},
}
for i, s := range scenarios {
collection.Type = s.collectionType
result := models.NewRecordsFromNullStringMaps(collection, data)
encoded, err := json.Marshal(result)
if err != nil {
t.Errorf("(%d) Unexpected error: %v", i, err)
continue
}
if string(encoded) != s.expectedJson {
t.Errorf("(%d) Expected \n%v \ngot \n%v", i, s.expectedJson, string(encoded))
}
}
}
func TestRecordTableName(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
collection.Name = "test"
collection.RefreshId()
m := models.NewRecord(collection)
if m.TableName() != collection.Name {
t.Fatalf("Expected table %q, got %q", collection.Name, m.TableName())
}
}
func TestRecordCollection(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
collection.RefreshId()
m := models.NewRecord(collection)
if m.Collection().Id != collection.Id {
t.Fatalf("Expected collection with id %v, got %v", collection.Id, m.Collection().Id)
}
}
func TestRecordOriginalCopy(t *testing.T) {
t.Parallel()
m := models.NewRecord(&models.Collection{})
m.Load(map[string]any{"f": "123"})
// change the field
m.Set("f", "456")
if v := m.GetString("f"); v != "456" {
t.Fatalf("Expected f to be %q, got %q", "456", v)
}
if v := m.OriginalCopy().GetString("f"); v != "123" {
t.Fatalf("Expected the initial/original f to be %q, got %q", "123", v)
}
// loading new data shouldn't affect the original state
m.Load(map[string]any{"f": "789"})
if v := m.GetString("f"); v != "789" {
t.Fatalf("Expected f to be %q, got %q", "789", v)
}
if v := m.OriginalCopy().GetString("f"); v != "123" {
t.Fatalf("Expected the initial/original f still to be %q, got %q", "123", v)
}
}
func TestRecordCleanCopy(t *testing.T) {
t.Parallel()
m := models.NewRecord(&models.Collection{
Name: "cname",
Type: models.CollectionTypeAuth,
})
m.Load(map[string]any{
"id": "id1",
"created": "2023-01-01 00:00:00.000Z",
"updated": "2023-01-02 00:00:00.000Z",
"username": "test",
"verified": true,
"email": "test@example.com",
"unknown": "456",
})
// make a change to ensure that the latest data is targeted
m.Set("id", "id2")
// allow the special flags and options to check whether they will be ignored
m.SetExpand(map[string]any{"test": 123})
m.IgnoreEmailVisibility(true)
m.WithUnknownData(true)
copy := m.CleanCopy()
copyExport, _ := copy.MarshalJSON()
expectedExport := []byte(`{"collectionId":"","collectionName":"cname","created":"2023-01-01 00:00:00.000Z","emailVisibility":false,"id":"id2","updated":"2023-01-02 00:00:00.000Z","username":"test","verified":true}`)
if !bytes.Equal(copyExport, expectedExport) {
t.Fatalf("Expected clean export \n%s, \ngot \n%s", expectedExport, copyExport)
}
}
func TestRecordSetAndGetExpand(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
m := models.NewRecord(collection)
data := map[string]any{"test": 123}
m.SetExpand(data)
// change the original data to check if it was shallow copied
data["test"] = 456
expand := m.Expand()
if v, ok := expand["test"]; !ok || v != 123 {
t.Fatalf("Expected expand.test to be %v, got %v", 123, v)
}
}
func TestRecordMergeExpand(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
m := models.NewRecord(collection)
m.Id = "m"
// a
a := models.NewRecord(collection)
a.Id = "a"
a1 := models.NewRecord(collection)
a1.Id = "a1"
a2 := models.NewRecord(collection)
a2.Id = "a2"
a3 := models.NewRecord(collection)
a3.Id = "a3"
a31 := models.NewRecord(collection)
a31.Id = "a31"
a32 := models.NewRecord(collection)
a32.Id = "a32"
a.SetExpand(map[string]any{
"a1": a1,
"a23": []*models.Record{a2, a3},
})
a3.SetExpand(map[string]any{
"a31": a31,
"a32": []*models.Record{a32},
})
// b
b := models.NewRecord(collection)
b.Id = "b"
b1 := models.NewRecord(collection)
b1.Id = "b1"
b.SetExpand(map[string]any{
"b1": b1,
})
// c
c := models.NewRecord(collection)
c.Id = "c"
// load initial expand
m.SetExpand(map[string]any{
"a": a,
"b": b,
"c": []*models.Record{c},
})
// a (new)
aNew := models.NewRecord(collection)
aNew.Id = a.Id
a3New := models.NewRecord(collection)
a3New.Id = a3.Id
a32New := models.NewRecord(collection)
a32New.Id = "a32New"
a33New := models.NewRecord(collection)
a33New.Id = "a33New"
a3New.SetExpand(map[string]any{
"a32": []*models.Record{a32New},
"a33New": a33New,
})
aNew.SetExpand(map[string]any{
"a23": []*models.Record{a2, a3New},
})
// b (new)
bNew := models.NewRecord(collection)
bNew.Id = "bNew"
dNew := models.NewRecord(collection)
dNew.Id = "dNew"
// merge expands
m.MergeExpand(map[string]any{
"a": aNew,
"b": []*models.Record{bNew},
"dNew": dNew,
})
result := m.Expand()
raw, err := json.Marshal(result)
if err != nil {
t.Fatal(err)
}
rawStr := string(raw)
expected := `{"a":{"collectionId":"","collectionName":"","created":"","expand":{"a1":{"collectionId":"","collectionName":"","created":"","id":"a1","updated":""},"a23":[{"collectionId":"","collectionName":"","created":"","id":"a2","updated":""},{"collectionId":"","collectionName":"","created":"","expand":{"a31":{"collectionId":"","collectionName":"","created":"","id":"a31","updated":""},"a32":[{"collectionId":"","collectionName":"","created":"","id":"a32","updated":""},{"collectionId":"","collectionName":"","created":"","id":"a32New","updated":""}],"a33New":{"collectionId":"","collectionName":"","created":"","id":"a33New","updated":""}},"id":"a3","updated":""}]},"id":"a","updated":""},"b":[{"collectionId":"","collectionName":"","created":"","expand":{"b1":{"collectionId":"","collectionName":"","created":"","id":"b1","updated":""}},"id":"b","updated":""},{"collectionId":"","collectionName":"","created":"","id":"bNew","updated":""}],"c":[{"collectionId":"","collectionName":"","created":"","id":"c","updated":""}],"dNew":{"collectionId":"","collectionName":"","created":"","id":"dNew","updated":""}}`
if expected != rawStr {
t.Fatalf("Expected \n%v, \ngot \n%v", expected, rawStr)
}
}
func TestRecordMergeExpandNilCheck(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
scenarios := []struct {
name string
expand map[string]any
expected string
}{
{
"nil expand",
nil,
`{"collectionId":"","collectionName":"","created":"","id":"","updated":""}`,
},
{
"empty expand",
map[string]any{},
`{"collectionId":"","collectionName":"","created":"","id":"","updated":""}`,
},
{
"non-empty expand",
map[string]any{"test": models.NewRecord(collection)},
`{"collectionId":"","collectionName":"","created":"","expand":{"test":{"collectionId":"","collectionName":"","created":"","id":"","updated":""}},"id":"","updated":""}`,
},
}
for _, s := range scenarios {
m := models.NewRecord(collection)
m.MergeExpand(s.expand)
raw, err := json.Marshal(m)
if err != nil {
t.Fatal(err)
}
rawStr := string(raw)
if rawStr != s.expected {
t.Fatalf("[%s] Expected \n%v, \ngot \n%v", s.name, s.expected, rawStr)
}
}
}
func TestRecordExpandedRel(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
main := models.NewRecord(collection)
single := models.NewRecord(collection)
single.Id = "single"
multiple1 := models.NewRecord(collection)
multiple1.Id = "multiple1"
multiple2 := models.NewRecord(collection)
multiple2.Id = "multiple2"
main.SetExpand(map[string]any{
"single": single,
"multiple": []*models.Record{multiple1, multiple2},
})
if v := main.ExpandedOne("missing"); v != nil {
t.Fatalf("Expected nil, got %v", v)
}
if v := main.ExpandedOne("single"); v == nil || v.Id != "single" {
t.Fatalf("Expected record with id %q, got %v", "single", v)
}
if v := main.ExpandedOne("multiple"); v == nil || v.Id != "multiple1" {
t.Fatalf("Expected record with id %q, got %v", "multiple1", v)
}
}
func TestRecordExpandedAll(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
main := models.NewRecord(collection)
single := models.NewRecord(collection)
single.Id = "single"
multiple1 := models.NewRecord(collection)
multiple1.Id = "multiple1"
multiple2 := models.NewRecord(collection)
multiple2.Id = "multiple2"
main.SetExpand(map[string]any{
"single": single,
"multiple": []*models.Record{multiple1, multiple2},
})
if v := main.ExpandedAll("missing"); v != nil {
t.Fatalf("Expected nil, got %v", v)
}
if v := main.ExpandedAll("single"); len(v) != 1 || v[0].Id != "single" {
t.Fatalf("Expected [single] slice, got %v", v)
}
if v := main.ExpandedAll("multiple"); len(v) != 2 || v[0].Id != "multiple1" || v[1].Id != "multiple2" {
t.Fatalf("Expected [multiple1, multiple2] slice, got %v", v)
}
}
func TestRecordSchemaData(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Type: models.CollectionTypeAuth,
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeNumber,
},
),
}
m := models.NewRecord(collection)
m.Set("email", "test@example.com")
m.Set("field1", 123)
m.Set("field2", 456)
m.Set("unknown", 789)
encoded, err := json.Marshal(m.SchemaData())
if err != nil {
t.Fatal(err)
}
expected := `{"field1":"123","field2":456}`
if v := string(encoded); v != expected {
t.Fatalf("Expected \n%v \ngot \n%v", v, expected)
}
}
func TestRecordUnknownData(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeNumber,
},
),
}
data := map[string]any{
"id": "test_id",
"created": "2022-01-01 00:00:00.000",
"updated": "2022-01-01 00:00:00.000",
"collectionId": "test_collectionId",
"collectionName": "test_collectionName",
"expand": "test_expand",
"field1": "test_field1",
"field2": "test_field1",
"unknown1": "test_unknown1",
"unknown2": "test_unknown2",
"passwordHash": "test_passwordHash",
"username": "test_username",
"emailVisibility": true,
"email": "test_email",
"verified": true,
"tokenKey": "test_tokenKey",
"lastResetSentAt": "2022-01-01 00:00:00.000",
"lastVerificationSentAt": "2022-01-01 00:00:00.000",
}
scenarios := []struct {
collectionType string
expectedKeys []string
}{
{
models.CollectionTypeBase,
[]string{
"unknown1",
"unknown2",
"passwordHash",
"username",
"emailVisibility",
"email",
"verified",
"tokenKey",
"lastResetSentAt",
"lastVerificationSentAt",
},
},
{
models.CollectionTypeAuth,
[]string{"unknown1", "unknown2"},
},
}
for i, s := range scenarios {
collection.Type = s.collectionType
m := models.NewRecord(collection)
m.Load(data)
result := m.UnknownData()
if len(result) != len(s.expectedKeys) {
t.Errorf("(%d) Expected data \n%v \ngot \n%v", i, s.expectedKeys, result)
continue
}
for _, key := range s.expectedKeys {
if _, ok := result[key]; !ok {
t.Errorf("(%d) Missing expected key %q in \n%v", i, key, result)
}
}
}
}
func TestRecordSetAndGet(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeNumber,
},
// fields that are not explicitly set to check
// the default retrieval value (single and multiple)
&schema.SchemaField{
Name: "field3",
Type: schema.FieldTypeBool,
},
&schema.SchemaField{
Name: "field4",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 2},
},
&schema.SchemaField{
Name: "field5",
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)},
},
),
}
m := models.NewRecord(collection)
m.Set("id", "test_id")
m.Set("created", "2022-09-15 00:00:00.123Z")
m.Set("updated", "invalid")
m.Set("field1", 123) // should be casted to string
m.Set("field2", "invlaid") // should be casted to zero-number
m.Set("unknown", 456) // undefined fields are allowed but not exported by default
m.Set("expand", map[string]any{"test": 123}) // should store the value in m.expand
if v := m.Get("id"); v != "test_id" {
t.Fatalf("Expected id %q, got %q", "test_id", v)
}
if v := m.GetString("created"); v != "2022-09-15 00:00:00.123Z" {
t.Fatalf("Expected created %q, got %q", "2022-09-15 00:00:00.123Z", v)
}
if v := m.GetString("updated"); v != "" {
t.Fatalf("Expected updated to be empty, got %q", v)
}
if v, ok := m.Get("field1").(string); !ok || v != "123" {
t.Fatalf("Expected field1 %#v, got %#v", "123", m.Get("field1"))
}
if v, ok := m.Get("field2").(float64); !ok || v != 0.0 {
t.Fatalf("Expected field2 %#v, got %#v", 0.0, m.Get("field2"))
}
if v, ok := m.Get("field3").(bool); !ok || v != false {
t.Fatalf("Expected field3 %#v, got %#v", false, m.Get("field3"))
}
if v, ok := m.Get("field4").([]string); !ok || len(v) != 0 {
t.Fatalf("Expected field4 %#v, got %#v", "[]", m.Get("field4"))
}
if v, ok := m.Get("field5").(string); !ok || len(v) != 0 {
t.Fatalf("Expected field5 %#v, got %#v", "", m.Get("field5"))
}
if v := m.Get("unknown"); v != 456 {
t.Fatalf("Expected unknown %v, got %v", 456, v)
}
if m.Expand()["test"] != 123 {
t.Fatalf("Expected expand to be %v, got %v", map[string]any{"test": 123}, m.Expand())
}
}
func TestRecordGetBool(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected bool
}{
{nil, false},
{"", false},
{0, false},
{1, true},
{[]string{"true"}, false},
{time.Now(), false},
{"test", false},
{"false", false},
{"true", true},
{false, false},
{true, true},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetBool("test")
if result != s.expected {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, result)
}
}
}
func TestRecordGetString(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected string
}{
{nil, ""},
{"", ""},
{0, "0"},
{1.4, "1.4"},
{[]string{"true"}, ""},
{map[string]int{"test": 1}, ""},
{[]byte("abc"), "abc"},
{"test", "test"},
{false, "false"},
{true, "true"},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetString("test")
if result != s.expected {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, result)
}
}
}
func TestRecordGetInt(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected int
}{
{nil, 0},
{"", 0},
{[]string{"true"}, 0},
{map[string]int{"test": 1}, 0},
{time.Now(), 0},
{"test", 0},
{123, 123},
{2.4, 2},
{"123", 123},
{"123.5", 0},
{false, 0},
{true, 1},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetInt("test")
if result != s.expected {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, result)
}
}
}
func TestRecordGetFloat(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected float64
}{
{nil, 0},
{"", 0},
{[]string{"true"}, 0},
{map[string]int{"test": 1}, 0},
{time.Now(), 0},
{"test", 0},
{123, 123},
{2.4, 2.4},
{"123", 123},
{"123.5", 123.5},
{false, 0},
{true, 1},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetFloat("test")
if result != s.expected {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, result)
}
}
}
func TestRecordGetTime(t *testing.T) {
t.Parallel()
nowTime := time.Now()
testTime, _ := time.Parse(types.DefaultDateLayout, "2022-01-01 08:00:40.000Z")
scenarios := []struct {
value any
expected time.Time
}{
{nil, time.Time{}},
{"", time.Time{}},
{false, time.Time{}},
{true, time.Time{}},
{"test", time.Time{}},
{[]string{"true"}, time.Time{}},
{map[string]int{"test": 1}, time.Time{}},
{1641024040, testTime},
{"2022-01-01 08:00:40.000", testTime},
{nowTime, nowTime},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetTime("test")
if !result.Equal(s.expected) {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, result)
}
}
}
func TestRecordGetDateTime(t *testing.T) {
t.Parallel()
nowTime := time.Now()
testTime, _ := time.Parse(types.DefaultDateLayout, "2022-01-01 08:00:40.000Z")
scenarios := []struct {
value any
expected time.Time
}{
{nil, time.Time{}},
{"", time.Time{}},
{false, time.Time{}},
{true, time.Time{}},
{"test", time.Time{}},
{[]string{"true"}, time.Time{}},
{map[string]int{"test": 1}, time.Time{}},
{1641024040, testTime},
{"2022-01-01 08:00:40.000", testTime},
{nowTime, nowTime},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetDateTime("test")
if !result.Time().Equal(s.expected) {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, result)
}
}
}
func TestRecordGetStringSlice(t *testing.T) {
t.Parallel()
nowTime := time.Now()
scenarios := []struct {
value any
expected []string
}{
{nil, []string{}},
{"", []string{}},
{false, []string{"false"}},
{true, []string{"true"}},
{nowTime, []string{}},
{123, []string{"123"}},
{"test", []string{"test"}},
{map[string]int{"test": 1}, []string{}},
{`["test1", "test2"]`, []string{"test1", "test2"}},
{[]int{123, 123, 456}, []string{"123", "456"}},
{[]string{"test", "test", "123"}, []string{"test", "123"}},
}
collection := &models.Collection{}
for i, s := range scenarios {
m := models.NewRecord(collection)
m.Set("test", s.value)
result := m.GetStringSlice("test")
if len(result) != len(s.expected) {
t.Errorf("(%d) Expected %d elements, got %d: %v", i, len(s.expected), len(result), result)
continue
}
for _, v := range result {
if !list.ExistInSlice(v, s.expected) {
t.Errorf("(%d) Cannot find %v in %v", i, v, s.expected)
}
}
}
}
func TestRecordUnmarshalJSONField(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(&schema.SchemaField{
Name: "field",
Type: schema.FieldTypeJson,
}),
}
m := models.NewRecord(collection)
var testPointer *string
var testStr string
var testInt int
var testBool bool
var testSlice []int
var testMap map[string]any
scenarios := []struct {
value any
destination any
expectError bool
expectedJson string
}{
{nil, testStr, true, `""`},
{"", testStr, false, `""`},
{1, testInt, false, `1`},
{true, testBool, false, `true`},
{[]int{1, 2, 3}, testSlice, false, `[1,2,3]`},
{map[string]any{"test": 123}, testMap, false, `{"test":123}`},
// json encoded values
{`null`, testPointer, false, `null`},
{`true`, testBool, false, `true`},
{`456`, testInt, false, `456`},
{`"test"`, testStr, false, `"test"`},
{`[4,5,6]`, testSlice, false, `[4,5,6]`},
{`{"test":456}`, testMap, false, `{"test":456}`},
}
for i, s := range scenarios {
m.Set("field", s.value)
err := m.UnmarshalJSONField("field", &s.destination)
hasErr := err != nil
if hasErr != s.expectError {
t.Errorf("(%d) Expected hasErr %v, got %v", i, s.expectError, hasErr)
continue
}
raw, _ := json.Marshal(s.destination)
if v := string(raw); v != s.expectedJson {
t.Errorf("(%d) Expected %q, got %q", i, s.expectedJson, v)
}
}
}
func TestRecordBaseFilesPath(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
collection.RefreshId()
collection.Name = "test"
m := models.NewRecord(collection)
m.RefreshId()
expected := collection.BaseFilesPath() + "/" + m.Id
result := m.BaseFilesPath()
if result != expected {
t.Fatalf("Expected %q, got %q", expected, result)
}
}
func TestRecordFindFileFieldByFile(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{
MaxSelect: 1,
MaxSize: 1,
},
},
&schema.SchemaField{
Name: "field3",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{
MaxSelect: 2,
MaxSize: 1,
},
},
),
}
m := models.NewRecord(collection)
m.Set("field1", "test")
m.Set("field2", "test.png")
m.Set("field3", []string{"test1.png", "test2.png"})
scenarios := []struct {
filename string
expectField string
}{
{"", ""},
{"test", ""},
{"test2", ""},
{"test.png", "field2"},
{"test2.png", "field3"},
}
for i, s := range scenarios {
result := m.FindFileFieldByFile(s.filename)
var fieldName string
if result != nil {
fieldName = result.Name
}
if s.expectField != fieldName {
t.Errorf("(%d) Expected field %v, got %v", i, s.expectField, result)
continue
}
}
}
func TestRecordLoadAndData(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeNumber,
},
),
}
data := map[string]any{
"id": "test_id",
"created": "2022-01-01 10:00:00.123Z",
"updated": "2022-01-01 10:00:00.456Z",
"field1": "test_field",
"field2": "123", // should be casted to float
"unknown": "test_unknown",
// auth collection sepcific casting test
"passwordHash": "test_passwordHash",
"emailVisibility": "12345", // should be casted to bool only for auth collections
"username": 123, // should be casted to string only for auth collections
"email": "test_email",
"verified": true,
"tokenKey": "test_tokenKey",
"lastResetSentAt": "2022-01-01 11:00:00.000", // should be casted to DateTime only for auth collections
"lastVerificationSentAt": "2022-01-01 12:00:00.000", // should be casted to DateTime only for auth collections
}
scenarios := []struct {
collectionType string
}{
{models.CollectionTypeBase},
{models.CollectionTypeAuth},
}
for i, s := range scenarios {
collection.Type = s.collectionType
m := models.NewRecord(collection)
m.Load(data)
expectations := map[string]any{}
for k, v := range data {
expectations[k] = v
}
expectations["created"], _ = types.ParseDateTime("2022-01-01 10:00:00.123Z")
expectations["updated"], _ = types.ParseDateTime("2022-01-01 10:00:00.456Z")
expectations["field2"] = 123.0
// extra casting test
if collection.IsAuth() {
lastResetSentAt, _ := types.ParseDateTime(expectations["lastResetSentAt"])
lastVerificationSentAt, _ := types.ParseDateTime(expectations["lastVerificationSentAt"])
expectations["emailVisibility"] = false
expectations["username"] = "123"
expectations["verified"] = true
expectations["lastResetSentAt"] = lastResetSentAt
expectations["lastVerificationSentAt"] = lastVerificationSentAt
}
for k, v := range expectations {
if m.Get(k) != v {
t.Errorf("(%d) Expected field %s to be %v, got %v", i, k, v, m.Get(k))
}
}
}
}
func TestRecordColumnValueMap(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{
MaxSelect: 1,
MaxSize: 1,
},
},
&schema.SchemaField{
Name: "field3",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{
MaxSelect: 2,
Values: []string{"test1", "test2", "test3"},
},
},
&schema.SchemaField{
Name: "field4",
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{
MaxSelect: types.Pointer(2),
},
},
),
}
scenarios := []struct {
collectionType string
expectedJson string
}{
{
models.CollectionTypeBase,
`{"created":"2022-01-01 10:00:30.123Z","field1":"test","field2":"test.png","field3":["test1","test2"],"field4":["test11","test12"],"id":"test_id","updated":""}`,
},
{
models.CollectionTypeAuth,
`{"created":"2022-01-01 10:00:30.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":"test.png","field3":["test1","test2"],"field4":["test11","test12"],"id":"test_id","lastResetSentAt":"2022-01-02 10:00:30.123Z","lastVerificationSentAt":"","passwordHash":"test_passwordHash","tokenKey":"test_tokenKey","updated":"","username":"test_username","verified":false}`,
},
}
created, _ := types.ParseDateTime("2022-01-01 10:00:30.123Z")
lastResetSentAt, _ := types.ParseDateTime("2022-01-02 10:00:30.123Z")
data := map[string]any{
"id": "test_id",
"created": created,
"field1": "test",
"field2": "test.png",
"field3": []string{"test1", "test2"},
"field4": []string{"test11", "test12", "test11"}, // strip duplicate,
"unknown": "test_unknown",
"passwordHash": "test_passwordHash",
"username": "test_username",
"emailVisibility": true,
"email": "test_email",
"verified": "invalid", // should be casted
"tokenKey": "test_tokenKey",
"lastResetSentAt": lastResetSentAt,
}
m := models.NewRecord(collection)
for i, s := range scenarios {
collection.Type = s.collectionType
m.Load(data)
result := m.ColumnValueMap()
encoded, err := json.Marshal(result)
if err != nil {
t.Errorf("(%d) Unexpected error %v", i, err)
continue
}
if str := string(encoded); str != s.expectedJson {
t.Errorf("(%d) Expected \n%v \ngot \n%v", i, s.expectedJson, str)
}
}
}
func TestRecordPublicExportAndMarshalJSON(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "c_name",
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{
MaxSelect: 1,
MaxSize: 1,
},
},
&schema.SchemaField{
Name: "field3",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{
MaxSelect: 2,
Values: []string{"test1", "test2", "test3"},
},
},
),
}
collection.Id = "c_id"
scenarios := []struct {
collectionType string
exportHidden bool
exportUnknown bool
expectedJson string
}{
// base
{
models.CollectionTypeBase,
false,
false,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","updated":""}`,
},
{
models.CollectionTypeBase,
true,
false,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","updated":""}`,
},
{
models.CollectionTypeBase,
false,
true,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","email":"test_email","emailVisibility":"test_invalid","expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","lastResetSentAt":"2022-01-02 10:00:30.123Z","lastVerificationSentAt":"test_lastVerificationSentAt","passwordHash":"test_passwordHash","tokenKey":"test_tokenKey","unknown":"test_unknown","updated":"","username":123,"verified":true}`,
},
{
models.CollectionTypeBase,
true,
true,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","email":"test_email","emailVisibility":"test_invalid","expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","lastResetSentAt":"2022-01-02 10:00:30.123Z","lastVerificationSentAt":"test_lastVerificationSentAt","passwordHash":"test_passwordHash","tokenKey":"test_tokenKey","unknown":"test_unknown","updated":"","username":123,"verified":true}`,
},
// auth
{
models.CollectionTypeAuth,
false,
false,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","emailVisibility":false,"expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","updated":"","username":"123","verified":true}`,
},
{
models.CollectionTypeAuth,
true,
false,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","email":"test_email","emailVisibility":false,"expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","updated":"","username":"123","verified":true}`,
},
{
models.CollectionTypeAuth,
false,
true,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","emailVisibility":false,"expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","unknown":"test_unknown","updated":"","username":"123","verified":true}`,
},
{
models.CollectionTypeAuth,
true,
true,
`{"collectionId":"c_id","collectionName":"c_name","created":"2022-01-01 10:00:30.123Z","email":"test_email","emailVisibility":false,"expand":{"test":123},"field1":"test","field2":"test.png","field3":["test1","test2"],"id":"test_id","unknown":"test_unknown","updated":"","username":"123","verified":true}`,
},
}
created, _ := types.ParseDateTime("2022-01-01 10:00:30.123Z")
lastResetSentAt, _ := types.ParseDateTime("2022-01-02 10:00:30.123Z")
data := map[string]any{
"id": "test_id",
"created": created,
"field1": "test",
"field2": "test.png",
"field3": []string{"test1", "test2"},
"expand": map[string]any{"test": 123},
"collectionId": "m_id", // should be always ignored
"collectionName": "m_name", // should be always ignored
"unknown": "test_unknown",
"passwordHash": "test_passwordHash",
"username": 123, // for auth collections should be casted to string
"emailVisibility": "test_invalid", // for auth collections should be casted to bool
"email": "test_email",
"verified": true,
"tokenKey": "test_tokenKey",
"lastResetSentAt": lastResetSentAt,
"lastVerificationSentAt": "test_lastVerificationSentAt",
}
m := models.NewRecord(collection)
for i, s := range scenarios {
collection.Type = s.collectionType
m.Load(data)
m.IgnoreEmailVisibility(s.exportHidden)
m.WithUnknownData(s.exportUnknown)
exportResult, err := json.Marshal(m.PublicExport())
if err != nil {
t.Errorf("(%d) Unexpected error %v", i, err)
continue
}
exportResultStr := string(exportResult)
// MarshalJSON and PublicExport should return the same
marshalResult, err := m.MarshalJSON()
if err != nil {
t.Errorf("(%d) Unexpected error %v", i, err)
continue
}
marshalResultStr := string(marshalResult)
if exportResultStr != marshalResultStr {
t.Errorf("(%d) Expected the PublicExport to be the same as MarshalJSON, but got \n%v \nvs \n%v", i, exportResultStr, marshalResultStr)
}
if exportResultStr != s.expectedJson {
t.Errorf("(%d) Expected json \n%v \ngot \n%v", i, s.expectedJson, exportResultStr)
}
}
}
func TestRecordUnmarshalJSON(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "field1",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "field2",
Type: schema.FieldTypeNumber,
},
),
}
data := map[string]any{
"id": "test_id",
"created": "2022-01-01 10:00:00.123Z",
"updated": "2022-01-01 10:00:00.456Z",
"field1": "test_field",
"field2": "123", // should be casted to float
"unknown": "test_unknown",
// auth collection sepcific casting test
"passwordHash": "test_passwordHash",
"emailVisibility": "12345", // should be casted to bool only for auth collections
"username": 123.123, // should be casted to string only for auth collections
"email": "test_email",
"verified": true,
"tokenKey": "test_tokenKey",
"lastResetSentAt": "2022-01-01 11:00:00.000", // should be casted to DateTime only for auth collections
"lastVerificationSentAt": "2022-01-01 12:00:00.000", // should be casted to DateTime only for auth collections
}
dataRaw, err := json.Marshal(data)
if err != nil {
t.Fatalf("Unexpected data marshal error %v", err)
}
scenarios := []struct {
collectionType string
}{
{models.CollectionTypeBase},
{models.CollectionTypeAuth},
}
// with invalid data
m0 := models.NewRecord(collection)
if err := m0.UnmarshalJSON([]byte("test")); err == nil {
t.Fatal("Expected error, got nil")
}
// with valid data (it should be pretty much the same as load)
for i, s := range scenarios {
collection.Type = s.collectionType
m := models.NewRecord(collection)
err := m.UnmarshalJSON(dataRaw)
if err != nil {
t.Errorf("(%d) Unexpected error %v", i, err)
continue
}
expectations := map[string]any{}
for k, v := range data {
expectations[k] = v
}
expectations["created"], _ = types.ParseDateTime("2022-01-01 10:00:00.123Z")
expectations["updated"], _ = types.ParseDateTime("2022-01-01 10:00:00.456Z")
expectations["field2"] = 123.0
// extra casting test
if collection.IsAuth() {
lastResetSentAt, _ := types.ParseDateTime(expectations["lastResetSentAt"])
lastVerificationSentAt, _ := types.ParseDateTime(expectations["lastVerificationSentAt"])
expectations["emailVisibility"] = false
expectations["username"] = "123.123"
expectations["verified"] = true
expectations["lastResetSentAt"] = lastResetSentAt
expectations["lastVerificationSentAt"] = lastVerificationSentAt
}
for k, v := range expectations {
if m.Get(k) != v {
t.Errorf("(%d) Expected field %s to be %v, got %v", i, k, v, m.Get(k))
}
}
}
}
func TestRecordReplaceModifers(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
Name: "text",
Type: schema.FieldTypeText,
},
&schema.SchemaField{
Name: "number",
Type: schema.FieldTypeNumber,
},
&schema.SchemaField{
Name: "rel_one",
Type: schema.FieldTypeRelation,
Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)},
},
&schema.SchemaField{
Name: "rel_many",
Type: schema.FieldTypeRelation,
},
&schema.SchemaField{
Name: "select_one",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "select_many",
Type: schema.FieldTypeSelect,
Options: &schema.SelectOptions{MaxSelect: 10},
},
&schema.SchemaField{
Name: "file_one",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "file_one_index",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "file_one_name",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 1},
},
&schema.SchemaField{
Name: "file_many",
Type: schema.FieldTypeFile,
Options: &schema.FileOptions{MaxSelect: 10},
},
),
}
record := models.NewRecord(collection)
record.Load(map[string]any{
"text": "test",
"number": 10,
"rel_one": "a",
"rel_many": []string{"a", "b"},
"select_one": "a",
"select_many": []string{"a", "b", "c"},
"file_one": "a",
"file_one_index": "b",
"file_one_name": "c",
"file_many": []string{"a", "b", "c", "d", "e", "f"},
})
result := record.ReplaceModifers(map[string]any{
"text-": "m-",
"text+": "m+",
"number-": 3,
"number+": 5,
"rel_one-": "a",
"rel_one+": "b",
"rel_many-": []string{"a"},
"rel_many+": []string{"c", "d", "e"},
"select_one-": "a",
"select_one+": "c",
"select_many-": []string{"b", "c"},
"select_many+": []string{"d", "e"},
"file_one+": "skip", // should be ignored
"file_one-": "a",
"file_one_index.0": "",
"file_one_name.c": "",
"file_many+": []string{"e", "f"}, // should be ignored
"file_many-": []string{"c", "d"},
"file_many.f": nil,
"file_many.0": nil,
})
raw, err := json.Marshal(result)
if err != nil {
t.Fatal(err)
}
expected := `{"file_many":["b","e"],"file_one":"","file_one_index":"","file_one_name":"","number":12,"rel_many":["b","c","d","e"],"rel_one":"b","select_many":["a","d","e"],"select_one":"c","text":"test"}`
if v := string(raw); v != expected {
t.Fatalf("Expected \n%s, \ngot \n%s", expected, v)
}
}
// -------------------------------------------------------------------
// Auth helpers:
// -------------------------------------------------------------------
func TestRecordUsername(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
}{
{models.CollectionTypeBase, true},
{models.CollectionTypeAuth, false},
}
testValue := "test 1232 !@#%" // formatting isn't checked
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetUsername(testValue); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.Username(); v != "" {
t.Fatalf("(%d) Expected empty string, got %q", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameUsername); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameUsername, v)
}
} else {
if err := m.SetUsername(testValue); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.Username(); v != testValue {
t.Fatalf("(%d) Expected %q, got %q", i, testValue, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameUsername); v != testValue {
t.Fatalf("(%d) Expected data field value %q, got %q", i, testValue, v)
}
}
}
}
func TestRecordEmail(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
}{
{models.CollectionTypeBase, true},
{models.CollectionTypeAuth, false},
}
testValue := "test 1232 !@#%" // formatting isn't checked
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetEmail(testValue); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.Email(); v != "" {
t.Fatalf("(%d) Expected empty string, got %q", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameEmail); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameEmail, v)
}
} else {
if err := m.SetEmail(testValue); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.Email(); v != testValue {
t.Fatalf("(%d) Expected %q, got %q", i, testValue, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameEmail); v != testValue {
t.Fatalf("(%d) Expected data field value %q, got %q", i, testValue, v)
}
}
}
}
func TestRecordEmailVisibility(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
value bool
expectError bool
}{
{models.CollectionTypeBase, true, true},
{models.CollectionTypeBase, true, true},
{models.CollectionTypeAuth, false, false},
{models.CollectionTypeAuth, true, false},
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetEmailVisibility(s.value); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.EmailVisibility(); v != false {
t.Fatalf("(%d) Expected empty string, got %v", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameEmailVisibility); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameEmailVisibility, v)
}
} else {
if err := m.SetEmailVisibility(s.value); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.EmailVisibility(); v != s.value {
t.Fatalf("(%d) Expected %v, got %v", i, s.value, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameEmailVisibility); v != s.value {
t.Fatalf("(%d) Expected data field value %v, got %v", i, s.value, v)
}
}
}
}
func TestRecordEmailVerified(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
value bool
expectError bool
}{
{models.CollectionTypeBase, true, true},
{models.CollectionTypeBase, true, true},
{models.CollectionTypeAuth, false, false},
{models.CollectionTypeAuth, true, false},
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetVerified(s.value); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.Verified(); v != false {
t.Fatalf("(%d) Expected empty string, got %v", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameVerified); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameVerified, v)
}
} else {
if err := m.SetVerified(s.value); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.Verified(); v != s.value {
t.Fatalf("(%d) Expected %v, got %v", i, s.value, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameVerified); v != s.value {
t.Fatalf("(%d) Expected data field value %v, got %v", i, s.value, v)
}
}
}
}
func TestRecordTokenKey(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
}{
{models.CollectionTypeBase, true},
{models.CollectionTypeAuth, false},
}
testValue := "test 1232 !@#%" // formatting isn't checked
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetTokenKey(testValue); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.TokenKey(); v != "" {
t.Fatalf("(%d) Expected empty string, got %q", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameTokenKey); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameTokenKey, v)
}
} else {
if err := m.SetTokenKey(testValue); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.TokenKey(); v != testValue {
t.Fatalf("(%d) Expected %q, got %q", i, testValue, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameTokenKey); v != testValue {
t.Fatalf("(%d) Expected data field value %q, got %q", i, testValue, v)
}
}
}
}
func TestRecordRefreshTokenKey(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
}{
{models.CollectionTypeBase, true},
{models.CollectionTypeAuth, false},
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.RefreshTokenKey(); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.TokenKey(); v != "" {
t.Fatalf("(%d) Expected empty string, got %q", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameTokenKey); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameTokenKey, v)
}
} else {
if err := m.RefreshTokenKey(); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.TokenKey(); len(v) != 50 {
t.Fatalf("(%d) Expected 50 chars, got %d", i, len(v))
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameTokenKey); v != m.TokenKey() {
t.Fatalf("(%d) Expected data field value %q, got %q", i, m.TokenKey(), v)
}
}
}
}
func TestRecordLastResetSentAt(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
}{
{models.CollectionTypeBase, true},
{models.CollectionTypeAuth, false},
}
testValue, err := types.ParseDateTime("2022-01-01 00:00:00.123Z")
if err != nil {
t.Fatal(err)
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetLastResetSentAt(testValue); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.LastResetSentAt(); !v.IsZero() {
t.Fatalf("(%d) Expected empty value, got %v", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameLastResetSentAt); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameLastResetSentAt, v)
}
} else {
if err := m.SetLastResetSentAt(testValue); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.LastResetSentAt(); v != testValue {
t.Fatalf("(%d) Expected %v, got %v", i, testValue, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameLastResetSentAt); v != testValue {
t.Fatalf("(%d) Expected data field value %v, got %v", i, testValue, v)
}
}
}
}
func TestRecordLastVerificationSentAt(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
}{
{models.CollectionTypeBase, true},
{models.CollectionTypeAuth, false},
}
testValue, err := types.ParseDateTime("2022-01-01 00:00:00.123Z")
if err != nil {
t.Fatal(err)
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetLastVerificationSentAt(testValue); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.LastVerificationSentAt(); !v.IsZero() {
t.Fatalf("(%d) Expected empty value, got %v", i, v)
}
// verify that nothing is stored in the record data slice
if v := m.Get(schema.FieldNameLastVerificationSentAt); v != nil {
t.Fatalf("(%d) Didn't expect data field %q: %v", i, schema.FieldNameLastVerificationSentAt, v)
}
} else {
if err := m.SetLastVerificationSentAt(testValue); err != nil {
t.Fatalf("(%d) Expected nil, got error %v", i, err)
}
if v := m.LastVerificationSentAt(); v != testValue {
t.Fatalf("(%d) Expected %v, got %v", i, testValue, v)
}
// verify that the field is stored in the record data slice
if v := m.Get(schema.FieldNameLastVerificationSentAt); v != testValue {
t.Fatalf("(%d) Expected data field value %v, got %v", i, testValue, v)
}
}
}
}
func TestRecordPasswordHash(t *testing.T) {
t.Parallel()
m := models.NewRecord(&models.Collection{})
if v := m.PasswordHash(); v != "" {
t.Errorf("Expected PasswordHash() to be empty, got %v", v)
}
m.Set(schema.FieldNamePasswordHash, "test")
if v := m.PasswordHash(); v != "test" {
t.Errorf("Expected PasswordHash() to be 'test', got %v", v)
}
}
func TestRecordValidatePassword(t *testing.T) {
t.Parallel()
// 123456
hash := "$2a$10$YKU8mPP8sTE3xZrpuM.xQuq27KJ7aIJB2oUeKPsDDqZshbl5g5cDK"
scenarios := []struct {
collectionType string
password string
hash string
expected bool
}{
{models.CollectionTypeBase, "123456", hash, false},
{models.CollectionTypeAuth, "", "", false},
{models.CollectionTypeAuth, "", hash, false},
{models.CollectionTypeAuth, "123456", hash, true},
{models.CollectionTypeAuth, "654321", hash, false},
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
m.Set(schema.FieldNamePasswordHash, hash)
if v := m.ValidatePassword(s.password); v != s.expected {
t.Errorf("(%d) Expected %v, got %v", i, s.expected, v)
}
}
}
func TestRecordSetPassword(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
password string
expectError bool
}{
{models.CollectionTypeBase, "", true},
{models.CollectionTypeBase, "123456", true},
{models.CollectionTypeAuth, "", true},
{models.CollectionTypeAuth, "123456", false},
}
for i, s := range scenarios {
collection := &models.Collection{Type: s.collectionType}
m := models.NewRecord(collection)
if s.expectError {
if err := m.SetPassword(s.password); err == nil {
t.Errorf("(%d) Expected error, got nil", i)
}
if v := m.GetString(schema.FieldNamePasswordHash); v != "" {
t.Errorf("(%d) Expected empty hash, got %q", i, v)
}
} else {
if err := m.SetPassword(s.password); err != nil {
t.Errorf("(%d) Expected nil, got err", i)
}
if v := m.GetString(schema.FieldNamePasswordHash); v == "" {
t.Errorf("(%d) Expected non empty hash", i)
}
if !m.ValidatePassword(s.password) {
t.Errorf("(%d) Expected true, got false", i)
}
}
}
}