Skip to content

Commit

Permalink
adding test suite & scenarios for Sqlite Encryption.
Browse files Browse the repository at this point in the history
  • Loading branch information
AnalogJ committed Nov 7, 2023
1 parent 85c34ba commit d0130d1
Showing 1 changed file with 264 additions and 0 deletions.
264 changes: 264 additions & 0 deletions backend/pkg/database/sqlite_repository_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
package database

import (
"fmt"
mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock"
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
"github.com/golang/mock/gomock"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"io/ioutil"
"log"
"os"
"testing"
)

// Define the suite, and absorb the built-in basic suite
// functionality from testify - including a T() method which
// returns the current testing context
type SqliteRepositoryTestSuite struct {
suite.Suite
MockCtrl *gomock.Controller
TestDatabase *os.File
}

// BeforeTest has a function to be executed right before the test starts and receives the suite and test names as input
func (suite *SqliteRepositoryTestSuite) BeforeTest(suiteName, testName string) {
suite.MockCtrl = gomock.NewController(suite.T())

dbFile, err := ioutil.TempFile("", fmt.Sprintf("%s.*.db", testName))
if err != nil {
log.Fatal(err)
}
suite.TestDatabase = dbFile

}

// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
func (suite *SqliteRepositoryTestSuite) AfterTest(suiteName, testName string) {
suite.MockCtrl.Finish()
os.Remove(suite.TestDatabase.Name())
os.Remove(suite.TestDatabase.Name() + "-shm")
os.Remove(suite.TestDatabase.Name() + "-wal")
}

// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestSqliteRepositoryTestSuite(t *testing.T) {
suite.Run(t, new(SqliteRepositoryTestSuite))

}

// Scenario 0: repository creation with default settings (no encryption)
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_DefaultPragmaSettings() {
//setup
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

repo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
sqliteRepo, sqliteRepoOk := repo.(*GormRepository)
require.True(suite.T(), sqliteRepoOk)

//test
var journalMode string
resp := sqliteRepo.GormClient.Raw("PRAGMA journal_mode;").Scan(&journalMode)
require.NoError(suite.T(), resp.Error)

var busyTimeout int
resp = sqliteRepo.GormClient.Raw("PRAGMA busy_timeout;").Scan(&busyTimeout)
require.NoError(suite.T(), resp.Error)

var foreignKeys bool
resp = sqliteRepo.GormClient.Raw("PRAGMA foreign_keys;").Scan(&foreignKeys)
require.NoError(suite.T(), resp.Error)

//assert
require.Equal(suite.T(), "wal", journalMode)
require.Equal(suite.T(), 5000, busyTimeout)
require.Equal(suite.T(), true, foreignKeys)
}

// Scenario 1: repository creation with encryption
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey() {
//setup
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

repo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
sqliteRepo, sqliteRepoOk := repo.(*GormRepository)
require.True(suite.T(), sqliteRepoOk)

//test
var cipher string
resp := sqliteRepo.GormClient.Raw("PRAGMA cipher;").Scan(&cipher)
require.NoError(suite.T(), resp.Error)

var legacy int
resp = sqliteRepo.GormClient.Raw("PRAGMA legacy;").Scan(&legacy)
require.NoError(suite.T(), resp.Error)

var hmacUse bool
resp = sqliteRepo.GormClient.Raw("PRAGMA hmac_use;").Scan(&hmacUse)
require.NoError(suite.T(), resp.Error)

var kdfIter int
resp = sqliteRepo.GormClient.Raw("PRAGMA kdf_iter;").Scan(&kdfIter)
require.NoError(suite.T(), resp.Error)

var legacyPageSize int
resp = sqliteRepo.GormClient.Raw("PRAGMA legacy_page_size;").Scan(&legacyPageSize)
require.NoError(suite.T(), resp.Error)

//assert
require.Equal(suite.T(), "sqlcipher", cipher)
require.Equal(suite.T(), 3, legacy)
require.Equal(suite.T(), 1024, legacyPageSize)
require.Equal(suite.T(), false, hmacUse)
require.Equal(suite.T(), 4000, kdfIter)
}

// Scenario 2: repository creation with encryption key, closing the app, and then reopening with a changed/incorrect key
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithIncorrectKeyShouldFail() {
//setup
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//intialize the database with a key
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)

fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig2.EXPECT().GetString("database.encryption.key").Return("incorrect_key_here").AnyTimes()
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//test
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())

//assert
require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error())
}

// Scenario 3: repository creation with encryption key, closing the app, and then reopening with the correct key
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithCorrectKeyShouldPass() {
//setup
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//intialize the database with a key
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)

fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig2.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//test
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())

//assert
require.NoError(suite.T(), err2)
}

// Scenario 4: repository creation with encryption key, closing the app, and then reopening with deaults (no encryption)
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithNoEncryptionKeyShouldFail() {
//setup
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//intialize the database with a key
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)

fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//test
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())

//assert
require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error())
}

// Scenario 5: repository creation without encryption key (defaults), closing the app, and then reopening with an encryption key
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithoutEncryption_WhenReopenWithEncryptionKeyShouldFail() {
//setup
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//intialize the database with a key
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)

fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
fakeConfig2.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//test
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())

//assert
require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error())
}

// Scenario 6: repository creation without encryption key (defaults), closing the app, and then reopening without an encryption key
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithoutEncryption_WhenReopenWithoutEncryptionKeyShouldPass() {
//setup
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//intialize the database with a key
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)

fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()

//test
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())

//assert
require.NoError(suite.T(), err2)
}

0 comments on commit d0130d1

Please # to comment.