docs/server/storage/database_test.go

360 lines
11 KiB
Go

package storage
import (
"io/ioutil"
"os"
"testing"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
)
// SampleTUF returns a sample TUFFile with the given Version (ID will have
// to be set independently)
func SampleTUF(version int) TUFFile {
return TUFFile{
Gun: "testGUN",
Role: "root",
Version: version,
Data: []byte("1"),
}
}
func SampleUpdate(version int) MetaUpdate {
return MetaUpdate{
Role: "root",
Version: version,
Data: []byte("1"),
}
}
// SetUpSQLite creates a sqlite database for testing
func SetUpSQLite(t *testing.T, dbDir string) (*gorm.DB, *SQLStorage) {
dbStore, err := NewSQLStorage("sqlite3", dbDir+"test_db")
assert.NoError(t, err)
// Create the DB tables
err = CreateTUFTable(dbStore.DB)
assert.NoError(t, err)
err = CreateTimestampTable(dbStore.DB)
assert.NoError(t, err)
// verify that the tables are empty
var count int
for _, model := range [2]interface{}{&TUFFile{}, &TimestampKey{}} {
query := dbStore.DB.Model(model).Count(&count)
assert.NoError(t, query.Error)
assert.Equal(t, 0, count)
}
return &dbStore.DB, dbStore
}
// TestSQLUpdateCurrent asserts that UpdateCurrent will add a new TUF file
// if no previous version existed.
func TestSQLUpdateCurrentNew(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
// Adding a new TUF file should succeed
err = dbStore.UpdateCurrent("testGUN", SampleUpdate(0))
assert.NoError(t, err, "Creating a row in an empty DB failed.")
// There should just be one row
var rows []TUFFile
query := gormDB.Select("ID, Gun, Role, Version, Data").Find(&rows)
assert.NoError(t, query.Error)
expected := SampleTUF(0)
expected.ID = 1
assert.Equal(t, []TUFFile{expected}, rows)
}
// TestSQLUpdateCurrentNewVersion asserts that UpdateCurrent will add a
// new (higher) version of an existing TUF file
func TestSQLUpdateCurrentNewVersion(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
// insert row
oldVersion := SampleTUF(0)
query := gormDB.Create(&oldVersion)
assert.NoError(t, query.Error, "Creating a row in an empty DB failed.")
// UpdateCurrent with a newer version should succeed
update := SampleUpdate(2)
err = dbStore.UpdateCurrent("testGUN", update)
assert.NoError(t, err, "Creating a row in an empty DB failed.")
// There should just be one row
var rows []TUFFile
query = gormDB.Select("ID, Gun, Role, Version, Data").Find(&rows)
assert.NoError(t, query.Error)
oldVersion.Model = gorm.Model{ID: 1}
expected := SampleTUF(2)
expected.Model = gorm.Model{ID: 2}
assert.Equal(t, []TUFFile{oldVersion, expected}, rows)
}
// TestSQLUpdateCurrentOldVersionError asserts that an error is raised if
// trying to update to an older version of a TUF file.
func TestSQLUpdateCurrentOldVersionError(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
// insert row
newVersion := SampleTUF(3)
query := gormDB.Create(&newVersion)
assert.NoError(t, query.Error, "Creating a row in an empty DB failed.")
// UpdateCurrent should fail due to the version being lower than the
// previous row
err = dbStore.UpdateCurrent("testGUN", SampleUpdate(0))
assert.Error(t, err, "Error should not be nil")
assert.IsType(t, &ErrOldVersion{}, err,
"Expected ErrOldVersion error type, got: %v", err)
// There should just be one row
var rows []TUFFile
query = gormDB.Select("ID, Gun, Role, Version, Data").Find(&rows)
assert.NoError(t, query.Error)
newVersion.Model = gorm.Model{ID: 1}
assert.Equal(t, []TUFFile{newVersion}, rows)
dbStore.DB.Close()
}
// TestSQLUpdateMany asserts that inserting multiple updates succeeds if the
// updates do not conflict with each.
func TestSQLUpdateMany(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
err = dbStore.UpdateMany("testGUN", []MetaUpdate{
SampleUpdate(0),
{
Role: "targets",
Version: 1,
Data: []byte("2"),
},
SampleUpdate(2),
})
assert.NoError(t, err, "UpdateMany errored unexpectedly: %v", err)
gorm1 := SampleTUF(0)
gorm1.ID = 1
gorm2 := TUFFile{
Model: gorm.Model{ID: 2}, Gun: "testGUN", Role: "targets",
Version: 1, Data: []byte("2")}
gorm3 := SampleTUF(2)
gorm3.ID = 3
expected := []TUFFile{gorm1, gorm2, gorm3}
var rows []TUFFile
query := gormDB.Select("ID, Gun, Role, Version, Data").Find(&rows)
assert.NoError(t, query.Error)
assert.Equal(t, expected, rows)
dbStore.DB.Close()
}
// TestSQLUpdateManyVersionOrder asserts that inserting updates with
// non-monotonic versions still succeeds.
func TestSQLUpdateManyVersionOrder(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
err = dbStore.UpdateMany(
"testGUN", []MetaUpdate{SampleUpdate(2), SampleUpdate(0)})
assert.NoError(t, err)
// the whole transaction should have rolled back, so there should be
// no entries.
gorm1 := SampleTUF(2)
gorm1.ID = 1
gorm2 := SampleTUF(0)
gorm2.ID = 2
var rows []TUFFile
query := gormDB.Select("ID, Gun, Role, Version, Data").Find(&rows)
assert.NoError(t, query.Error)
assert.Equal(t, []TUFFile{gorm1, gorm2}, rows)
dbStore.DB.Close()
}
// TestSQLUpdateManyDuplicateRollback asserts that inserting duplicate
// updates fails.
func TestSQLUpdateManyDuplicateRollback(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
update := SampleUpdate(0)
err = dbStore.UpdateMany("testGUN", []MetaUpdate{update, update})
assert.Error(
t, err, "There should be an error updating the same data twice.")
assert.IsType(t, &ErrOldVersion{}, err,
"UpdateMany returned wrong error type")
// the whole transaction should have rolled back, so there should be
// no entries.
var count int
query := gormDB.Model(&TUFFile{}).Count(&count)
assert.NoError(t, query.Error)
assert.Equal(t, 0, count)
dbStore.DB.Close()
}
func TestSQLGetCurrent(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
byt, err := dbStore.GetCurrent("testGUN", "root")
assert.Nil(t, byt)
assert.Error(t, err, "There should be an error Getting an empty table")
assert.IsType(t, &ErrNotFound{}, err, "Should get a not found error")
tuf := SampleTUF(0)
query := gormDB.Create(&tuf)
assert.NoError(t, query.Error, "Creating a row in an empty DB failed.")
byt, err = dbStore.GetCurrent("testGUN", "root")
assert.NoError(t, err, "There should not be any errors getting.")
assert.Equal(t, []byte("1"), byt, "Returned data was incorrect")
dbStore.DB.Close()
}
func TestSQLDelete(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
tuf := SampleTUF(0)
query := gormDB.Create(&tuf)
assert.NoError(t, query.Error, "Creating a row in an empty DB failed.")
err = dbStore.Delete("testGUN")
assert.NoError(t, err, "There should not be any errors deleting.")
// verify deletion
var count int
query = gormDB.Model(&TUFFile{}).Count(&count)
assert.NoError(t, query.Error)
assert.Equal(t, 0, count)
dbStore.DB.Close()
}
func TestSQLGetTimestampKeyNoKey(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
cipher, public, err := dbStore.GetTimestampKey("testGUN")
assert.Equal(t, "", cipher)
assert.Nil(t, public)
assert.IsType(t, &ErrNoKey{}, err,
"Expected ErrNoKey from GetTimestampKey")
query := gormDB.Create(&TimestampKey{
Gun: "testGUN",
Cipher: "testCipher",
Public: []byte("1"),
})
assert.NoError(
t, query.Error, "Inserting timestamp into empty DB should succeed")
cipher, public, err = dbStore.GetTimestampKey("testGUN")
assert.Equal(t, "testCipher", cipher,
"Returned cipher was incorrect")
assert.Equal(t, []byte("1"), public, "Returned pubkey was incorrect")
}
func TestSQLSetTimestampKeyExists(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
gormDB, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
err = dbStore.SetTimestampKey("testGUN", "testCipher", []byte("1"))
assert.NoError(t, err, "Inserting timestamp into empty DB should succeed")
err = dbStore.SetTimestampKey("testGUN", "testCipher", []byte("1"))
assert.Error(t, err)
assert.IsType(t, &ErrTimestampKeyExists{}, err,
"Expected ErrTimestampKeyExists from SetTimestampKey")
var rows []TimestampKey
query := gormDB.Select("ID, Gun, Cipher, Public").Find(&rows)
assert.NoError(t, query.Error)
expected := TimestampKey{Gun: "testGUN", Cipher: "testCipher",
Public: []byte("1")}
expected.Model = gorm.Model{ID: 1}
assert.Equal(t, []TimestampKey{expected}, rows)
dbStore.DB.Close()
}
// TestDBCheckHealthTableMissing asserts that the health check fails if one or
// both the tables are missing.
func TestDBCheckHealthTableMissing(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
_, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
dbStore.DropTable(&TUFFile{})
dbStore.DropTable(&TimestampKey{})
// No tables, health check fails
err = dbStore.CheckHealth()
assert.Error(t, err, "Cannot access table:")
// only one table existing causes health check to fail
CreateTUFTable(dbStore.DB)
err = dbStore.CheckHealth()
assert.Error(t, err, "Cannot access table:")
dbStore.DropTable(&TUFFile{})
CreateTimestampTable(dbStore.DB)
err = dbStore.CheckHealth()
assert.Error(t, err, "Cannot access table:")
}
// TestDBCheckHealthDBCOnnection asserts that if the DB is not connectable, the
// health check fails.
func TestDBCheckHealthDBConnectionFail(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
_, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
err = dbStore.Close()
assert.NoError(t, err)
err = dbStore.CheckHealth()
assert.Error(t, err, "Cannot access table:")
}
// TestDBCheckHealthSuceeds asserts that if the DB is connectable and both
// tables exist, the health check succeeds.
func TestDBCheckHealthSucceeds(t *testing.T) {
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
_, dbStore := SetUpSQLite(t, tempBaseDir)
defer os.RemoveAll(tempBaseDir)
err = dbStore.CheckHealth()
assert.NoError(t, err)
}