gitlab.com/beacon-software/gadget@v0.0.0-20181217202115-54565ea1ed5e/database/model_test.go (about)

     1  package database
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  
    11  	"gitlab.com/beacon-software/gadget/generator"
    12  	"gitlab.com/beacon-software/gadget/log"
    13  )
    14  
    15  func TestNewPrimaryKey(t *testing.T) {
    16  	assert := assert.New(t)
    17  
    18  	pk := NewPrimaryKey(1)
    19  	assert.Equal(1, pk.Value())
    20  
    21  	pk = NewPrimaryKey("foo")
    22  	assert.Equal("foo", pk.Value())
    23  }
    24  
    25  func TestNewListOptions(t *testing.T) {
    26  	assert := assert.New(t)
    27  
    28  	data := []struct {
    29  		Expected *ListOptions
    30  		Limit    uint
    31  		Offset   uint
    32  	}{
    33  		{&ListOptions{Limit: 1, Offset: 0}, 0, 0},
    34  		{&ListOptions{Limit: MaxLimit, Offset: 10}, MaxLimit + 1, 10},
    35  		{&ListOptions{Limit: 5, Offset: 5}, 5, 5},
    36  	}
    37  	for _, test := range data {
    38  		actual := NewListOptions(test.Limit, test.Offset)
    39  		assert.Equal(test.Expected, actual)
    40  	}
    41  }
    42  
    43  func TestBadConnection(t *testing.T) {
    44  	assert := assert.New(t)
    45  	_, err := connect("mysql", "baduser:badpassword@tcp(localhost:3306)/foo", log.NewStackLogger())
    46  	assert.EqualError(err, NewDatabaseConnectionError().Error())
    47  }
    48  
    49  func TestBadRecordImplementation(t *testing.T) {
    50  	assert := assert.New(t)
    51  	spec := newSpecification()
    52  
    53  	record := &DetailsTestRecord{}
    54  
    55  	err := spec.DB.Create(record)
    56  	assert.IsType(&SQLExecutionError{}, err)
    57  	assert.IsType(&SQLExecutionError{}, spec.DB.Read(record, NewPrimaryKey("foo")))
    58  	options := NewListOptions(10, 0)
    59  	assert.IsType(&SQLExecutionError{}, spec.DB.List(record, []DetailsTestRecord{}, options))
    60  	assert.IsType(&SQLExecutionError{}, spec.DB.Update(record))
    61  	assert.IsType(&SQLExecutionError{}, spec.DB.Delete(record))
    62  }
    63  
    64  func TestCreate(t *testing.T) {
    65  	assert := assert.New(t)
    66  	spec := newSpecification()
    67  
    68  	name := generator.Name()
    69  	record := &TestRecord{Name: name}
    70  	assert.NoError(spec.DB.Create(record))
    71  	assert.Equal(name, record.Name)
    72  	assert.Equal("tst", record.ID[:3])
    73  	assert.Equal(record.PrimaryKey().Value(), record.ID)
    74  }
    75  
    76  func TestCreateDuplicate(t *testing.T) {
    77  	assert := assert.New(t)
    78  	spec := newSpecification()
    79  
    80  	name := generator.Name()
    81  	record := &TestRecord{Name: name}
    82  	assert.NoError(spec.DB.Create(record))
    83  	assert.Equal(name, record.Name)
    84  	assert.Equal("tst", record.ID[:3])
    85  
    86  	record = &TestRecord{Name: name}
    87  	err := spec.DB.Create(record)
    88  	assert.IsType(&UniqueConstraintError{}, err)
    89  }
    90  
    91  func TestRead(t *testing.T) {
    92  	assert := assert.New(t)
    93  	spec := newSpecification()
    94  
    95  	expected := &TestRecord{Name: generator.Name()}
    96  	assert.NoError(spec.DB.Create(expected))
    97  
    98  	actual := &TestRecord{}
    99  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   100  	assert.Equal(expected, actual)
   101  }
   102  
   103  func TestReadOneWhere(t *testing.T) {
   104  	assert := assert.New(t)
   105  	spec := newSpecification()
   106  
   107  	expected := &TestRecord{Name: generator.Name()}
   108  	assert.NoError(spec.DB.Create(expected))
   109  
   110  	actual := &TestRecord{}
   111  	assert.NoError(spec.DB.ReadOneWhere(actual, TestMeta.Name.Equal(expected.Name)))
   112  	assert.Equal(expected, actual)
   113  }
   114  
   115  func TestList(t *testing.T) {
   116  	assert := assert.New(t)
   117  	spec := newSpecification()
   118  
   119  	records := make([]TestRecord, 5)
   120  	for i := range records {
   121  		record := &TestRecord{Name: fmt.Sprintf("Test %s", strconv.Itoa(i))}
   122  		assert.NoError(spec.DB.Create(record))
   123  		records[i] = *record
   124  	}
   125  
   126  	lookup := &[]TestRecord{}
   127  	options := NewListOptions(3, 0)
   128  	assert.NoError(spec.DB.List(&TestRecord{}, lookup, options))
   129  	assert.Equal(3, len(*lookup))
   130  }
   131  
   132  func TestWhere(t *testing.T) {
   133  	assert := assert.New(t)
   134  	spec := newSpecification()
   135  
   136  	records := make([]*TestRecord, 5)
   137  	for i := range records {
   138  		record := &TestRecord{Name: generator.Name()}
   139  		assert.NoError(spec.DB.Create(record))
   140  		records[i] = record
   141  	}
   142  
   143  	where := TestMeta.Name.Equal(records[0].Name)
   144  	actual := []*TestRecord{}
   145  	err := spec.DB.ListWhere(&TestRecord{}, &actual, where)
   146  	if assert.NoError(err) {
   147  		assert.Equal(records[0], actual[0])
   148  		assert.Equal(1, len(actual))
   149  	}
   150  	where = TestMeta.Name.Equal(records[0])
   151  	err = spec.DB.ListWhere(&TestRecord{}, &actual, where)
   152  	assert.Error(err)
   153  }
   154  
   155  func TestWhereTx(t *testing.T) {
   156  	assert := assert.New(t)
   157  	spec := newSpecification()
   158  
   159  	tx := spec.DB.MustBegin()
   160  	defer tx.Commit()
   161  	records := make([]*TestRecord, 5)
   162  	for i := range records {
   163  		record := &TestRecord{Name: generator.Name()}
   164  		assert.NoError(spec.DB.CreateTx(record, tx))
   165  		records[i] = record
   166  	}
   167  
   168  	where := TestMeta.Name.Equal(records[0].Name)
   169  	actual := []*TestRecord{}
   170  	err := spec.DB.ListWhere(&TestRecord{}, &actual, where)
   171  	if assert.NoError(err) {
   172  		assert.Equal(0, len(actual))
   173  	}
   174  
   175  	err = spec.DB.ListWhereTx(tx, &TestRecord{}, &actual, where)
   176  	if assert.NoError(err) {
   177  		assert.Equal(records[0], actual[0])
   178  		assert.Equal(1, len(actual))
   179  	}
   180  	where = TestMeta.Name.Equal(records[0])
   181  	err = spec.DB.ListWhereTx(tx, &TestRecord{}, &actual, where)
   182  	assert.Error(err)
   183  }
   184  
   185  func TestWhereIn(t *testing.T) {
   186  	assert := assert.New(t)
   187  	spec := newSpecification()
   188  
   189  	records := make([]*TestRecord, 5)
   190  	for i := range records {
   191  		record := &TestRecord{Name: generator.Name()}
   192  		assert.NoError(spec.DB.Create(record))
   193  		records[i] = record
   194  		time.Sleep(time.Second)
   195  	}
   196  
   197  	where := TestMeta.Name.In(records[0].Name, records[1].Name)
   198  
   199  	actual := []*TestRecord{}
   200  	err := spec.DB.ListWhere(&TestRecord{}, &actual, where)
   201  	if assert.NoError(err) {
   202  		assert.Equal(records[0], actual[0])
   203  		assert.Equal(records[1], actual[1])
   204  		assert.Equal(2, len(actual))
   205  	}
   206  	where = TestMeta.Name.In(records[0])
   207  	assert.Error(spec.DB.ListWhere(&TestRecord{}, &actual, where))
   208  
   209  	where = TestMeta.Name.In(records[0].Name, records[1].Name).And(TestMeta.ID.Equal(records[0].ID))
   210  	actual = []*TestRecord{}
   211  	err = spec.DB.ListWhere(&TestRecord{}, &actual, where)
   212  	if assert.NoError(err) {
   213  		assert.Equal(records[0], actual[0])
   214  		assert.Equal(1, len(actual))
   215  	}
   216  
   217  	where = TestMeta.ID.Equal(records[1].ID).And(TestMeta.Name.In(records[0].Name, records[1].Name))
   218  	actual = []*TestRecord{}
   219  	err = spec.DB.ListWhere(&TestRecord{}, &actual, where)
   220  	if assert.NoError(err) {
   221  		assert.Equal(records[1], actual[0])
   222  		assert.Equal(1, len(actual))
   223  	}
   224  }
   225  
   226  func TestDelete(t *testing.T) {
   227  	assert := assert.New(t)
   228  	spec := newSpecification()
   229  
   230  	expected := &TestRecord{Name: generator.Name()}
   231  	assert.NoError(spec.DB.Create(expected))
   232  
   233  	actual := &TestRecord{}
   234  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   235  	assert.Equal(expected, actual)
   236  
   237  	assert.NoError(spec.DB.Delete(actual))
   238  	assert.IsType(&NotFoundError{}, spec.DB.Read(actual, expected.PrimaryKey()))
   239  	assert.NoError(spec.DB.Delete(actual))
   240  }
   241  
   242  func TestDeleteTx(t *testing.T) {
   243  	assert := assert.New(t)
   244  	spec := newSpecification()
   245  
   246  	expected := &TestRecord{Name: generator.Name()}
   247  	assert.NoError(spec.DB.Create(expected))
   248  
   249  	actual := &TestRecord{}
   250  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   251  	assert.Equal(expected, actual)
   252  
   253  	tx := spec.DB.MustBegin()
   254  	assert.NoError(spec.DB.DeleteTx(actual, tx))
   255  	tx.Rollback()
   256  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   257  
   258  	tx = spec.DB.MustBegin()
   259  	assert.NoError(spec.DB.DeleteTx(actual, tx))
   260  	tx.Commit()
   261  	assert.IsType(&NotFoundError{}, spec.DB.Read(actual, expected.PrimaryKey()))
   262  }
   263  
   264  func TestDeleteWhere(t *testing.T) {
   265  	assert := assert.New(t)
   266  	spec := newSpecification()
   267  
   268  	expected := &TestRecord{Name: generator.Name()}
   269  	assert.NoError(spec.DB.Create(expected))
   270  
   271  	actual := &TestRecord{}
   272  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   273  	assert.Equal(expected, actual)
   274  
   275  	assert.NoError(spec.DB.DeleteWhere(actual, TestMeta.Name.Equal(expected.Name)))
   276  	assert.IsType(&NotFoundError{}, spec.DB.Read(actual, expected.PrimaryKey()))
   277  	assert.NoError(spec.DB.DeleteWhere(actual, TestMeta.Name.Equal(expected.Name)))
   278  }
   279  
   280  func TestDeleteWhereTx(t *testing.T) {
   281  	assert := assert.New(t)
   282  	spec := newSpecification()
   283  
   284  	expected := &TestRecord{Name: "DeleteWhereMe"}
   285  	assert.NoError(spec.DB.Create(expected))
   286  
   287  	actual := &TestRecord{}
   288  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   289  	assert.Equal(expected, actual)
   290  
   291  	tx := spec.DB.MustBegin()
   292  	assert.NoError(spec.DB.DeleteWhereTx(actual, tx, TestMeta.Name.Equal(expected.Name)))
   293  	tx.Rollback()
   294  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   295  
   296  	tx = spec.DB.MustBegin()
   297  	assert.NoError(spec.DB.DeleteWhereTx(actual, tx, TestMeta.Name.Equal(expected.Name)))
   298  	tx.Commit()
   299  	assert.IsType(&NotFoundError{}, spec.DB.Read(actual, expected.PrimaryKey()))
   300  }
   301  
   302  func TestUpdate(t *testing.T) {
   303  	assert := assert.New(t)
   304  	spec := newSpecification()
   305  
   306  	expected := &TestRecord{Name: "Update Me"}
   307  	assert.NoError(spec.DB.Create(expected))
   308  
   309  	actual := &TestRecord{}
   310  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   311  	assert.Equal(expected, actual)
   312  
   313  	expected.Name = "Updated"
   314  	assert.NoError(spec.DB.Update(expected))
   315  	assert.NoError(spec.DB.Read(actual, expected.PrimaryKey()))
   316  	assert.Equal(expected, actual)
   317  }
   318  
   319  func TestUpsertTx(t *testing.T) {
   320  	assert := assert.New(t)
   321  	spec := newSpecification()
   322  
   323  	recordID := generator.ID("tst")
   324  	record := &TestRecord{
   325  		ID:        recordID,
   326  		Name:      generator.Name(),
   327  		CreatedOn: time.Now(),
   328  		UpdatedOn: time.Now(),
   329  	}
   330  	tx := spec.DB.MustBegin()
   331  	updatedOn := record.UpdatedOn
   332  	assert.NoError(spec.DB.UpsertTx(record, tx))
   333  	assert.Equal(recordID, record.ID)
   334  	assert.NotEqual(updatedOn, record.UpdatedOn)
   335  
   336  	updatedOn = record.UpdatedOn
   337  	err := spec.DB.UpsertTx(record, tx)
   338  	assert.NoError(err)
   339  	assert.Equal(updatedOn, record.UpdatedOn)
   340  
   341  	time.Sleep(2 * time.Second)
   342  	expected := generator.Name()
   343  	assert.NotEqual(expected, record.Name)
   344  	record.Name = expected
   345  	assert.NoError(spec.DB.UpsertTx(record, tx))
   346  	assert.Equal(updatedOn, record.UpdatedOn)
   347  	assert.Equal(expected, record.Name)
   348  	tx.Rollback()
   349  }
   350  
   351  func TestTransaction(t *testing.T) {
   352  	assert := assert.New(t)
   353  	spec := newSpecification()
   354  
   355  	name := generator.Name()
   356  	record := &TestRecord{Name: name}
   357  	tx, err := spec.DB.Beginx()
   358  	assert.NoError(err)
   359  
   360  	assert.NoError(spec.DB.CreateTx(record, tx))
   361  	assert.Equal(name, record.Name)
   362  	assert.Equal("tst", record.ID[:3])
   363  	assert.Equal(record.PrimaryKey().Value(), record.ID)
   364  
   365  	actual := &TestRecord{}
   366  	err = spec.DB.ReadTx(actual, record.PrimaryKey(), tx)
   367  	assert.NoError(err)
   368  	tx.Rollback()
   369  
   370  	err = spec.DB.Read(actual, record.PrimaryKey())
   371  	assert.EqualError(err, NewNotFoundError().Error())
   372  }
   373  
   374  type initDupe struct {
   375  	i    int
   376  	base string
   377  }
   378  
   379  func (id *initDupe) id() string {
   380  	defer func() { id.base = generator.ID("nodupe") }()
   381  	return id.base
   382  }
   383  
   384  func TestDuplicateID(t *testing.T) {
   385  	assert := assert.New(t)
   386  	spec := newSpecification()
   387  
   388  	record := NewTestDuper()
   389  	assert.NoError(spec.DB.Create(record))
   390  	assert.Equal("dup", record.ID[:3])
   391  
   392  	record2 := NewTestDuper()
   393  	assert.NoError(spec.DB.Create(record2))
   394  	assert.Equal("dup", record2.ID[:3])
   395  	assert.NotEqual(record2.ID, record.ID)
   396  
   397  	record3 := NewTestDuper()
   398  	record3.intializer = func() string { return record.ID }
   399  	assert.Error(spec.DB.Create(record3))
   400  
   401  	record3 = NewTestDuper()
   402  	initDuper := &initDupe{base: record.ID}
   403  	record3.intializer = func() string { return initDuper.id() }
   404  
   405  	assert.NoError(spec.DB.Create(record3))
   406  	assert.Equal("nodupe", record3.ID[:6])
   407  	assert.NotEqual(record3.ID, record.ID)
   408  }