async def test_generational_delete(time, model: Model, dest, source, simple_config): time.setNow(time.local(2019, 5, 10)) now = time.now() # Create 4 backups, configured to keep 3 source.setMax(3) source.insert("Fri", time.local(2019, 5, 10, 1)) source.insert("Thu", time.local(2019, 5, 9, 1)) wed = source.insert("Wed", time.local(2019, 5, 8, 1)) source.insert("Mon", time.local(2019, 5, 6, 1)) # configure only one to be kept in both places simple_config.config.update({ "days_between_backups": 1, "generational_weeks": 1, "generational_days": 2 }) model.reinitialize() await model.sync(now) # Shoud only delete wed, since it isn't kept in the generational backup config source.assertThat(current=3, deleted=1) assert source.deleted == [wed] assert len(model.backups) == 3 dest.assertThat(current=3, saved=3)
async def test_never_delete_ignored_snapshots(time: FakeTime, model: Model, dest: HelperTestSource, source: HelperTestSource): source.setMax(1) dest.setMax(1) # A sync should create a snapshot and back it up to dest. await model.sync(time.now()) source.assertThat(created=1, current=1) dest.assertThat(saved=1, current=1) source.reset() dest.reset() # Another sync shoudl delete a snapshot, which is just a sanity check. time.advance(days=5) await model.sync(time.now()) source.assertThat(created=1, current=1, deleted=1) dest.assertThat(saved=1, current=1, deleted=1) assert model.nextSnapshot(time.now()) == time.now() + timedelta(days=3) source.reset() dest.reset() # Make the snapshot ignored, which should cause a new snapshot to be created # and synced without the ignored one getting deleted. next(iter((await dest.get()).values())).setIgnore(True) next(iter((await source.get()).values())).setIgnore(True) assert model.nextSnapshot(time.now()) < time.now() await model.sync(time.now()) source.assertThat(created=1, current=2) dest.assertThat(saved=1, current=2)
async def test_ignore_startup_delay(time: FakeTime, model: Model, dest: HelperTestSource, source: HelperTestSource, global_info: GlobalInfo): time.setNow(time.local(2019, 5, 10)) global_info.triggerSnapshotCooldown(timedelta(minutes=10)) model.ignore_startup_delay = True assert model.nextSnapshot(time.now()) == time.now() - timedelta(minutes=1) assert not model.waiting_for_startup
async def test_wait_for_startup_no_snapshot(time: FakeTime, model: Model, dest: HelperTestSource, source: HelperTestSource, global_info: GlobalInfo): time.setNow(time.local(2019, 5, 10)) global_info.triggerSnapshotCooldown(timedelta(minutes=10)) assert model.nextSnapshot(time.now()) == time.now() + timedelta(minutes=10) assert model.nextSnapshot(time.now()) == global_info.snapshotCooldownTime() assert model.waiting_for_startup time.advance(minutes=10) assert model.nextSnapshot(time.now()) == time.now() - timedelta(minutes=1) assert not model.waiting_for_startup
async def test_generational_empty(time, model: Model, dest, source, simple_config: Config): time.setNow(time.local(2019, 5, 10)) now = time.now() simple_config.config.update({ "days_between_backups": 1, "generational_weeks": 1, "generational_days": 2 }) simple_config.override(Setting.DAYS_BETWEEN_BACKUPS, 1) model.reinitialize() assert len(model.backups) == 0 await model.sync(now) assert len(model.backups) == 1
async def test_wait_for_startup_with_backup(time: FakeTime, model: Model, dest: HelperTestSource, source: HelperTestSource, global_info: GlobalInfo): time.setNow(time.local(2019, 5, 10)) global_info.triggerBackupCooldown(timedelta(minutes=10)) source.setMax(3) source.insert("old", time.now() - timedelta(days=7)) assert model.nextBackup(time.now()) == time.now() + timedelta(minutes=10) assert model.nextBackup(time.now()) == global_info.backupCooldownTime() assert model.waiting_for_startup time.advance(minutes=10) assert model.nextBackup(time.now()) == time.now() - timedelta(minutes=1) assert not model.waiting_for_startup
async def test_alternate_timezone(coord: Coordinator, time: FakeTime, model: Model, dest, source, simple_config: Config): time.setTimeZone("Europe/Stockholm") simple_config.override(Setting.BACKUP_TIME_OF_DAY, "12:00") simple_config.override(Setting.DAYS_BETWEEN_BACKUPS, 1) source.setMax(10) source.insert("Fri", time.toUtc(time.local(2020, 3, 16, 18, 5))) time.setNow(time.local(2020, 3, 16, 18, 6)) model.reinitialize() coord.reset() await coord.sync() assert not coord.check() assert coord.nextBackupTime() == time.local(2020, 3, 17, 12) time.setNow(time.local(2020, 3, 17, 11, 59)) await coord.sync() assert not coord.check() time.setNow(time.local(2020, 3, 17, 12)) assert coord.check()
def test_next_time_of_day(estimator, data_cache): time: FakeTime = FakeTime() now: datetime = datetime(1985, 12, 6, 1, 0, 0).astimezone(timezone.utc) time.setNow(now) info = GlobalInfo(time) config: Config = createConfig().override(Setting.DAYS_BETWEEN_BACKUPS, 1).override( Setting.BACKUP_TIME_OF_DAY, '08:00') model: Model = Model(config, time, default_source, default_source, info, estimator, data_cache) assert model._nextBackup(now=now, last_backup=None) == now - timedelta(minutes=1) assert model._nextBackup(now=now, last_backup=now - timedelta(days=1)) == datetime(1985, 12, 5, 8, 0, tzinfo=test_tz) assert model._nextBackup(now=now, last_backup=now) == datetime(1985, 12, 6, 8, 0, tzinfo=test_tz) assert model._nextBackup(now=now, last_backup=datetime(1985, 12, 6, 8, 0, tzinfo=test_tz)) == datetime( 1985, 12, 7, 8, 0, tzinfo=test_tz) assert model._nextBackup(now=datetime(1985, 12, 6, 8, 0, tzinfo=test_tz), last_backup=datetime(1985, 12, 6, 8, 0, tzinfo=test_tz)) == datetime( 1985, 12, 7, 8, 0, tzinfo=test_tz)
async def test_new_upload_no_delete(time, model: Model, source, dest, simple_config): now = time.now() # create a single old backup source.setMax(2) dest.setMax(2) backup_dest = dest.insert("older", now - timedelta(days=1), "older") backup_source = source.insert("older", now - timedelta(days=1), "older") # configure keeping two in both places simple_config.config.update({"days_between_backups": 1}) model.reinitialize() await model.sync(now) # Another backup should have been created and saved source.assertThat(current=2, created=1) dest.assertThat(current=2, saved=1) assert len(model.backups) == 2 assertBackup(model, [source.created[0], dest.saved[0]]) assertBackup(model, [backup_dest, backup_source])
def test_next_time(estimator, data_cache): time: FakeTime = FakeTime() now: datetime = datetime(1985, 12, 6, 1, 0, 0).astimezone(timezone.utc) time.setNow(now) info = GlobalInfo(time) config: Config = createConfig().override(Setting.DAYS_BETWEEN_SNAPSHOTS, 0) model: Model = Model(config, time, default_source, default_source, info, estimator, data_cache) assert model._nextSnapshot(now=now, last_snapshot=None) is None assert model._nextSnapshot(now=now, last_snapshot=now) is None config: Config = createConfig().override(Setting.DAYS_BETWEEN_SNAPSHOTS, 1) model: Model = Model(config, time, default_source, default_source, info, estimator, data_cache) assert model._nextSnapshot( now=now, last_snapshot=None) == now - timedelta(minutes=1) assert model._nextSnapshot(now=now, last_snapshot=now) == now + timedelta(days=1) assert model._nextSnapshot(now=now, last_snapshot=now - timedelta(days=1)) == now assert model._nextSnapshot(now=now, last_snapshot=now + timedelta(days=1)) == now + timedelta(days=2)
async def test_new_upload_with_delete(time, model: Model, source, dest, simple_config): now = time.now() # create a single old backups source.setMax(1) dest.setMax(1) backup_dest = dest.insert("older", now - timedelta(days=1), "older") backup_source = source.insert("older", now - timedelta(days=1), "older") # configure only one to be kept in both places simple_config.config.update({"days_between_backups": 1}) model.reinitialize() await model.sync(now) # Old snapshto shoudl be deleted, new one shoudl be created and uploaded. source.assertThat(current=1, created=1, deleted=1) dest.assertThat(current=1, saved=1, deleted=1) assert dest.deleted == [backup_dest] assert source.deleted == [backup_source] assert len(model.backups) == 1 assertBackup(model, [source.created[0], dest.saved[0]])
def test_next_time_of_day_drift(estimator): time: FakeTime = FakeTime() info = GlobalInfo(time) now: datetime = datetime(1985, 12, 6, 1, 0, 0).astimezone(timezone.utc) config: Config = Config().override(Setting.DAYS_BETWEEN_SNAPSHOTS, 1).override( Setting.SNAPSHOT_TIME_OF_DAY, '08:00') model: Model = Model(config, time, default_source, default_source, info, estimator) assert model._nextSnapshot( now=now, last_snapshot=None) == now - timedelta(minutes=1) assert model._nextSnapshot( now=now, last_snapshot=now - timedelta(days=1) + timedelta(minutes=1)) == now
async def test_multiple_deletes_allowed(time, model: Model, source, dest, simple_config): now = time.now() simple_config.config.update({"confirm_multiple_deletes": False}) # create 4 backups in dest dest.setMax(1) current = dest.insert("current", now, "current") old = dest.insert("old", now - timedelta(days=1), "old") older = dest.insert("older", now - timedelta(days=2), "older") oldest = dest.insert("oldest", now - timedelta(days=3), "oldest") # configure keeping 1 simple_config.config.update({ "max_backups_in_google_drive": 1, }) model.reinitialize() await model.sync(now) source.assertUnchanged() dest.assertThat(current=1, deleted=3) assert dest.deleted == [oldest, older, old] assert len(model.backups) == 1 assertBackup(model, [current])
async def test_dont_delete_purgable(time, model: Model, source, dest, simple_config): now = time.now() # create a single old backup, retained source.setMax(1) dest.setMax(1) backup_dest = dest.insert("older", now - timedelta(days=1), "older") backup_dest.setRetained(True) backup_source = source.insert("older", now - timedelta(days=1), "older") backup_source.setRetained(True) # configure only one to be kept in both places simple_config.config.update({"days_between_backups": 1}) model.reinitialize() await model.sync(now) # Old snapshto shoudl be kept, new one should be created and uploaded. source.assertThat(current=2, created=1) dest.assertThat(current=2, saved=1) assert len(model.backups) == 2 assertBackup(model, [backup_dest, backup_source]) assertBackup(model, [source.created[0], dest.saved[0]])
def test_timeOfDay(estimator, model: Model) -> None: assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '00:00') model.reinitialize() assert model.getTimeOfDay() == (0, 0) model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '23:59') model.reinitialize() assert model.getTimeOfDay() == (23, 59) model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:59') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:60') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '-1:60') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:-1') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, 'boop:60') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:boop') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:10:22') model.reinitialize() assert model.getTimeOfDay() is None model.config.override(Setting.SNAPSHOT_TIME_OF_DAY, '10') model.reinitialize() assert model.getTimeOfDay() is None
def model(source, dest, time, simple_config, global_info, estimator, data_cache): return Model(simple_config, time, source, dest, global_info, estimator, data_cache)
def test_timeOfDay(estimator) -> None: time: FakeTime = FakeTime() info = GlobalInfo(time) config: Config = Config() model: Model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config = Config().override(Setting.SNAPSHOT_TIME_OF_DAY, '00:00') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() == (0, 0) config.override(Setting.SNAPSHOT_TIME_OF_DAY, '23:59') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() == (23, 59) config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:59') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:60') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, '-1:60') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:-1') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, 'boop:60') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:boop') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, '24:10:22') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None config.override(Setting.SNAPSHOT_TIME_OF_DAY, '10') model = Model(config, time, default_source, default_source, info, estimator) assert model.getTimeOfDay() is None