2023-02-03 17:04:35 +00:00
|
|
|
import pytest
|
2023-02-03 19:09:24 +00:00
|
|
|
import os.path as path
|
2023-02-20 16:09:01 +00:00
|
|
|
from os import makedirs
|
2023-02-22 15:58:36 +00:00
|
|
|
from os import remove
|
|
|
|
from os import listdir
|
2023-04-07 15:18:54 +00:00
|
|
|
from datetime import datetime, timedelta, timezone
|
2023-02-03 17:04:35 +00:00
|
|
|
|
2023-04-12 17:18:12 +00:00
|
|
|
import selfprivacy_api.services as services
|
|
|
|
from selfprivacy_api.services import get_service_by_id
|
2023-02-03 17:04:35 +00:00
|
|
|
from selfprivacy_api.services.test_service import DummyService
|
2023-04-10 13:22:33 +00:00
|
|
|
from selfprivacy_api.graphql.queries.providers import BackupProvider
|
2023-02-22 13:35:55 +00:00
|
|
|
|
2023-02-08 15:14:08 +00:00
|
|
|
from selfprivacy_api.backup import Backups
|
2023-02-01 11:58:55 +00:00
|
|
|
import selfprivacy_api.backup.providers as providers
|
|
|
|
from selfprivacy_api.backup.providers import AbstractBackupProvider
|
|
|
|
from selfprivacy_api.backup.providers.backblaze import Backblaze
|
2023-03-29 11:45:52 +00:00
|
|
|
from selfprivacy_api.backup.tasks import start_backup
|
2023-04-10 13:22:33 +00:00
|
|
|
from selfprivacy_api.backup.storage import Storage
|
|
|
|
|
2023-02-01 11:58:55 +00:00
|
|
|
|
2023-02-03 19:09:24 +00:00
|
|
|
TESTFILE_BODY = "testytest!"
|
2023-02-08 14:05:25 +00:00
|
|
|
REPO_NAME = "test_backup"
|
2023-02-03 19:09:24 +00:00
|
|
|
|
|
|
|
|
2023-02-20 16:09:01 +00:00
|
|
|
@pytest.fixture(scope="function")
|
|
|
|
def backups(tmpdir):
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.reset()
|
|
|
|
|
2023-02-20 16:09:01 +00:00
|
|
|
test_repo_path = path.join(tmpdir, "totallyunrelated")
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.set_localfile_repo(test_repo_path)
|
2023-03-13 19:03:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture()
|
|
|
|
def backups_backblaze(generic_userdata):
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.reset()
|
2023-02-20 16:09:01 +00:00
|
|
|
|
|
|
|
|
2023-02-03 17:04:35 +00:00
|
|
|
@pytest.fixture()
|
2023-02-20 16:09:01 +00:00
|
|
|
def raw_dummy_service(tmpdir, backups):
|
|
|
|
service_dir = path.join(tmpdir, "test_service")
|
|
|
|
makedirs(service_dir)
|
|
|
|
|
|
|
|
testfile_path = path.join(service_dir, "testfile.txt")
|
|
|
|
with open(testfile_path, "w") as file:
|
2023-02-03 19:09:24 +00:00
|
|
|
file.write(TESTFILE_BODY)
|
2023-02-08 14:05:25 +00:00
|
|
|
|
2023-04-14 10:18:21 +00:00
|
|
|
# we need this to not change get_drive() much
|
2023-02-20 16:09:01 +00:00
|
|
|
class TestDummyService(DummyService, location=service_dir):
|
2023-02-08 14:05:25 +00:00
|
|
|
pass
|
|
|
|
|
2023-02-20 10:35:51 +00:00
|
|
|
service = TestDummyService()
|
2023-02-20 16:09:01 +00:00
|
|
|
return service
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture()
|
|
|
|
def dummy_service(tmpdir, backups, raw_dummy_service):
|
|
|
|
service = raw_dummy_service
|
|
|
|
repo_path = path.join(tmpdir, "test_repo")
|
|
|
|
assert not path.exists(repo_path)
|
|
|
|
# assert not repo_path
|
|
|
|
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.init_repo(service)
|
2023-04-12 17:18:12 +00:00
|
|
|
|
|
|
|
# register our service
|
|
|
|
services.services.append(service)
|
|
|
|
|
|
|
|
assert get_service_by_id(service.get_id()) is not None
|
2023-02-20 10:35:51 +00:00
|
|
|
return service
|
2023-02-03 17:04:35 +00:00
|
|
|
|
|
|
|
|
2023-02-03 18:49:24 +00:00
|
|
|
@pytest.fixture()
|
2023-02-08 14:05:25 +00:00
|
|
|
def memory_backup() -> AbstractBackupProvider:
|
2023-02-03 18:49:24 +00:00
|
|
|
ProviderClass = providers.get_provider(BackupProvider.MEMORY)
|
|
|
|
assert ProviderClass is not None
|
|
|
|
memory_provider = ProviderClass(login="", key="")
|
|
|
|
assert memory_provider is not None
|
|
|
|
return memory_provider
|
|
|
|
|
|
|
|
|
2023-02-20 11:50:52 +00:00
|
|
|
@pytest.fixture()
|
|
|
|
def file_backup(tmpdir) -> AbstractBackupProvider:
|
|
|
|
test_repo_path = path.join(tmpdir, "test_repo")
|
|
|
|
ProviderClass = providers.get_provider(BackupProvider.FILE)
|
|
|
|
assert ProviderClass is not None
|
|
|
|
provider = ProviderClass(test_repo_path)
|
|
|
|
assert provider is not None
|
|
|
|
return provider
|
|
|
|
|
|
|
|
|
2023-03-10 14:14:41 +00:00
|
|
|
def test_config_load(generic_userdata):
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.reset()
|
|
|
|
provider = Backups.provider()
|
2023-03-10 14:14:41 +00:00
|
|
|
|
|
|
|
assert provider is not None
|
|
|
|
assert isinstance(provider, Backblaze)
|
|
|
|
assert provider.login == "ID"
|
|
|
|
assert provider.key == "KEY"
|
|
|
|
|
|
|
|
|
2023-02-01 11:58:55 +00:00
|
|
|
def test_select_backend():
|
|
|
|
provider = providers.get_provider(BackupProvider.BACKBLAZE)
|
|
|
|
assert provider is not None
|
|
|
|
assert provider == Backblaze
|
2023-02-03 17:04:35 +00:00
|
|
|
|
|
|
|
|
2023-02-20 11:50:52 +00:00
|
|
|
def test_file_backend_init(file_backup):
|
|
|
|
file_backup.backuper.init("somerepo")
|
|
|
|
|
|
|
|
|
2023-02-20 16:09:01 +00:00
|
|
|
def test_backup_simple_file(raw_dummy_service, file_backup):
|
2023-02-03 17:04:35 +00:00
|
|
|
# temporarily incomplete
|
2023-02-20 16:09:01 +00:00
|
|
|
service = raw_dummy_service
|
|
|
|
assert service is not None
|
|
|
|
assert file_backup is not None
|
|
|
|
|
|
|
|
name = service.get_id()
|
|
|
|
file_backup.backuper.init(name)
|
2023-02-08 15:14:08 +00:00
|
|
|
|
|
|
|
|
2023-02-20 16:09:01 +00:00
|
|
|
def test_backup_service(dummy_service, backups):
|
2023-04-07 15:18:54 +00:00
|
|
|
assert Backups.get_last_backed_up(dummy_service) is None
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.back_up(dummy_service)
|
2023-02-13 11:16:35 +00:00
|
|
|
|
2023-04-07 15:18:54 +00:00
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
date = Backups.get_last_backed_up(dummy_service)
|
|
|
|
assert date is not None
|
|
|
|
assert now > date
|
|
|
|
assert now - date < timedelta(minutes=1)
|
|
|
|
|
2023-02-13 11:16:35 +00:00
|
|
|
|
2023-02-17 15:55:19 +00:00
|
|
|
def test_no_repo(memory_backup):
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
assert memory_backup.backuper.get_snapshots("") == []
|
|
|
|
|
|
|
|
|
2023-02-22 10:25:51 +00:00
|
|
|
def test_one_snapshot(backups, dummy_service):
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.back_up(dummy_service)
|
2023-02-22 13:35:55 +00:00
|
|
|
|
2023-03-29 11:15:38 +00:00
|
|
|
snaps = Backups.get_snapshots(dummy_service)
|
2023-02-22 13:35:55 +00:00
|
|
|
assert len(snaps) == 1
|
|
|
|
snap = snaps[0]
|
|
|
|
assert snap.service_name == dummy_service.get_id()
|
2023-02-22 15:58:36 +00:00
|
|
|
|
|
|
|
|
2023-04-03 17:23:16 +00:00
|
|
|
def test_backup_returns_snapshot(backups, dummy_service):
|
2023-04-14 10:18:21 +00:00
|
|
|
service_folder = dummy_service.get_drive()
|
2023-04-03 17:23:16 +00:00
|
|
|
provider = Backups.provider()
|
|
|
|
name = dummy_service.get_id()
|
|
|
|
snapshot = provider.backuper.start_backup(service_folder, name)
|
|
|
|
|
|
|
|
assert snapshot.id is not None
|
|
|
|
assert snapshot.service_name == name
|
|
|
|
assert snapshot.created_at is not None
|
|
|
|
|
|
|
|
|
2023-02-22 15:58:36 +00:00
|
|
|
def test_restore(backups, dummy_service):
|
2023-04-14 10:18:21 +00:00
|
|
|
service_folder = dummy_service.get_drive()
|
2023-02-22 15:58:36 +00:00
|
|
|
file_to_nuke = listdir(service_folder)[0]
|
|
|
|
assert file_to_nuke is not None
|
|
|
|
path_to_nuke = path.join(service_folder, file_to_nuke)
|
|
|
|
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.back_up(dummy_service)
|
|
|
|
snap = Backups.get_snapshots(dummy_service)[0]
|
2023-02-22 15:58:36 +00:00
|
|
|
assert snap is not None
|
|
|
|
|
|
|
|
assert path.exists(path_to_nuke)
|
|
|
|
remove(path_to_nuke)
|
|
|
|
assert not path.exists(path_to_nuke)
|
|
|
|
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.restore_service_from_snapshot(dummy_service, snap.id)
|
2023-02-22 15:58:36 +00:00
|
|
|
assert path.exists(path_to_nuke)
|
2023-02-22 18:48:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_sizing(backups, dummy_service):
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.back_up(dummy_service)
|
|
|
|
snap = Backups.get_snapshots(dummy_service)[0]
|
|
|
|
size = Backups.service_snapshot_size(dummy_service, snap.id)
|
2023-02-22 18:48:08 +00:00
|
|
|
assert size is not None
|
|
|
|
assert size > 0
|
2023-03-13 19:03:41 +00:00
|
|
|
|
|
|
|
|
2023-03-14 00:39:15 +00:00
|
|
|
def test_init_tracking(backups, raw_dummy_service):
|
2023-03-29 11:15:38 +00:00
|
|
|
assert Backups.is_initted(raw_dummy_service) is False
|
2023-03-14 00:39:15 +00:00
|
|
|
|
2023-03-29 11:15:38 +00:00
|
|
|
Backups.init_repo(raw_dummy_service)
|
2023-03-14 00:39:15 +00:00
|
|
|
|
2023-03-29 11:15:38 +00:00
|
|
|
assert Backups.is_initted(raw_dummy_service) is True
|
2023-03-29 11:45:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_backup_service_task(backups, dummy_service):
|
|
|
|
handle = start_backup(dummy_service)
|
|
|
|
handle(blocking=True)
|
|
|
|
|
|
|
|
snaps = Backups.get_snapshots(dummy_service)
|
|
|
|
assert len(snaps) == 1
|
2023-04-03 21:59:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_autobackup_enable_service(backups, dummy_service):
|
|
|
|
assert not Backups.is_autobackup_enabled(dummy_service)
|
|
|
|
|
|
|
|
Backups.enable_autobackup(dummy_service)
|
|
|
|
assert Backups.is_autobackup_enabled(dummy_service)
|
|
|
|
|
|
|
|
Backups.disable_autobackup(dummy_service)
|
|
|
|
assert not Backups.is_autobackup_enabled(dummy_service)
|
2023-04-03 22:39:04 +00:00
|
|
|
|
|
|
|
|
2023-04-10 15:51:54 +00:00
|
|
|
def test_autobackup_enable_service_storage(backups, dummy_service):
|
|
|
|
assert len(Storage.services_with_autobackup()) == 0
|
|
|
|
|
|
|
|
Backups.enable_autobackup(dummy_service)
|
|
|
|
assert len(Storage.services_with_autobackup()) == 1
|
|
|
|
assert Storage.services_with_autobackup()[0] == dummy_service.get_id()
|
|
|
|
|
|
|
|
Backups.disable_autobackup(dummy_service)
|
|
|
|
assert len(Storage.services_with_autobackup()) == 0
|
|
|
|
|
|
|
|
|
2023-04-03 22:39:04 +00:00
|
|
|
def test_set_autobackup_period(backups):
|
|
|
|
assert Backups.autobackup_period_minutes() is None
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(2)
|
|
|
|
assert Backups.autobackup_period_minutes() == 2
|
|
|
|
|
|
|
|
Backups.disable_all_autobackup()
|
|
|
|
assert Backups.autobackup_period_minutes() is None
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(3)
|
|
|
|
assert Backups.autobackup_period_minutes() == 3
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(0)
|
|
|
|
assert Backups.autobackup_period_minutes() is None
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(3)
|
|
|
|
assert Backups.autobackup_period_minutes() == 3
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(-1)
|
|
|
|
assert Backups.autobackup_period_minutes() is None
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
|
2023-04-10 15:51:54 +00:00
|
|
|
def test_no_default_autobackup(backups, dummy_service):
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert not Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
|
|
|
|
def test_autobackup_timer_periods(backups, dummy_service):
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
backup_period = 13 # minutes
|
|
|
|
|
|
|
|
Backups.enable_autobackup(dummy_service)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert not Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(backup_period)
|
|
|
|
assert Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(0)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert not Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
|
|
|
|
def test_autobackup_timer_enabling(backups, dummy_service):
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
backup_period = 13 # minutes
|
|
|
|
|
|
|
|
Backups.set_autobackup_period_minutes(backup_period)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert not Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
Backups.enable_autobackup(dummy_service)
|
|
|
|
assert Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
Backups.disable_autobackup(dummy_service)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert not Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
|
|
|
|
def test_autobackup_timing(backups, dummy_service):
|
|
|
|
backup_period = 13 # minutes
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
|
|
|
|
Backups.enable_autobackup(dummy_service)
|
|
|
|
Backups.set_autobackup_period_minutes(backup_period)
|
|
|
|
assert Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
Backups.back_up(dummy_service)
|
|
|
|
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), now)
|
|
|
|
assert not Backups.is_time_to_backup(now)
|
|
|
|
|
|
|
|
past = datetime.now(timezone.utc) - timedelta(minutes=1)
|
|
|
|
assert not Backups.is_time_to_backup_service(dummy_service.get_id(), past)
|
|
|
|
assert not Backups.is_time_to_backup(past)
|
|
|
|
|
|
|
|
future = datetime.now(timezone.utc) + timedelta(minutes=backup_period + 2)
|
|
|
|
assert Backups.is_time_to_backup_service(dummy_service.get_id(), future)
|
|
|
|
assert Backups.is_time_to_backup(future)
|
|
|
|
|
|
|
|
|
2023-04-10 13:22:33 +00:00
|
|
|
# Storage
|
|
|
|
def test_snapshots_caching(backups, dummy_service):
|
|
|
|
Backups.back_up(dummy_service)
|
|
|
|
|
|
|
|
# we test indirectly that we do redis calls instead of shell calls
|
|
|
|
start = datetime.now()
|
|
|
|
for i in range(10):
|
|
|
|
snapshots = Backups.get_snapshots(dummy_service)
|
|
|
|
assert len(snapshots) == 1
|
|
|
|
assert datetime.now() - start < timedelta(seconds=0.5)
|
|
|
|
|
|
|
|
cached_snapshots = Storage.get_cached_snapshots()
|
|
|
|
assert len(cached_snapshots) == 1
|
|
|
|
|
|
|
|
Storage.delete_cached_snapshot(cached_snapshots[0])
|
|
|
|
cached_snapshots = Storage.get_cached_snapshots()
|
|
|
|
assert len(cached_snapshots) == 0
|
|
|
|
|
|
|
|
snapshots = Backups.get_snapshots(dummy_service)
|
|
|
|
assert len(snapshots) == 1
|
|
|
|
cached_snapshots = Storage.get_cached_snapshots()
|
|
|
|
assert len(cached_snapshots) == 1
|
|
|
|
|
|
|
|
|
|
|
|
# Storage
|
|
|
|
def test_init_tracking_caching(backups, raw_dummy_service):
|
|
|
|
assert Storage.has_init_mark(raw_dummy_service) is False
|
|
|
|
|
|
|
|
Storage.mark_as_init(raw_dummy_service)
|
|
|
|
|
|
|
|
assert Storage.has_init_mark(raw_dummy_service) is True
|
|
|
|
assert Backups.is_initted(raw_dummy_service) is True
|
|
|
|
|
|
|
|
|
|
|
|
# Storage
|
|
|
|
def test_init_tracking_caching2(backups, raw_dummy_service):
|
|
|
|
assert Storage.has_init_mark(raw_dummy_service) is False
|
|
|
|
|
|
|
|
Backups.init_repo(raw_dummy_service)
|
|
|
|
|
|
|
|
assert Storage.has_init_mark(raw_dummy_service) is True
|
|
|
|
|
|
|
|
|
|
|
|
# Storage
|
|
|
|
def test_provider_storage(backups_backblaze):
|
|
|
|
Backups.reset()
|
|
|
|
provider = Backups.provider()
|
|
|
|
|
|
|
|
assert provider is not None
|
|
|
|
|
|
|
|
assert isinstance(provider, Backblaze)
|
|
|
|
assert provider.login == "ID"
|
|
|
|
assert provider.key == "KEY"
|
|
|
|
|
|
|
|
Storage.store_provider(provider)
|
|
|
|
restored_provider = Backups.load_provider_redis()
|
|
|
|
assert isinstance(restored_provider, Backblaze)
|
|
|
|
assert restored_provider.login == "ID"
|
|
|
|
assert restored_provider.key == "KEY"
|
2023-04-12 17:18:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_services_to_back_up(backups, dummy_service):
|
|
|
|
backup_period = 13 # minutes
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
|
|
|
|
Backups.enable_autobackup(dummy_service)
|
|
|
|
Backups.set_autobackup_period_minutes(backup_period)
|
|
|
|
|
|
|
|
services = Backups.services_to_back_up(now)
|
|
|
|
assert len(services) == 1
|
|
|
|
assert services[0].get_id() == dummy_service.get_id()
|