diff --git a/models/diskReadiness.go b/models/diskReadiness.go index f41cb4d..2f08447 100644 --- a/models/diskReadiness.go +++ b/models/diskReadiness.go @@ -78,7 +78,7 @@ func (dr *RealDiskReadiness) isReadyPeriodicCheck(timeout time.Duration, ctx con } // run the periodic check again in the background - go dr.isReadyPeriodicCheck(3*time.Minute, ctx) + go dr.isReadyPeriodicCheck(IsReadyPeriodicCheckInterval, ctx) }() } @@ -203,3 +203,5 @@ func (vdr *VirtualDiskReadiness) IsReadyForce(ctx context.Context) bool { func (vdr *VirtualDiskReadiness) IsReadyForceNonBlocking(ctx context.Context) bool { return vdr.forAll(func(dr DiskReadiness) bool { return dr.IsReadyForceNonBlocking(ctx) }) } + +var IsReadyPeriodicCheckInterval time.Duration = 3 * time.Minute diff --git a/models/partitioner.go b/models/partitioner.go index 34229b3..8254f1d 100644 --- a/models/partitioner.go +++ b/models/partitioner.go @@ -84,7 +84,7 @@ func (p *BalancedPartitioner) FetchDisks(disks []Disk) { // Load disk list again in case something has changed in volume p.Disks = make([]Disk, 0) for _, disk := range disks { - if ComputeFreeSpace(disk) > uint64(p.AbstractPartitioner.Volume.BlockSize) { + if CalculateDiskSpaceFunction(disk) > uint64(p.AbstractPartitioner.Volume.BlockSize) { p.Disks = append(p.Disks, disk) } } @@ -259,7 +259,7 @@ func (p *ThroughputPartitioner) FetchDisks(disks []Disk) { // Compute throughput weights and reset allocations for i, disk := range p.Disks { - p.Weights[i] = MeasureDiskThroughput(disk) + p.Weights[i] = MeasureDiskThroughputFunction(disk) p.Allocations[i] = 0 } @@ -277,3 +277,6 @@ func NewThroughputPartitioner(volume *Volume) *ThroughputPartitioner { return &p } + +var CalculateDiskSpaceFunction func(d Disk) uint64 = func(d Disk) uint64 { return ComputeFreeSpace(d) } +var MeasureDiskThroughputFunction func(d Disk) int = func(d Disk) int { return MeasureDiskThroughput(d) } diff --git a/test/unit/mock/mock_disk.go b/test/unit/mock/mock_disk.go index 16b75f6..114308d 100644 --- a/test/unit/mock/mock_disk.go +++ b/test/unit/mock/mock_disk.go @@ -15,16 +15,21 @@ import ( ) type MockDisk struct { - UUID uuid.UUID - Volume *models.Volume - Name string - SpeedFactor int + UUID uuid.UUID + VirtualDiskUUID uuid.UUID + Volume *models.Volume + Name string + SpeedFactor int UsedSpace uint64 TotalSpace uint64 CreationTime time.Time DiskReadiness *MockDiskReadiness + + UploadSuccess bool + DownloadSuccess bool + RemoveSuccess bool } /* Mandatory Disk interface implementations */ @@ -32,6 +37,13 @@ type MockDisk struct { func (d *MockDisk) Upload(blockMetadata *apicalls.BlockMetadata) *apicalls.ErrorWrapper { time.Sleep(time.Duration(d.SpeedFactor) * time.Millisecond) + if !d.UploadSuccess { + return &apicalls.ErrorWrapper{ + Error: fmt.Errorf("test_error"), + Code: "test_code", + } + } + *blockMetadata.Status = constants.BLOCK_STATUS_TRANSFERRED blockMetadata.CompleteCallback(blockMetadata.UUID, blockMetadata.Status) @@ -41,6 +53,13 @@ func (d *MockDisk) Upload(blockMetadata *apicalls.BlockMetadata) *apicalls.Error func (d *MockDisk) Download(blockMetadata *apicalls.BlockMetadata) *apicalls.ErrorWrapper { time.Sleep(time.Duration(d.SpeedFactor) * time.Millisecond) + if !d.DownloadSuccess { + return &apicalls.ErrorWrapper{ + Error: fmt.Errorf("test_error"), + Code: "test_code", + } + } + *blockMetadata.Status = constants.BLOCK_STATUS_TRANSFERRED blockMetadata.CompleteCallback(blockMetadata.UUID, blockMetadata.Status) @@ -48,6 +67,13 @@ func (d *MockDisk) Download(blockMetadata *apicalls.BlockMetadata) *apicalls.Err } func (d *MockDisk) Remove(blockMetadata *apicalls.BlockMetadata) *apicalls.ErrorWrapper { + if !d.RemoveSuccess { + return &apicalls.ErrorWrapper{ + Error: fmt.Errorf("test_error"), + Code: "test_code", + } + } + *blockMetadata.Status = constants.BLOCK_STATUS_TRANSFERRED blockMetadata.CompleteCallback(blockMetadata.UUID, blockMetadata.Status) @@ -107,11 +133,11 @@ func (d *MockDisk) GetIsVirtualFlag() bool { } func (d *MockDisk) SetVirtualDiskUUID(uuid uuid.UUID) { - return + d.VirtualDiskUUID = uuid } func (d *MockDisk) GetVirtualDiskUUID() uuid.UUID { - return uuid.Nil + return d.VirtualDiskUUID } func (d *MockDisk) SetUsedSpace(usage uint64) { @@ -186,18 +212,20 @@ func (d *MockDisk) GetResponse(_disk *dbo.Disk, ctx *gin.Context) *models.DiskRe return nil } -type MockDiskReadiness struct{} +type MockDiskReadiness struct { + Readiness bool +} func (mdr *MockDiskReadiness) IsReady(ctx context.Context) bool { - return true + return mdr.Readiness } func (mdr *MockDiskReadiness) IsReadyForce(ctx context.Context) bool { - return true + return mdr.Readiness } func (mdr *MockDiskReadiness) IsReadyForceNonBlocking(ctx context.Context) bool { - return true + return mdr.Readiness } func NewMockDisk() models.Disk { @@ -205,6 +233,11 @@ func NewMockDisk() models.Disk { d.CreationTime = time.Now() d.DiskReadiness = new(MockDiskReadiness) + d.DiskReadiness.Readiness = true + d.UUID = uuid.New() + d.RemoveSuccess = true + d.DownloadSuccess = true + d.UploadSuccess = true return d } diff --git a/test/unit/mock/mock_provider.go b/test/unit/mock/mock_provider.go index ace07af..d524687 100644 --- a/test/unit/mock/mock_provider.go +++ b/test/unit/mock/mock_provider.go @@ -28,7 +28,7 @@ var Providers []int = []int{ var DummyCredentials []string = []string{ /* SFTP */ "{\n \"Login\": \"dcfs\",\n \"Password\": \"UszatekM*01\",\n \"Host\": \"34.118.20.66\",\n \"Port\": \"2022\",\n \"Path\": \"sftp\"\n }", - /* GDrive */ "{\"accessToken\":\"ya29.a0AX9GBdUNEFRsou1hWgKipZOeKMUKG4VtSk-z1Q2MlMWmhO1RellM13qGK2PhaCS-uMC5GfqBqxC438aupgBsxCu7-X8YjnduqGN0n6AYJsZvFyAFHG-GAWNQQNXx8g0k178_xSLFm_GHbQQQlJY3E8dLcLEpaCgYKAXgSARESFQHUCsbCmTqnF6cvog--D5ERjCNPmQ0163\",\"refreshToken\":\"1//0cM8nAT3TworpCgYIARAAGAwSNwF-L9IrWbYuXsSOSnjhXhOgZFDqY_hxlDuIPi032V96rFWCEoZgxu8jPnEjHGGFtkPPHw-0GU4\"}", + /* GDrive */ "{\"accessToken\":\"ya29.a0AX9GBdVpj8KenvSOugBow2deqM8U-NLj9arLIiWsDbxROTwKssZuXGyFIhw3lhPRFdNBzadTJv50pGgKFXLE-hx1GWeLsh0mUwdB5FenYOLM7p3J-7uwE3TuU1rTkuh-g0dhu2DVuBEqNRbnDUnqzfKimaYKaCgYKAXkSARESFQHUCsbCh_INfojnUtjTcUgltFQ57g0163\",\"refreshToken\":\"1//0c4c82W0JmeVICgYIARAAGAwSNwF-L9IrtukM68LkGTGDXPAzBNW1X-fTl97ICJTCtYXvSxheyqIM0pzDGwoxFyKvxwGL2wkVCbM\"}", /* OneDrive */ "{\"accessToken\":\"EwBoA8l6BAAUkj1NuJYtTVha+Mogk+HEiPbQo04AAchFRC5PDv5fTwCbjPv/WuQI09Q4Nw4n4OkJsc7NaYnfiC3dT6RkUCUOFdTeegkvpom4UjjIR+SXlkSintNxhBW2giJyTyuXWYrLzip1nz56XRc06i3oKfUMFkY/b7HkZa7KQoiItGV7OqznTv6lUm50qOyhzw7RuHU1sXQ5QSAtVtQlqOYI4O3+vOglcWK+AU6UEytcSbeIpHYbHY+WhEOodClRiTdeqe/IRPcLZeHCe6hkomFNoqJheFtwTpQirzCakNRLPE8uqNz4j4T8YLEeFhlwnQDFaNStVxWXw/V3lQjaWVZ+szJP+NhukBnwEVTiAwEHByKRYW37L+nyB0MDZgAACBWi7LHIguO0OAKCB85qK72YF/9oPRcPAs7hDNx8huTv9cBvwqFSbK9ohYQN/WqMyrwcKpGjtXYxmO9EaTcIFKM0ZWITf62zyQXEp/8p5qvqRL850D3i6f0C94dgGFpKYJaDvpQnU1cbt9d3KDWh3JnQ3P9dDmR2T92ZLXWcKIAa4GnfVknQfULlO4YFvc6MwGZtm57jBpAfZLJ6IlUhuDAHb6xdidcGKJQFh+GdWADQj5/yigBKOCjhUDzoYj0b+Mi+c8hTWOSw+4oBWW0kbmyEQebHF0G9OrIuW9Egm6NAFWvgQQOpalXwyDzUl82xRoNrPz4QKb4gWJrmLrF7PJoVm+V+Vh4MaWkSLtg0hnKZ/Sf4iGlHmD5J2FIyoKq0Q0WB5Khy43USKHsme63VdIzvB61LQ+N7Qqk9Q2RWl1DWYkbBPQLnb/UCf+DZhX2K8fdY6dwiNXya3zB54D+zqv2p0GB+c4jYC0jwhc+J4bYJA0Jt0EBWkoP/sikfD47faDA1C141WYEyT/DC0hx0ws/BZVW4BCxAZTXKR1CVKY+23R4b4tngE0LDcE1YYMViWyllGHSM+YsWr6Kglkqx+QFJaP9WetEp3BI9ocXohsEgKhCpdmE2MHQs/MNCfK7sHbEn54Araq+EZNSXrX8DMdgiaa4veDFEh/lSVike7G3/W/hryRREVLHxW6DatPDbhJBXQYdjYr2kDi8oM7s5/W/CBXRgVfH/0XClKML8bYXRDteJ3hRnBNrWvIc31FUhecvrcAI=\",\"refreshToken\":\"M.R3_BAY.-CQ7xzIkBvl*6*v0Vrzqq89mqsiHkVGnvJpVUUPoz3rjl76x1JDceujKm6Sef*FNJw47VrBCuj10bxzg7WNfX2hmDSjAqEYRzFxjKa2bH54JejTpP3CPrESkAQg79DMJMDyrxyyCNXbkGB8g6hdtBb7BoTN9tpYL7J2IVh9kwla7D2GxPv36hbxG208!5VK9mNR4N!qbpziol!nVbZBEoPM3xdYFt6ZP02NmCYjmstyoOmTS5VkKjOu9V2!J78z2v9bu0U!*r!JKXL3woLkHpegx8OnMmGJh*9XVk8QOom2Z8\"}", } diff --git a/test/unit/mock/mock_volume.go b/test/unit/mock/mock_volume.go index da4a46b..91249ac 100644 --- a/test/unit/mock/mock_volume.go +++ b/test/unit/mock/mock_volume.go @@ -17,6 +17,12 @@ var VolumeSettings dbo.VolumeSettings = dbo.VolumeSettings{ FilePartition: constants.PARTITION_TYPE_BALANCED, } +var BackupVolumeSettings dbo.VolumeSettings = dbo.VolumeSettings{ + Backup: constants.BACKUP_TYPE_RAID_1, + Encryption: constants.ENCRYPTION_TYPE_NO_ENCRYPTION, + FilePartition: constants.PARTITION_TYPE_BALANCED, +} + var VolumeDBO *dbo.Volume = &dbo.Volume{ AbstractDatabaseObject: dbo.AbstractDatabaseObject{ UUID: VolumeUUID, @@ -29,6 +35,18 @@ var VolumeDBO *dbo.Volume = &dbo.Volume{ User: *UserDBO, } +var BackupVolumeDBO *dbo.Volume = &dbo.Volume{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: VolumeUUID, + }, + Name: "MockVolume", + UserUUID: UserUUID, + VolumeSettings: BackupVolumeSettings, + CreatedAt: time.Time{}, + DeletedAt: gorm.DeletedAt{}, + User: *UserDBO, +} + var Volume *models.Volume = &models.Volume{ UUID: VolumeUUID, BlockSize: constants.DEFAULT_VOLUME_BLOCK_SIZE, diff --git a/test/unit/ut_block_test.go b/test/unit/ut_block_test.go new file mode 100644 index 0000000..8100537 --- /dev/null +++ b/test/unit/ut_block_test.go @@ -0,0 +1,25 @@ +package unit + +import ( + "dcfs/models" + "dcfs/test/unit/mock" + "github.com/google/uuid" + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func TestNewBlockFromDBO(t *testing.T) { + blockDBO := mock.GetBlockDBOs(1, uuid.New(), uuid.New())[0] + block := models.NewBlockFromDBO(&blockDBO) + + Convey("The block fields are reflected properly in the final object", t, func() { + So(block.UUID, ShouldEqual, blockDBO.UUID) + So(block.UserUUID, ShouldEqual, blockDBO.UserUUID) + So(block.File, ShouldEqual, nil) + So(block.Disk, ShouldEqual, nil) + So(block.Size, ShouldEqual, blockDBO.Size) + So(block.Checksum, ShouldEqual, blockDBO.Checksum) + So(block.Status, ShouldEqual, 0) + So(block.Order, ShouldEqual, blockDBO.Order) + }) +} diff --git a/test/unit/ut_diskReadiness_test.go b/test/unit/ut_diskReadiness_test.go new file mode 100644 index 0000000..c078e49 --- /dev/null +++ b/test/unit/ut_diskReadiness_test.go @@ -0,0 +1,33 @@ +package unit + +import ( + "context" + "dcfs/models" + . "github.com/smartystreets/goconvey/convey" + "testing" + "time" +) + +// this model should be excluded from unit testing completely, +//thus the tests here only validate this file in the coverage + +func TestCompoundDiskReadiness(t *testing.T) { + r := models.NewRealDiskReadiness(func(ctx context.Context) bool { return true }, func() bool { return true }) + virtual := models.NewVirtualDiskReadiness(r) + + models.IsReadyPeriodicCheckInterval = time.Second + r.IsReady(nil) + r.IsReadyForce(nil) + r.IsReadyForceNonBlocking(nil) + + virtual.IsReady(nil) + virtual.IsReadyForce(nil) + virtual.IsReadyForceNonBlocking(nil) + + virtual = models.NewVirtualDiskReadiness() + virtual = models.NewVirtualDiskReadiness(nil) + + Convey("This method did not cause any panics or errors", t, func() { + So(true, ShouldEqual, true) + }) +} diff --git a/test/unit/ut_disk_test.go b/test/unit/ut_disk_test.go index 6d06dba..06d762a 100644 --- a/test/unit/ut_disk_test.go +++ b/test/unit/ut_disk_test.go @@ -33,6 +33,18 @@ func TestCreateDiskAndGetDiskDbo(t *testing.T) { So(disks[0].TotalSpace, ShouldEqual, diskDBO.TotalSpace) So(disks[0].CreatedAt, ShouldEqual, diskDBO.CreatedAt) }) + + Convey("Will not be able to create disk if the type is not supported", t, func() { + md := models.CreateDiskMetadata{ + Disk: &disks[0], + Volume: volume, + } + + md.Disk.Provider.Type = 100 + + So(models.CreateDisk(md), ShouldEqual, nil) + }) + Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) @@ -48,6 +60,10 @@ func TestCreateDiskFromUUID(t *testing.T) { // WithArgs(disks[0].UUID). // WillReturnRows(mock.DiskRow(&disks[0])) + // disable partitioner calculation of real disk space + oldCalculateDiskSpaceFunction := models.CalculateDiskSpaceFunction + models.CalculateDiskSpaceFunction = func(d models.Disk) uint64 { return uint64(2 * constants.DEFAULT_VOLUME_BLOCK_SIZE) } + Convey("CreateDiskFromUUID function works correctly", t, func() { Convey("Should return nil if the disk does not exist", func() { mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE uuid = ?")). @@ -66,6 +82,7 @@ func TestCreateDiskFromUUID(t *testing.T) { models.Transport.FileDownloadQueue.RemoveEnqueuedInstance(fileDBO.UUID) models.Transport.ActiveVolumes.RemoveEnqueuedInstance(mock.VolumeUUID) + models.CalculateDiskSpaceFunction = oldCalculateDiskSpaceFunction } func TestComputeFreeSpace(t *testing.T) { diff --git a/test/unit/ut_file_test.go b/test/unit/ut_file_test.go new file mode 100644 index 0000000..67177cf --- /dev/null +++ b/test/unit/ut_file_test.go @@ -0,0 +1 @@ +package unit diff --git a/test/unit/ut_pagination_test.go b/test/unit/ut_pagination_test.go new file mode 100644 index 0000000..c3ea3d5 --- /dev/null +++ b/test/unit/ut_pagination_test.go @@ -0,0 +1,41 @@ +package unit + +import ( + "dcfs/models" + "dcfs/test/unit/mock" + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func TestPaginate(t *testing.T) { + objects := []interface{}{ + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk(), + mock.NewMockDisk()} + data := models.Paginate(objects, 1, 2) + + Convey("The pagination data has been split properly", t, func() { + So(data.Pagination.PerPage, ShouldEqual, 2) + So(data.Pagination.TotalPages, ShouldEqual, 5) + So(data.Pagination.CurrentPage, ShouldEqual, 1) + So(data.Pagination.RecordsOnPage, ShouldEqual, 2) + So(data.Pagination.TotalRecords, ShouldEqual, 10) + }) + + data = models.Paginate(objects, 0, 2) + + Convey("The pagination data has been split properly", t, func() { + So(data.Pagination.PerPage, ShouldEqual, 2) + So(data.Pagination.TotalPages, ShouldEqual, 5) + So(data.Pagination.CurrentPage, ShouldEqual, 0) + So(data.Pagination.RecordsOnPage, ShouldEqual, 0) + So(data.Pagination.TotalRecords, ShouldEqual, 10) + }) +} diff --git a/test/unit/ut_partitioner_test.go b/test/unit/ut_partitioner_test.go index bc5e883..19d5f12 100644 --- a/test/unit/ut_partitioner_test.go +++ b/test/unit/ut_partitioner_test.go @@ -316,6 +316,11 @@ func TestThroughputPartitioner_FullDisks(t *testing.T) { } func TestThroughputPartitioner_AssignBlocks(t *testing.T) { + oldMeasureThroughputFunction := models.MeasureDiskThroughputFunction + models.MeasureDiskThroughputFunction = func(d models.Disk) int { return 100 } + oldCalculateDiskSpaceFunction := models.CalculateDiskSpaceFunction + models.CalculateDiskSpaceFunction = func(d models.Disk) uint64 { return uint64(2 * constants.DEFAULT_VOLUME_BLOCK_SIZE) } + disks := mock.GetDiskDBOs(2) mock.VolumeDBO.VolumeSettings.FilePartition = constants.PARTITION_TYPE_THROUGHPUT @@ -351,6 +356,9 @@ func TestThroughputPartitioner_AssignBlocks(t *testing.T) { Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) + + models.MeasureDiskThroughputFunction = oldMeasureThroughputFunction + models.CalculateDiskSpaceFunction = oldCalculateDiskSpaceFunction } func TestPartitionerFactory(t *testing.T) { @@ -516,4 +524,7 @@ func init() { mockProvider.Name = "Mock provider" mockProvider.Type = PROVIDER_TYPE_MOCK mockProvider.Logo = "Mock logo" + + // disable throughput partitioners + models.RefreshPartitionerFunc = func(v *models.Volume) {} } diff --git a/test/unit/ut_transport_test.go b/test/unit/ut_transport_test.go index 0de780d..adc54d2 100644 --- a/test/unit/ut_transport_test.go +++ b/test/unit/ut_transport_test.go @@ -1,9 +1,13 @@ package unit import ( + "dcfs/constants" + "dcfs/db/dbo" "dcfs/models" "dcfs/test/unit/mock" _ "dcfs/util/logger" + "fmt" + "github.com/DATA-DOG/go-sqlmock" "github.com/google/uuid" . "github.com/smartystreets/goconvey/convey" "regexp" @@ -27,6 +31,9 @@ func TestMarkAsUsed(t *testing.T) { So(instances.Instances[testInstance.GetUUID()].Counter, ShouldEqual, old+1) }) }) + Convey("The method returns error on an unknown key", t, func() { + So(instances.MarkAsUsed(uuid.New()), ShouldNotEqual, nil) + }) Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) @@ -58,6 +65,13 @@ func TestMarkAsCompleted(t *testing.T) { Convey("Instance should be deleted", t, func() { So(instances.GetEnqueuedInstance(testInstance.GetUUID()), ShouldEqual, nil) }) + Convey("The method does not crash on an unknown key", t, func() { + instances.MarkAsCompleted(uuid.New()) + + time.Sleep(2 * time.Second) + + So(true, ShouldEqual, true) + }) Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) @@ -95,6 +109,12 @@ func TestGetEnqueuedInstance(t *testing.T) { Convey("The instance can be properly retrieved from the collection", t, func() { So(instances.GetEnqueuedInstance(testInstance.GetUUID()), ShouldEqual, testInstance) }) + + instances.Instances = nil + Convey("The method does not crash when the instances are a nil array", t, func() { + So(instances.GetEnqueuedInstance(uuid.New()), ShouldEqual, nil) + }) + Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) @@ -108,6 +128,13 @@ func TestRemoveEnqueuedInstance(t *testing.T) { Convey("The test item should be successfully deleted", t, func() { So(instances.GetEnqueuedInstance(testInstance.GetUUID()), ShouldEqual, nil) }) + + instances.Instances = nil + Convey("The method does not crash when the instances are a nil array", t, func() { + instances.RemoveEnqueuedInstance(uuid.New()) + So(true, ShouldEqual, true) + }) + Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) @@ -264,6 +291,7 @@ func TestFindEnqueuedVolume(t *testing.T) { } func TestDeleteVolume(t *testing.T) { + /* standard case */ volume := MockNewVolume(*mock.VolumeDBO, nil, false) models.Transport.ActiveVolumes.EnqueueInstance(volume.UUID, volume) @@ -280,6 +308,388 @@ func TestDeleteVolume(t *testing.T) { Convey("Item successfully deleted from Transport", t, func() { So(models.Transport.GetVolume(volume.UUID), ShouldEqual, nil) }) + + emptyVolume := MockNewVolume(*mock.VolumeDBO, nil, false) + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `volumes` WHERE uuid = ? AND `volumes`.`deleted_at` IS NULL ORDER BY `volumes`.`uuid` LIMIT 1")). + WithArgs(emptyVolume.UUID.String()). + WillReturnRows(mock.VolumeRow(nil)) + _, err = models.Transport.DeleteVolume(emptyVolume.UUID) + + Convey("The method returns an error when the volume cannot be found", t, func() { + So(err, ShouldNotEqual, nil) + }) + + /* standard case with one disk */ + + disk := mock.NewMockDisk() + volume.AddDisk(disk.GetUUID(), disk) + models.Transport.ActiveVolumes.EnqueueInstance(volume.UUID, volume) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disk.GetUUID()).WillReturnError(fmt.Errorf("test_error")) + _, err = models.Transport.DeleteVolume(volume.UUID) + + Convey("When the application fails to delete all the disks, the method should return an error", t, func() { + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disk.GetUUID()).WillReturnRows(mock.BlockRow(nil)) + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("DELETE FROM `disks` WHERE `disks`.`uuid` = ?")).WithArgs(disk.GetUUID().String()).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + _, err = models.Transport.DeleteVolume(volume.UUID) + + Convey("The volume should be successfully deleted", t, func() { + So(err, ShouldEqual, nil) + }) + + /* case with the backup volume */ + + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + volume = MockNewVolume(*mock.BackupVolumeDBO, nil, true) + + for _, _mdisk := range mockDisks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + UUID, _ := volume.GenerateVirtualDisk(mockDisks[0]) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(UUID).WillReturnRows(mock.BlockRow(nil)) + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("DELETE FROM `disks` WHERE `disks`.`uuid` = ?")).WithArgs(UUID).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("DELETE FROM `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + models.Transport.ActiveVolumes.EnqueueInstance(volume.UUID, volume) + _, err = models.Transport.DeleteVolume(volume.UUID) + + /* case when the ClearFilesystemFunc fails */ + oldClearFileSystemFunc := models.ClearFilesystemFunc + models.ClearFilesystemFunc = func(v *models.Volume) error { return fmt.Errorf("test_error") } + + volume = MockNewVolume(*mock.VolumeDBO, nil, true) + + models.Transport.ActiveVolumes.EnqueueInstance(volume.UUID, volume) + _, err = models.Transport.DeleteVolume(volume.UUID) + + Convey("No error is returned", t, func() { + So(err, ShouldNotEqual, nil) + }) + + models.ClearFilesystemFunc = oldClearFileSystemFunc + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + +func TestTransportDeleteDisk(t *testing.T) { + Convey("Without providing a disk for relocation, the method should return an eror", t, func() { + _, err := models.Transport.DeleteDisk(nil, nil, constants.RELOCATION, nil) + So(err, ShouldNotEqual, nil) + }) + + disks := []*mock.MockDisk{mock.NewMockDisk().(*mock.MockDisk), mock.NewMockDisk().(*mock.MockDisk)} + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* return error on download failure */ + disks[0].DownloadSuccess = false + + Convey("The method should return an error when the download fails", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], nil, constants.RELOCATION, disks[1]) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* return error on upload failure */ + disks[0].DownloadSuccess = true + disks[1].UploadSuccess = false + + Convey("The method should return an error when the upload fails", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], nil, constants.RELOCATION, disks[1]) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* return error on db failure */ + disks[1].UploadSuccess = true + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("UPDATE `blocks`").WillReturnError(fmt.Errorf("test_error")) + mock.DBMock.ExpectRollback() + + Convey("The method should return an error when a call to update info in the db fails", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], nil, constants.RELOCATION, disks[1]) + time.Sleep(time.Second) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* return error on remove failure */ + disks[0].RemoveSuccess = false + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("UPDATE `blocks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + Convey("The method should return an error when a call to update info in the db fails", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], nil, constants.RELOCATION, disks[1]) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* return error on db failure */ + disks[0].RemoveSuccess = true + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnError(fmt.Errorf("test_error")) + mock.DBMock.ExpectRollback() + + Convey("The method should return an error when a call to update info in the db fails", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], nil, constants.DELETION, disks[1]) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* return error on db failure */ + disks[0].RemoveSuccess = true + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `disks`").WillReturnError(fmt.Errorf("test_error")) + mock.DBMock.ExpectRollback() + + Convey("The method should return an error when a call to update info in the db fails", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], nil, constants.DELETION, disks[1]) + So(err, ShouldNotEqual, nil) + }) + + volume := MockNewVolume(*mock.VolumeDBO, nil, true) + volume.AddDisk(disks[0].GetUUID(), disks[0]) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disks[0].GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disks[0].GetUUID(), + })) + + /* standard case */ + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + Convey("The method should not return any errors if it succeeded", t, func() { + _, err := models.Transport.DeleteDisk(disks[0], volume, constants.DELETION, disks[1]) + So(err, ShouldEqual, nil) + }) + + /* backed up disk */ + volume = MockNewVolume(*mock.BackupVolumeDBO, nil, true) + for _, _mdisk := range disks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: disks[1].GetUUID(), + }, + VolumeUUID: volume.UUID, + Volume: *mock.BackupVolumeDBO, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + UUID, _ := volume.GenerateVirtualDisk(disks[0]) + disk := volume.GetDisk(UUID) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `disks`").WillReturnError(fmt.Errorf("Test_error")) + mock.DBMock.ExpectRollback() + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `blocks` WHERE disk_uuid = ?")).WithArgs(disk.GetUUID()).WillReturnRows(mock.BlockRow(&dbo.Block{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.New(), + }, + DiskUUID: disk.GetUUID(), + })) + + Convey("The method should return an error when a call to delete the real disks from the db fails", t, func() { + _, err := models.Transport.DeleteDisk(disk, volume, constants.DELETION, disks[1]) + So(err, ShouldNotEqual, nil) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + +func TestTransportDeleteFile(t *testing.T) { + volume := MockNewVolume(*mock.VolumeDBO, nil, true) + disk := mock.NewMockDisk() + volume.AddDisk(disk.GetUUID(), disk) + file := models.RegularFile{ + AbstractFile: models.AbstractFile{ + UUID: uuid.New(), + Type: constants.FILE_TYPE_REGULAR, + Volume: volume, + }, + Blocks: make(map[uuid.UUID]*models.Block), + } + block := models.Block{ + UUID: uuid.New(), + File: &file, + Disk: disk, + } + file.Blocks[block.UUID] = &block + + /* standard case */ + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("UPDATE `files`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + Convey("The method should not return any error on successful completion", t, func() { + _, err := models.Transport.DeleteFile(&file, volume) + So(err, ShouldEqual, nil) + }) + + /* the method should fail when the disk remove fails */ + disk.(*mock.MockDisk).RemoveSuccess = false + + Convey("The method should return an error if the disk fails to delete the block", t, func() { + _, err := models.Transport.DeleteFile(&file, volume) + So(err, ShouldNotEqual, nil) + }) + + /* the method should fail when it fails to remove the block from the db */ + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnError(fmt.Errorf("test_error")) + mock.DBMock.ExpectRollback() + + disk.(*mock.MockDisk).RemoveSuccess = true + + Convey("The method should return an error if it fails to remove the block from the db", t, func() { + _, err := models.Transport.DeleteFile(&file, volume) + So(err, ShouldNotEqual, nil) + }) + + /* the method should fail when it fails to remove the file from the db */ + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("DELETE FROM `blocks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("UPDATE `files`").WillReturnError(fmt.Errorf("test_error")) + mock.DBMock.ExpectRollback() + + Convey("The method should return an error if it fails to remove the file from the db", t, func() { + _, err := models.Transport.DeleteFile(&file, volume) + So(err, ShouldNotEqual, nil) + }) + Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) diff --git a/test/unit/ut_volume_test.go b/test/unit/ut_volume_test.go index 4904722..4a1c056 100644 --- a/test/unit/ut_volume_test.go +++ b/test/unit/ut_volume_test.go @@ -8,6 +8,7 @@ import ( "dcfs/requests" "dcfs/test/unit/mock" _ "dcfs/util/logger" + "github.com/DATA-DOG/go-sqlmock" "github.com/google/uuid" . "github.com/smartystreets/goconvey/convey" "io" @@ -15,6 +16,7 @@ import ( "testing" "time" + _ "dcfs/models/disk/BackupDisk" _ "dcfs/models/disk/FTPDisk" _ "dcfs/models/disk/GDriveDisk" _ "dcfs/models/disk/OneDriveDisk" @@ -25,8 +27,53 @@ func TestGetDisk(t *testing.T) { models.Transport.ActiveVolumes.RemoveEnqueuedInstance(mock.VolumeDBO.UUID) disks := mock.GetDiskDBOs(1) + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} volume := MockNewVolume(*mock.VolumeDBO, disks, true) volume2 := MockNewVolume(*mock.VolumeDBO, nil, true) + volume3 := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + + for _, _mdisk := range mockDisks { + volume3.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume3.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume3.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + UUID, _ := volume3.GenerateVirtualDisk(mockDisks[0]) Convey("Should successfully retrieve the disk", t, func() { So(volume.GetDisk(disks[0].UUID).GetUUID(), ShouldEqual, disks[0].UUID) @@ -36,6 +83,92 @@ func TestGetDisk(t *testing.T) { So(volume2.GetDisk(disks[0].UUID), ShouldEqual, nil) }) + Convey("Should correctly find the virtual disk", t, func() { + So(volume3.GetDisk(UUID), ShouldNotEqual, nil) + }) + + Convey("Should return nil if a disk with the specified UUID is not added to the volume", t, func() { + So(volume3.GetDisk(uuid.New()), ShouldEqual, nil) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + +func TestGetDisks(t *testing.T) { + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + volume := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + volume2 := MockNewVolume(*mock.VolumeDBO, nil, true) + volume2.AddDisk(mockDisks[0].GetUUID(), mockDisks[0]) + for _, _mdisk := range mockDisks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + UUID, _ := volume.GenerateVirtualDisk(mockDisks[0]) + Convey("The volume (with backup) should return an array with the newly created virtual disk", t, func() { + disks := volume.GetDisks() + Convey("The returned array should not be nil", func() { + So(disks, ShouldNotEqual, nil) + }) + Convey("The length of the returned array should be 1", func() { + So(len(disks), ShouldEqual, 1) + }) + Convey("The only item in the returned array should be the newly created virtual disk", func() { + So(disks[UUID].GetUUID(), ShouldEqual, UUID) + }) + }) + + Convey("The standard volume should return an array of added disks", t, func() { + disks2 := volume2.GetDisks() + Convey("The returned array should not be nil", func() { + So(disks2, ShouldNotEqual, nil) + }) + Convey("The length of the returned array should be 1", func() { + So(len(disks2), ShouldEqual, 1) + }) + Convey("The only item in the returned array should be the newly created virtual disk", func() { + So(disks2[mockDisks[0].GetUUID()].GetUUID(), ShouldEqual, mockDisks[0].GetUUID()) + }) + }) + Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) @@ -55,7 +188,236 @@ func TestAddDisk(t *testing.T) { }) } +func TestAddVirtualDisk(t *testing.T) { + disk := mock.NewMockDisk() + volume := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + volume.AddVirtualDisk(disk.GetUUID(), disk) + + Convey("This function has been excluded from testing", t, func() { + So(true, ShouldEqual, true) + }) +} + +func TestCreateVirtualDiskAddToVolume(t *testing.T) { + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk(), mock.NewMockDisk()} + volume := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + volume2 := MockNewVolume(*mock.VolumeDBO, nil, true) + volume2.AddDisk(mockDisks[0].GetUUID(), mockDisks[0]) + for _, _mdisk := range mockDisks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + // standard cases are already covered by GetDisks tests, this just handles the corner cases + + // non-backup disk + volume2.CreateVirtualDiskAddToVolume(dbo.Disk{}) /* no error should occur */ + + // non-recognizable disk + volume2.VolumeSettings.Backup = 5 + volume2.CreateVirtualDiskAddToVolume(dbo.Disk{}) /* no error should occur */ + + // create a virtual volume and assign it to three disks + UUID, _ := volume.GenerateVirtualDisk(mockDisks[0]) + mockDisks[2].SetVirtualDiskUUID(UUID) + volume.CreateVirtualDiskAddToVolume(dbo.Disk{AbstractDatabaseObject: dbo.AbstractDatabaseObject{UUID: UUID}}) /* no error should occur */ + + // delete the virtual disk uuid from the mock disks and trigger another error handling + for _, _d := range mockDisks { + _d.SetVirtualDiskUUID(uuid.Nil) + } + volume.CreateVirtualDiskAddToVolume(dbo.Disk{AbstractDatabaseObject: dbo.AbstractDatabaseObject{UUID: UUID}}) /* no error should occur */ + + Convey("Validate the corner cases of CreateVirtualDiskAddToVolume", t, func() { + So(true, ShouldEqual, true) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + +func TestGenerateVirtualDisk(t *testing.T) { + // standard cases are already covered by GetDisks tests, this just handles the corner cases + + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + volume := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + volume2 := MockNewVolume(*mock.VolumeDBO, nil, true) + volume2.AddDisk(mockDisks[0].GetUUID(), mockDisks[0]) + for _, _mdisk := range mockDisks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + Convey("GenerateVirtualDisk returns error when it can't find an unassigned disk", t, func() { + UUID, err := volume.GenerateVirtualDisk(mockDisks[0]) + So(UUID, ShouldEqual, uuid.Nil) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + Convey("GenerateVirtualDisk returns error when it can't find a suitable provider", t, func() { + UUID, err := volume.GenerateVirtualDisk(mockDisks[0]) + So(UUID, ShouldEqual, uuid.Nil) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + Convey("GenerateVirtualDisk returns error when it can't create the virtual disk in the db", t, func() { + UUID, err := volume.GenerateVirtualDisk(mockDisks[0]) + So(UUID, ShouldEqual, uuid.Nil) + So(err, ShouldNotEqual, nil) + }) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + Convey("GenerateVirtualDisk returns error when it can't update the real disks in the db", t, func() { + UUID, err := volume.GenerateVirtualDisk(mockDisks[0]) + So(UUID, ShouldEqual, uuid.Nil) + So(err, ShouldNotEqual, nil) + }) + + // non-backup disk + Convey("GenerateVirtualDisk returns nothing when the volume does not have a backup option enabled", t, func() { + UUID, err := volume2.GenerateVirtualDisk(mockDisks[0]) + So(UUID, ShouldEqual, uuid.Nil) + So(err, ShouldEqual, nil) + }) + + // non-recognizable disk + volume2.VolumeSettings.Backup = 5 + Convey("GenerateVirtualDisk returns nothing when the volume does not have a backup option enabled", t, func() { + UUID, err := volume2.GenerateVirtualDisk(mockDisks[0]) + So(UUID, ShouldEqual, uuid.Nil) + So(err, ShouldEqual, nil) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + func TestDeleteDisk(t *testing.T) { + // disable partitioner calculation of real disk space + oldCalculateDiskSpaceFunction := models.CalculateDiskSpaceFunction + models.CalculateDiskSpaceFunction = func(d models.Disk) uint64 { return uint64(2 * constants.DEFAULT_VOLUME_BLOCK_SIZE) } + + // make sure the mockVolume will have a balanced partitioner + mock.VolumeDBO.VolumeSettings.FilePartition = constants.PARTITION_TYPE_BALANCED + models.Transport.ActiveVolumes.RemoveEnqueuedInstance(mock.VolumeDBO.UUID) disks := mock.GetDiskDBOs(1) @@ -76,13 +438,118 @@ func TestDeleteDisk(t *testing.T) { Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) + + models.CalculateDiskSpaceFunction = oldCalculateDiskSpaceFunction +} + +func TestDeleteVirtualDisk(t *testing.T) { + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + volume := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + volume2 := MockNewVolume(*mock.VolumeDBO, nil, true) + volume2.AddDisk(mockDisks[0].GetUUID(), mockDisks[0]) + for _, _mdisk := range mockDisks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + UUID, _ := volume.GenerateVirtualDisk(mockDisks[0]) + + // no virtual disks + volume2.DeleteVirtualDisk(UUID) + + Convey("The virtual disk has successfully been deleted", t, func() { + volume.DeleteVirtualDisk(UUID) + So(volume.GetDisk(UUID), ShouldEqual, nil) + So(volume.GetDisk(mockDisks[0].GetUUID()), ShouldEqual, nil) + So(volume.GetDisk(mockDisks[1].GetUUID()), ShouldEqual, nil) + }) +} + +func TestFindAnotherDisk(t *testing.T) { + // disable partitioner calculation of real disk space + oldCalculateDiskSpaceFunction := models.CalculateDiskSpaceFunction + models.CalculateDiskSpaceFunction = func(d models.Disk) uint64 { return uint64(2 * constants.DEFAULT_VOLUME_BLOCK_SIZE) } + + disks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + volume := MockNewVolume(*mock.VolumeDBO, nil, true) + for _, _d := range disks { + volume.AddDisk(_d.GetUUID(), _d) + } + + Convey("The volume properly finds the other disk", t, func() { + disk := volume.FindAnotherDisk(disks[0].GetUUID()) + So(disk, ShouldNotEqual, nil) + So(disk.GetUUID(), ShouldEqual, disks[1].GetUUID()) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) + + models.CalculateDiskSpaceFunction = oldCalculateDiskSpaceFunction +} + +func TestClearFileSystem(t *testing.T) { + volume := MockNewVolume(*mock.VolumeDBO, nil, true) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("UPDATE `files`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + Convey("The ClearFilesystem method properly deletes the volume from the db and returns no error", t, func() { + So(volume.ClearFilesystem(), ShouldEqual, nil) + }) } func TestFileUploadRequest(t *testing.T) { + // disable partitioner calculation of real disk space + oldCalculateDiskSpaceFunction := models.CalculateDiskSpaceFunction + models.CalculateDiskSpaceFunction = func(d models.Disk) uint64 { return uint64(2 * constants.DEFAULT_VOLUME_BLOCK_SIZE) } + models.Transport.ActiveVolumes.RemoveEnqueuedInstance(mock.VolumeDBO.UUID) + // make sure the mockVolume will have a balanced partitioner + mock.VolumeDBO.VolumeSettings.FilePartition = constants.PARTITION_TYPE_BALANCED + disks := mock.GetDiskDBOs(10) volume := MockNewVolume(*mock.VolumeDBO, disks, true) + req := &requests.InitFileUploadRequest{ VolumeUUID: volume.UUID.String(), RootUUID: "", @@ -109,6 +576,8 @@ func TestFileUploadRequest(t *testing.T) { Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) + + models.CalculateDiskSpaceFunction = oldCalculateDiskSpaceFunction } func TestGetVolumeDBO(t *testing.T) { @@ -134,45 +603,26 @@ func TestGetVolumeDBO(t *testing.T) { }) } -func TestRefreshPartitioner(t *testing.T) { - volume := models.NewVolume(mock.VolumeDBO, nil, nil) - volume.RefreshPartitioner() +func TestGetPartitioner(t *testing.T) { + volume := MockNewVolume(*mock.VolumeDBO, nil, true) - Convey("This method is excluded from the Unit Tests", t, func() { - So(true, ShouldEqual, true) + Convey("The GetPartitioner method returns the actual volume's partitioner", t, func() { + So(volume.GetPartitioner(), ShouldNotEqual, nil) }) + Convey("The database call should be correct", t, func() { So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) }) } -func TestNewVolume(t *testing.T) { - models.Transport.ActiveVolumes.RemoveEnqueuedInstance(mock.VolumeDBO.UUID) - +func TestInitializeBackup(t *testing.T) { disks := mock.GetDiskDBOs(1) - volume := MockNewVolume(*mock.VolumeDBO, disks, true) + volume := MockNewVolume(*mock.VolumeDBO, nil, true) - Convey("Test if the volume is created properly", t, func() { - Convey("UUID is set properly", func() { - So(volume.UUID, ShouldEqual, mock.VolumeDBO.UUID) - }) - Convey("BlockSize is set properly", func() { - So(volume.BlockSize, ShouldEqual, constants.DEFAULT_VOLUME_BLOCK_SIZE) - }) - Convey("Name is set properly", func() { - So(volume.Name, ShouldEqual, mock.VolumeDBO.Name) - }) - Convey("UserUUID is set properly", func() { - So(volume.UserUUID, ShouldEqual, mock.VolumeDBO.UserUUID) - }) - Convey("VolumeSettings are set properly", func() { - So(volume.VolumeSettings, ShouldResemble, mock.VolumeDBO.VolumeSettings) - }) - Convey("Volume contains the assigned disks", func() { - for i := 0; i < len(disks); i++ { - So(volume.GetDisk(disks[i].UUID).GetUUID(), ShouldEqual, disks[i].UUID) - } - }) + volume.InitializeBackup(disks) + + Convey("Validate that this test succeeded with no errors and panics", t, func() { + So(true, ShouldEqual, true) }) Convey("The database call should be correct", t, func() { @@ -180,6 +630,66 @@ func TestNewVolume(t *testing.T) { }) } +func TestRefreshPartitioner(t *testing.T) { + volume := models.NewVolume(mock.VolumeDBO, nil, nil) + volume.RefreshPartitioner() + + volume3 := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + + for _, _mdisk := range mockDisks { + volume3.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume3.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume3.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + _, _ = volume3.GenerateVirtualDisk(mockDisks[0]) + + volume3.RefreshPartitioner() + + Convey("This method is excluded from the Unit Tests", t, func() { + So(true, ShouldEqual, true) + }) + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + func TestEncrypt(t *testing.T) { volume := MockNewVolume(*mock.VolumeDBO, nil, true) block := make([]uint8, 1024) @@ -297,6 +807,117 @@ func TestDecrypt(t *testing.T) { }) } +func TestIsReady(t *testing.T) { + mockDisks := []models.Disk{mock.NewMockDisk(), mock.NewMockDisk()} + volume := MockNewVolume(*mock.BackupVolumeDBO, nil, true) + + for _, _mdisk := range mockDisks { + volume.AddDisk(_mdisk.GetUUID(), _mdisk) + } + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `disks` WHERE volume_uuid = ? AND is_virtual = ? AND virtual_disk_uuid = ? ORDER BY `disks`.`uuid` LIMIT 1")).WithArgs(volume.UUID.String(), false, uuid.Nil).WillReturnRows(mock.DiskRow(&dbo.Disk{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: mockDisks[1].GetUUID(), + }, + UserUUID: uuid.UUID{}, + VolumeUUID: volume.UUID, + ProviderUUID: uuid.UUID{}, + Credentials: "", + Name: "", + UsedSpace: 0, + TotalSpace: 0, + FreeSpace: 0, + CreatedAt: time.Time{}, + IsVirtual: false, + VirtualDiskUUID: uuid.UUID{}, + User: dbo.User{}, + Volume: *mock.BackupVolumeDBO, + Provider: dbo.Provider{}, + })) + + mock.DBMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `providers` WHERE type = ?")).WithArgs(constants.PROVIDER_TYPE_RAID1).WillReturnRows(mock.ProviderRow(&dbo.Provider{ + AbstractDatabaseObject: dbo.AbstractDatabaseObject{ + UUID: uuid.UUID{}, + }, + Type: constants.PROVIDER_TYPE_RAID1, + Name: "", + Logo: "", + })) + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec("INSERT INTO `disks`").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + mock.DBMock.ExpectBegin() + mock.DBMock.ExpectExec(regexp.QuoteMeta("UPDATE `disks`")).WillReturnResult(sqlmock.NewResult(1, 1)) + mock.DBMock.ExpectCommit() + + _, _ = volume.GenerateVirtualDisk(mockDisks[0]) + + newDisk := mock.NewMockDisk() + volume.AddDisk(newDisk.GetUUID(), newDisk) + + emptyVolume := MockNewVolume(*mock.VolumeDBO, nil, true) + + Convey("An empty volume should not be ready", t, func() { + So(emptyVolume.IsReady(nil, true), ShouldEqual, false) + }) + + Convey("A backed up volume should not be ready if not all real disks are backed up", t, func() { + So(volume.IsReady(nil, true), ShouldEqual, false) + }) + + emptyVolume.AddDisk(newDisk.GetUUID(), newDisk) + Convey("The function returns true if the volume is ready", t, func() { + So(emptyVolume.IsReady(nil, true), ShouldEqual, true) + So(emptyVolume.IsReady(nil, false), ShouldEqual, true) + }) + + Convey("The function returns false if the volume is not ready", t, func() { + newDisk.GetReadiness().(*mock.MockDiskReadiness).Readiness = false + So(emptyVolume.IsReady(nil, true), ShouldEqual, false) + So(emptyVolume.IsReady(nil, false), ShouldEqual, false) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + +func TestNewVolume(t *testing.T) { + models.Transport.ActiveVolumes.RemoveEnqueuedInstance(mock.VolumeDBO.UUID) + + disks := mock.GetDiskDBOs(1) + volume := MockNewVolume(*mock.VolumeDBO, disks, true) + + Convey("Test if the volume is created properly", t, func() { + Convey("UUID is set properly", func() { + So(volume.UUID, ShouldEqual, mock.VolumeDBO.UUID) + }) + Convey("BlockSize is set properly", func() { + So(volume.BlockSize, ShouldEqual, constants.DEFAULT_VOLUME_BLOCK_SIZE) + }) + Convey("Name is set properly", func() { + So(volume.Name, ShouldEqual, mock.VolumeDBO.Name) + }) + Convey("UserUUID is set properly", func() { + So(volume.UserUUID, ShouldEqual, mock.VolumeDBO.UserUUID) + }) + Convey("VolumeSettings are set properly", func() { + So(volume.VolumeSettings, ShouldResemble, mock.VolumeDBO.VolumeSettings) + }) + Convey("Volume contains the assigned disks", func() { + for i := 0; i < len(disks); i++ { + So(volume.GetDisk(disks[i].UUID).GetUUID(), ShouldEqual, disks[i].UUID) + } + }) + }) + + Convey("The database call should be correct", t, func() { + So(mock.DBMock.ExpectationsWereMet(), ShouldEqual, nil) + }) +} + func MockNewVolume(_volumeDBO dbo.Volume, _disks []dbo.Disk, dry_run bool) *models.Volume { var _disksPtr []*dbo.Disk for _, _disk := range _disks {