75f3dd141c
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CI and Release / lint-frontend (push) Has been cancelled
CI and Release / dockerfile-scan (push) Has been cancelled
CI and Release / test-frontend (push) Has been cancelled
CI and Release / lint-verification-agent (push) Has been cancelled
CI and Release / test-verification-agent (push) Has been cancelled
CI and Release / e2e-verification-agent (push) Has been cancelled
CI and Release / test-backend (push) Has been cancelled
CI and Release / build-dev-image (push) Has been cancelled
CI and Release / push-dev-image (push) Has been cancelled
CI and Release / build-image (push) Has been cancelled
CI and Release / build-verification-image (push) Has been cancelled
CI and Release / determine-version (push) Has been cancelled
CI and Release / push-image (push) Has been cancelled
CI and Release / push-verification-image (12) (push) Has been cancelled
CI and Release / lint-backend (push) Has been cancelled
CI and Release / push-verification-image (17) (push) Has been cancelled
CI and Release / push-verification-image (18) (push) Has been cancelled
CI and Release / release (push) Has been cancelled
CI and Release / publish-helm-chart (push) Has been cancelled
CI and Release / push-verification-image (13) (push) Has been cancelled
CI and Release / push-verification-image (14) (push) Has been cancelled
CI and Release / push-verification-image (15) (push) Has been cancelled
CI and Release / push-verification-image (16) (push) Has been cancelled
1723 lines
50 KiB
Go
1723 lines
50 KiB
Go
package backups_config_logical
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"databasus-backend/internal/config"
|
|
backups_core_enums "databasus-backend/internal/features/backups/backups/core/enums"
|
|
"databasus-backend/internal/features/databases"
|
|
postgresql_logical "databasus-backend/internal/features/databases/databases/postgresql/logical"
|
|
"databasus-backend/internal/features/intervals"
|
|
"databasus-backend/internal/features/notifiers"
|
|
"databasus-backend/internal/features/storages"
|
|
users_enums "databasus-backend/internal/features/users/enums"
|
|
users_testing "databasus-backend/internal/features/users/testing"
|
|
workspaces_controllers "databasus-backend/internal/features/workspaces/controllers"
|
|
workspaces_testing "databasus-backend/internal/features/workspaces/testing"
|
|
"databasus-backend/internal/util/period"
|
|
test_utils "databasus-backend/internal/util/testing"
|
|
"databasus-backend/internal/util/tools"
|
|
)
|
|
|
|
func createTestRouter() *gin.Engine {
|
|
router := workspaces_testing.CreateTestRouter(
|
|
workspaces_controllers.GetWorkspaceController(),
|
|
workspaces_controllers.GetMembershipController(),
|
|
databases.GetDatabaseController(),
|
|
GetBackupConfigController(),
|
|
)
|
|
return router
|
|
}
|
|
|
|
func Test_SaveBackupConfig_PermissionsEnforced(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
workspaceRole *users_enums.WorkspaceRole
|
|
isGlobalAdmin bool
|
|
expectSuccess bool
|
|
expectedStatusCode int
|
|
}{
|
|
{
|
|
name: "workspace owner can save backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleOwner; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "workspace admin can save backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleAdmin; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "workspace member can save backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleMember; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "workspace viewer cannot save backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleViewer; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: false,
|
|
expectedStatusCode: http.StatusBadRequest,
|
|
},
|
|
{
|
|
name: "global admin can save backup config",
|
|
workspaceRole: nil,
|
|
isGlobalAdmin: true,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
router := createTestRouter()
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace("Test Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", workspace.ID, owner.Token, router)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
var testUserToken string
|
|
if tt.isGlobalAdmin {
|
|
admin := users_testing.CreateTestUser(users_enums.UserRoleAdmin)
|
|
testUserToken = admin.Token
|
|
} else if tt.workspaceRole != nil && *tt.workspaceRole == users_enums.WorkspaceRoleOwner {
|
|
testUserToken = owner.Token
|
|
} else if tt.workspaceRole != nil {
|
|
member := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspaces_testing.AddMemberToWorkspace(
|
|
workspace,
|
|
member,
|
|
*tt.workspaceRole,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
testUserToken = member.Token
|
|
}
|
|
|
|
timeOfDay := "04:00"
|
|
request := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
}
|
|
|
|
var response LogicalBackupConfig
|
|
testResp := test_utils.MakePostRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+testUserToken,
|
|
request,
|
|
tt.expectedStatusCode,
|
|
&response,
|
|
)
|
|
|
|
if tt.expectSuccess {
|
|
assert.Equal(t, database.ID, response.DatabaseID)
|
|
assert.True(t, response.IsBackupsEnabled)
|
|
assert.Equal(t, period.PeriodWeek, response.RetentionTimePeriod)
|
|
} else {
|
|
assert.Contains(t, string(testResp.Body), "insufficient permissions")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_SaveBackupConfig_WhenUserIsNotWorkspaceMember_ReturnsForbidden(t *testing.T) {
|
|
router := createTestRouter()
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace("Test Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", workspace.ID, owner.Token, router)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
nonMember := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
|
|
timeOfDay := "04:00"
|
|
request := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+nonMember.Token,
|
|
request,
|
|
http.StatusBadRequest,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "insufficient permissions")
|
|
}
|
|
|
|
func Test_GetBackupConfigByDbID_PermissionsEnforced(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
workspaceRole *users_enums.WorkspaceRole
|
|
isGlobalAdmin bool
|
|
expectSuccess bool
|
|
expectedStatusCode int
|
|
}{
|
|
{
|
|
name: "workspace owner can get backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleOwner; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "workspace admin can get backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleAdmin; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "workspace member can get backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleMember; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "workspace viewer can get backup config",
|
|
workspaceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleViewer; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "global admin can get backup config",
|
|
workspaceRole: nil,
|
|
isGlobalAdmin: true,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "non-member cannot get backup config",
|
|
workspaceRole: nil,
|
|
isGlobalAdmin: false,
|
|
expectSuccess: false,
|
|
expectedStatusCode: http.StatusNotFound,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
router := createTestRouter()
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace("Test Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", workspace.ID, owner.Token, router)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
var testUserToken string
|
|
if tt.isGlobalAdmin {
|
|
admin := users_testing.CreateTestUser(users_enums.UserRoleAdmin)
|
|
testUserToken = admin.Token
|
|
} else if tt.workspaceRole != nil && *tt.workspaceRole == users_enums.WorkspaceRoleOwner {
|
|
testUserToken = owner.Token
|
|
} else if tt.workspaceRole != nil {
|
|
member := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspaces_testing.AddMemberToWorkspace(
|
|
workspace,
|
|
member,
|
|
*tt.workspaceRole,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
testUserToken = member.Token
|
|
} else {
|
|
nonMember := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
testUserToken = nonMember.Token
|
|
}
|
|
|
|
var response LogicalBackupConfig
|
|
testResp := test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String(),
|
|
"Bearer "+testUserToken,
|
|
tt.expectedStatusCode,
|
|
&response,
|
|
)
|
|
|
|
if tt.expectSuccess {
|
|
assert.Equal(t, database.ID, response.DatabaseID)
|
|
assert.NotNil(t, response.BackupInterval)
|
|
} else {
|
|
assert.Contains(t, string(testResp.Body), "backup configuration not found")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_GetBackupConfigByDbID_ReturnsDefaultConfigForNewDatabase(t *testing.T) {
|
|
router := createTestRouter()
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace("Test Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", workspace.ID, owner.Token, router)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
var response LogicalBackupConfig
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&response,
|
|
)
|
|
|
|
assert.Equal(t, database.ID, response.DatabaseID)
|
|
assert.False(t, response.IsBackupsEnabled)
|
|
assert.True(t, response.IsRetryIfFailed)
|
|
assert.Equal(t, 3, response.MaxFailedTriesCount)
|
|
assert.NotNil(t, response.BackupInterval)
|
|
}
|
|
|
|
func Test_IsStorageUsing_PermissionsEnforced(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
isStorageOwner bool
|
|
expectSuccess bool
|
|
expectedStatusCode int
|
|
}{
|
|
{
|
|
name: "storage owner can check storage usage",
|
|
isStorageOwner: true,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "non-storage-owner cannot check storage usage",
|
|
isStorageOwner: false,
|
|
expectSuccess: false,
|
|
expectedStatusCode: http.StatusInternalServerError,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
router := createTestRouter()
|
|
storageOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace(
|
|
"Test Workspace",
|
|
storageOwner,
|
|
router,
|
|
)
|
|
storage := createTestStorage(workspace.ID)
|
|
|
|
defer func() {
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
var testUserToken string
|
|
if tt.isStorageOwner {
|
|
testUserToken = storageOwner.Token
|
|
} else {
|
|
otherUser := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
testUserToken = otherUser.Token
|
|
}
|
|
|
|
if tt.expectSuccess {
|
|
var response map[string]bool
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/storage/"+storage.ID.String()+"/is-using",
|
|
"Bearer "+testUserToken,
|
|
tt.expectedStatusCode,
|
|
&response,
|
|
)
|
|
|
|
isUsing, exists := response["isUsing"]
|
|
assert.True(t, exists)
|
|
assert.False(t, isUsing)
|
|
} else {
|
|
testResp := test_utils.MakeGetRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/storage/"+storage.ID.String()+"/is-using",
|
|
"Bearer "+testUserToken,
|
|
tt.expectedStatusCode,
|
|
)
|
|
assert.Contains(t, string(testResp.Body), "error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_SaveBackupConfig_WithEncryptionNone_ConfigSaved(t *testing.T) {
|
|
router := createTestRouter()
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace("Test Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", workspace.ID, owner.Token, router)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
request := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
var response LogicalBackupConfig
|
|
test_utils.MakePostRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
&response,
|
|
)
|
|
|
|
assert.Equal(t, database.ID, response.DatabaseID)
|
|
assert.Equal(t, backups_core_enums.BackupEncryptionNone, response.Encryption)
|
|
}
|
|
|
|
func Test_SaveBackupConfig_WithEncryptionEncrypted_ConfigSaved(t *testing.T) {
|
|
router := createTestRouter()
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
workspace := workspaces_testing.CreateTestWorkspace("Test Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", workspace.ID, owner.Token, router)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(workspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
request := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionEncrypted,
|
|
}
|
|
|
|
var response LogicalBackupConfig
|
|
test_utils.MakePostRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
&response,
|
|
)
|
|
|
|
assert.Equal(t, database.ID, response.DatabaseID)
|
|
assert.Equal(t, backups_core_enums.BackupEncryptionEncrypted, response.Encryption)
|
|
}
|
|
|
|
func Test_TransferDatabase_PermissionsEnforced(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
sourceRole *users_enums.WorkspaceRole
|
|
targetRole *users_enums.WorkspaceRole
|
|
isGlobalAdmin bool
|
|
expectSuccess bool
|
|
expectedStatusCode int
|
|
}{
|
|
{
|
|
name: "owner in both workspaces can transfer",
|
|
sourceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleOwner; return &r }(),
|
|
targetRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleOwner; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "admin in both workspaces can transfer",
|
|
sourceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleAdmin; return &r }(),
|
|
targetRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleAdmin; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "member in both workspaces can transfer",
|
|
sourceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleMember; return &r }(),
|
|
targetRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleMember; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "viewer in both workspaces cannot transfer",
|
|
sourceRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleViewer; return &r }(),
|
|
targetRole: func() *users_enums.WorkspaceRole { r := users_enums.WorkspaceRoleViewer; return &r }(),
|
|
isGlobalAdmin: false,
|
|
expectSuccess: false,
|
|
expectedStatusCode: http.StatusForbidden,
|
|
},
|
|
{
|
|
name: "global admin can transfer",
|
|
sourceRole: nil,
|
|
targetRole: nil,
|
|
isGlobalAdmin: true,
|
|
expectSuccess: true,
|
|
expectedStatusCode: http.StatusOK,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
sourceOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
targetOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace(
|
|
"Source Workspace",
|
|
sourceOwner,
|
|
router,
|
|
)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace(
|
|
"Target Workspace",
|
|
targetOwner,
|
|
router,
|
|
)
|
|
|
|
database := createTestDatabaseViaAPI(
|
|
"Test Database",
|
|
sourceWorkspace.ID,
|
|
sourceOwner.Token,
|
|
router,
|
|
)
|
|
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
|
|
defer func() {
|
|
// Cleanup in correct order to avoid foreign key violations
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(50 * time.Millisecond) // Wait for cascade delete of backup_config
|
|
storages.RemoveTestStorage(targetStorage.ID)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
var testUserToken string
|
|
if tt.isGlobalAdmin {
|
|
admin := users_testing.CreateTestUser(users_enums.UserRoleAdmin)
|
|
testUserToken = admin.Token
|
|
} else if tt.sourceRole != nil {
|
|
testUser := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
if *tt.sourceRole == users_enums.WorkspaceRoleOwner {
|
|
testUserToken = sourceOwner.Token
|
|
workspaces_testing.AddMemberToWorkspace(
|
|
targetWorkspace,
|
|
sourceOwner,
|
|
*tt.targetRole,
|
|
targetOwner.Token,
|
|
router,
|
|
)
|
|
} else {
|
|
workspaces_testing.AddMemberToWorkspace(
|
|
sourceWorkspace,
|
|
testUser,
|
|
*tt.sourceRole,
|
|
sourceOwner.Token,
|
|
router,
|
|
)
|
|
workspaces_testing.AddMemberToWorkspace(
|
|
targetWorkspace,
|
|
testUser,
|
|
*tt.targetRole,
|
|
targetOwner.Token,
|
|
router,
|
|
)
|
|
testUserToken = testUser.Token
|
|
}
|
|
}
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+testUserToken,
|
|
request,
|
|
tt.expectedStatusCode,
|
|
)
|
|
|
|
if tt.expectSuccess {
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database.ID.String(),
|
|
"Bearer "+targetOwner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
} else {
|
|
assert.Contains(t, string(testResp.Body), "insufficient permissions")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_TransferDatabase_NonMemberInSourceWorkspace_CannotTransfer(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
sourceOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
targetOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace(
|
|
"Source Workspace",
|
|
sourceOwner,
|
|
router,
|
|
)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace(
|
|
"Target Workspace",
|
|
targetOwner,
|
|
router,
|
|
)
|
|
|
|
database := createTestDatabaseViaAPI(
|
|
"Test Database",
|
|
sourceWorkspace.ID,
|
|
sourceOwner.Token,
|
|
router,
|
|
)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+targetOwner.Token,
|
|
request,
|
|
http.StatusForbidden,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "insufficient permissions")
|
|
}
|
|
|
|
func Test_TransferDatabase_NonMemberInTargetWorkspace_CannotTransfer(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
sourceOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
targetOwner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace(
|
|
"Source Workspace",
|
|
sourceOwner,
|
|
router,
|
|
)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace(
|
|
"Target Workspace",
|
|
targetOwner,
|
|
router,
|
|
)
|
|
|
|
database := createTestDatabaseViaAPI(
|
|
"Test Database",
|
|
sourceWorkspace.ID,
|
|
sourceOwner.Token,
|
|
router,
|
|
)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+sourceOwner.Token,
|
|
request,
|
|
http.StatusForbidden,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "insufficient permissions")
|
|
}
|
|
|
|
func Test_TransferDatabase_ToNewStorage_DatabaseTransferd(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", sourceWorkspace.ID, owner.Token, router)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(200 * time.Millisecond) // Wait for cascading deletes
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
var savedConfig LogicalBackupConfig
|
|
test_utils.MakePostRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
&savedConfig,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
|
|
var retrievedConfig LogicalBackupConfig
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedConfig,
|
|
)
|
|
assert.NotNil(t, retrievedConfig.StorageID)
|
|
assert.Equal(t, targetStorage.ID, *retrievedConfig.StorageID)
|
|
}
|
|
|
|
func Test_TransferDatabase_WithExistingStorage_DatabaseAndStorageTransferd(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", sourceWorkspace.ID, owner.Token, router)
|
|
storage := createTestStorage(sourceWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(200 * time.Millisecond) // Wait for cascading deletes
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: storage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
var savedConfig LogicalBackupConfig
|
|
test_utils.MakePostRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
&savedConfig,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
IsTransferWithStorage: true,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
|
|
var retrievedStorage storages.Storage
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/storages/"+storage.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedStorage,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, retrievedStorage.WorkspaceID)
|
|
}
|
|
|
|
func Test_TransferDatabase_StorageHasOtherDBs_CannotTransfer(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database1 := createTestDatabaseViaAPI(
|
|
"Test Database 1",
|
|
sourceWorkspace.ID,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
database2 := createTestDatabaseViaAPI(
|
|
"Test Database 2",
|
|
sourceWorkspace.ID,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
storage := createTestStorage(sourceWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database1)
|
|
databases.RemoveTestDatabase(database2)
|
|
time.Sleep(200 * time.Millisecond) // Wait for cascading deletes
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest1 := LogicalBackupConfig{
|
|
DatabaseID: database1.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: storage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest1,
|
|
http.StatusOK,
|
|
)
|
|
|
|
backupConfigRequest2 := LogicalBackupConfig{
|
|
DatabaseID: database2.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: storage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest2,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
IsTransferWithStorage: true,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database1.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusBadRequest,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "storage has other attached databases")
|
|
}
|
|
|
|
func Test_TransferDatabase_WithNotifiers_NotifiersTransferred(t *testing.T) {
|
|
router := createTestRouterWithNotifiersForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", sourceWorkspace.ID, owner.Token, router)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
notifier := notifiers.CreateTestNotifier(sourceWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(200 * time.Millisecond)
|
|
notifiers.RemoveTestNotifier(notifier)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
database.Notifiers = []notifiers.Notifier{*notifier}
|
|
var updatedDatabase databases.Database
|
|
test_utils.MakePostRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/update",
|
|
"Bearer "+owner.Token,
|
|
database,
|
|
http.StatusOK,
|
|
&updatedDatabase,
|
|
)
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
IsTransferWithNotifiers: true,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
assert.Len(t, retrievedDatabase.Notifiers, 1)
|
|
|
|
var retrievedNotifier notifiers.Notifier
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/notifiers/"+notifier.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedNotifier,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, retrievedNotifier.WorkspaceID)
|
|
}
|
|
|
|
func Test_TransferDatabase_NotifierHasOtherDBs_NotifierSkipped(t *testing.T) {
|
|
router := createTestRouterWithNotifiersForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database1 := createTestDatabaseViaAPI(
|
|
"Test Database 1",
|
|
sourceWorkspace.ID,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
database2 := createTestDatabaseViaAPI(
|
|
"Test Database 2",
|
|
sourceWorkspace.ID,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
sharedNotifier := notifiers.CreateTestNotifier(sourceWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database1)
|
|
databases.RemoveTestDatabase(database2)
|
|
time.Sleep(200 * time.Millisecond)
|
|
notifiers.RemoveTestNotifier(sharedNotifier)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
database1.Notifiers = []notifiers.Notifier{*sharedNotifier}
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/update",
|
|
"Bearer "+owner.Token,
|
|
database1,
|
|
http.StatusOK,
|
|
)
|
|
|
|
database2.Notifiers = []notifiers.Notifier{*sharedNotifier}
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/update",
|
|
"Bearer "+owner.Token,
|
|
database2,
|
|
http.StatusOK,
|
|
)
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database1.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
IsTransferWithNotifiers: true,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database1.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database1.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
|
|
var retrievedNotifier notifiers.Notifier
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/notifiers/"+sharedNotifier.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedNotifier,
|
|
)
|
|
assert.Equal(t, sourceWorkspace.ID, retrievedNotifier.WorkspaceID)
|
|
}
|
|
|
|
func Test_TransferDatabase_WithMultipleNotifiers_OnlyExclusiveOnesTransferred(t *testing.T) {
|
|
router := createTestRouterWithNotifiersForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database1 := createTestDatabaseViaAPI(
|
|
"Test Database 1",
|
|
sourceWorkspace.ID,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
database2 := createTestDatabaseViaAPI(
|
|
"Test Database 2",
|
|
sourceWorkspace.ID,
|
|
owner.Token,
|
|
router,
|
|
)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
|
|
exclusiveNotifier := notifiers.CreateTestNotifier(sourceWorkspace.ID)
|
|
sharedNotifier := notifiers.CreateTestNotifier(sourceWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database1)
|
|
databases.RemoveTestDatabase(database2)
|
|
time.Sleep(200 * time.Millisecond)
|
|
notifiers.RemoveTestNotifier(exclusiveNotifier)
|
|
notifiers.RemoveTestNotifier(sharedNotifier)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
database1.Notifiers = []notifiers.Notifier{*exclusiveNotifier, *sharedNotifier}
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/update",
|
|
"Bearer "+owner.Token,
|
|
database1,
|
|
http.StatusOK,
|
|
)
|
|
|
|
database2.Notifiers = []notifiers.Notifier{*sharedNotifier}
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/update",
|
|
"Bearer "+owner.Token,
|
|
database2,
|
|
http.StatusOK,
|
|
)
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database1.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
IsTransferWithNotifiers: true,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database1.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database1.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
assert.Len(t, retrievedDatabase.Notifiers, 2)
|
|
|
|
var retrievedExclusiveNotifier notifiers.Notifier
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/notifiers/"+exclusiveNotifier.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedExclusiveNotifier,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, retrievedExclusiveNotifier.WorkspaceID)
|
|
|
|
var retrievedSharedNotifier notifiers.Notifier
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/notifiers/"+sharedNotifier.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedSharedNotifier,
|
|
)
|
|
assert.Equal(t, sourceWorkspace.ID, retrievedSharedNotifier.WorkspaceID)
|
|
}
|
|
|
|
func Test_TransferDatabase_WithTargetNotifiers_NotifiersAssigned(t *testing.T) {
|
|
router := createTestRouterWithNotifiersForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", sourceWorkspace.ID, owner.Token, router)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
targetNotifier := notifiers.CreateTestNotifier(targetWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(200 * time.Millisecond)
|
|
notifiers.RemoveTestNotifier(targetNotifier)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
TargetNotifierIDs: []uuid.UUID{targetNotifier.ID},
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusOK,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "database transferred successfully")
|
|
|
|
var retrievedDatabase databases.Database
|
|
test_utils.MakeGetRequestAndUnmarshal(
|
|
t,
|
|
router,
|
|
"/api/v1/databases/"+database.ID.String(),
|
|
"Bearer "+owner.Token,
|
|
http.StatusOK,
|
|
&retrievedDatabase,
|
|
)
|
|
assert.Equal(t, targetWorkspace.ID, *retrievedDatabase.WorkspaceID)
|
|
assert.Len(t, retrievedDatabase.Notifiers, 1)
|
|
assert.Equal(t, targetNotifier.ID, retrievedDatabase.Notifiers[0].ID)
|
|
}
|
|
|
|
func Test_TransferDatabase_TargetNotifierFromDifferentWorkspace_ReturnsBadRequest(t *testing.T) {
|
|
router := createTestRouterWithNotifiersForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
otherWorkspace := workspaces_testing.CreateTestWorkspace("Other Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", sourceWorkspace.ID, owner.Token, router)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
targetStorage := createTestStorage(targetWorkspace.ID)
|
|
wrongNotifier := notifiers.CreateTestNotifier(otherWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(200 * time.Millisecond)
|
|
notifiers.RemoveTestNotifier(wrongNotifier)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(otherWorkspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &targetStorage.ID,
|
|
TargetNotifierIDs: []uuid.UUID{wrongNotifier.ID},
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusBadRequest,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "target notifier does not belong to target workspace")
|
|
}
|
|
|
|
func Test_TransferDatabase_TargetStorageFromDifferentWorkspace_ReturnsBadRequest(t *testing.T) {
|
|
router := createTestRouterWithNotifiersForTransfer()
|
|
|
|
owner := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
sourceWorkspace := workspaces_testing.CreateTestWorkspace("Source Workspace", owner, router)
|
|
targetWorkspace := workspaces_testing.CreateTestWorkspace("Target Workspace", owner, router)
|
|
otherWorkspace := workspaces_testing.CreateTestWorkspace("Other Workspace", owner, router)
|
|
|
|
database := createTestDatabaseViaAPI("Test Database", sourceWorkspace.ID, owner.Token, router)
|
|
sourceStorage := createTestStorage(sourceWorkspace.ID)
|
|
wrongStorage := createTestStorage(otherWorkspace.ID)
|
|
|
|
defer func() {
|
|
databases.RemoveTestDatabase(database)
|
|
time.Sleep(200 * time.Millisecond)
|
|
workspaces_testing.RemoveTestWorkspace(sourceWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(targetWorkspace, router)
|
|
workspaces_testing.RemoveTestWorkspace(otherWorkspace, router)
|
|
}()
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigRequest := LogicalBackupConfig{
|
|
DatabaseID: database.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
Storage: sourceStorage,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner.Token,
|
|
backupConfigRequest,
|
|
http.StatusOK,
|
|
)
|
|
|
|
request := TransferDatabaseRequest{
|
|
TargetWorkspaceID: targetWorkspace.ID,
|
|
TargetStorageID: &wrongStorage.ID,
|
|
}
|
|
|
|
testResp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/database/"+database.ID.String()+"/transfer",
|
|
"Bearer "+owner.Token,
|
|
request,
|
|
http.StatusBadRequest,
|
|
)
|
|
|
|
assert.Contains(t, string(testResp.Body), "target storage does not belong to target workspace")
|
|
}
|
|
|
|
func Test_SaveBackupConfig_WithStorageFromAnotherWorkspace_ReturnsBadRequest(t *testing.T) {
|
|
router := createTestRouterWithStorageForTransfer()
|
|
|
|
owner1 := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
owner2 := users_testing.CreateTestUser(users_enums.UserRoleMember)
|
|
|
|
workspaceA := workspaces_testing.CreateTestWorkspace("Workspace A", owner1, router)
|
|
workspaceB := workspaces_testing.CreateTestWorkspace("Workspace B", owner2, router)
|
|
|
|
databaseA := createTestDatabaseViaAPI("Database A", workspaceA.ID, owner1.Token, router)
|
|
|
|
storageB := createTestStorage(workspaceB.ID)
|
|
|
|
timeOfDay := "04:00"
|
|
backupConfigWithForeignStorage := LogicalBackupConfig{
|
|
DatabaseID: databaseA.ID,
|
|
IsBackupsEnabled: true,
|
|
RetentionPolicyType: RetentionPolicyTypeTimePeriod,
|
|
RetentionTimePeriod: period.PeriodWeek,
|
|
BackupInterval: intervals.Interval{
|
|
Type: intervals.IntervalDaily,
|
|
TimeOfDay: &timeOfDay,
|
|
},
|
|
StorageID: &storageB.ID,
|
|
Storage: storageB,
|
|
SendNotificationsOn: []BackupNotificationType{
|
|
NotificationBackupFailed,
|
|
},
|
|
IsRetryIfFailed: true,
|
|
MaxFailedTriesCount: 3,
|
|
Encryption: backups_core_enums.BackupEncryptionNone,
|
|
}
|
|
|
|
resp := test_utils.MakePostRequest(
|
|
t,
|
|
router,
|
|
"/api/v1/backup-configs/save",
|
|
"Bearer "+owner1.Token,
|
|
backupConfigWithForeignStorage,
|
|
http.StatusBadRequest,
|
|
)
|
|
|
|
assert.Contains(t, string(resp.Body), "storage does not belong to the same workspace")
|
|
|
|
// Cleanup: database first (cascades to backup_config), then storage, then workspaces
|
|
databases.RemoveTestDatabase(databaseA)
|
|
storages.RemoveTestStorage(storageB.ID)
|
|
workspaces_testing.RemoveTestWorkspace(workspaceA, router)
|
|
workspaces_testing.RemoveTestWorkspace(workspaceB, router)
|
|
}
|
|
|
|
func createTestDatabaseViaAPI(
|
|
name string,
|
|
workspaceID uuid.UUID,
|
|
token string,
|
|
router *gin.Engine,
|
|
) *databases.Database {
|
|
env := config.GetEnv()
|
|
port, err := strconv.Atoi(env.TestLogicalPostgres16Port)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to parse TEST_LOGICAL_POSTGRES_16_PORT: %v", err))
|
|
}
|
|
|
|
testDbName := "testdb"
|
|
request := databases.Database{
|
|
WorkspaceID: &workspaceID,
|
|
Name: name,
|
|
Type: databases.DatabaseTypePostgresLogical,
|
|
PostgresqlLogical: &postgresql_logical.PostgresqlLogicalDatabase{
|
|
Version: tools.PostgresqlVersion16,
|
|
Host: config.GetEnv().TestLocalhost,
|
|
Port: port,
|
|
Username: "testuser",
|
|
Password: "testpassword",
|
|
Database: &testDbName,
|
|
CpuCount: 1,
|
|
},
|
|
}
|
|
|
|
w := workspaces_testing.MakeAPIRequest(
|
|
router,
|
|
"POST",
|
|
"/api/v1/databases/create",
|
|
"Bearer "+token,
|
|
request,
|
|
)
|
|
|
|
if w.Code != http.StatusCreated {
|
|
panic(
|
|
fmt.Sprintf("Failed to create database. Status: %d, Body: %s", w.Code, w.Body.String()),
|
|
)
|
|
}
|
|
|
|
var database databases.Database
|
|
if err := json.Unmarshal(w.Body.Bytes(), &database); err != nil {
|
|
panic(err)
|
|
}
|
|
return &database
|
|
}
|
|
|
|
func createTestStorage(workspaceID uuid.UUID) *storages.Storage {
|
|
return storages.CreateTestStorage(workspaceID)
|
|
}
|
|
|
|
func createTestRouterWithStorageForTransfer() *gin.Engine {
|
|
router := workspaces_testing.CreateTestRouter(
|
|
workspaces_controllers.GetWorkspaceController(),
|
|
workspaces_controllers.GetMembershipController(),
|
|
databases.GetDatabaseController(),
|
|
GetBackupConfigController(),
|
|
storages.GetStorageController(),
|
|
)
|
|
|
|
storages.SetupDependencies()
|
|
databases.SetupDependencies()
|
|
SetupDependencies()
|
|
|
|
return router
|
|
}
|
|
|
|
func createTestRouterWithNotifiersForTransfer() *gin.Engine {
|
|
router := workspaces_testing.CreateTestRouter(
|
|
workspaces_controllers.GetWorkspaceController(),
|
|
workspaces_controllers.GetMembershipController(),
|
|
databases.GetDatabaseController(),
|
|
GetBackupConfigController(),
|
|
storages.GetStorageController(),
|
|
notifiers.GetNotifierController(),
|
|
)
|
|
|
|
storages.SetupDependencies()
|
|
databases.SetupDependencies()
|
|
notifiers.SetupDependencies()
|
|
SetupDependencies()
|
|
|
|
return router
|
|
}
|