async def test_dont_purge_pending_backup(ha: HaSource, time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor): config.override(Setting.MAX_BACKUPS_IN_HA, 4) await ha.create(CreateOptions(time.now(), "Test Name 1")) await ha.create(CreateOptions(time.now(), "Test Name 2")) await ha.create(CreateOptions(time.now(), "Test Name 3")) await ha.create(CreateOptions(time.now(), "Test Name 4")) await model.sync(time.now()) config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0.1) interceptor.setSleep(URL_MATCH_BACKUP_FULL, sleep=2) await ha.create(CreateOptions(time.now(), "Test Name")) backups = list((await ha.get()).values()) assert len(backups) == 5 backup = backups[4] assert isinstance(backup, PendingBackup) # no backup should get purged yet because the ending backup isn't considered for purging. await model.sync(time.now()) backups = list((await ha.get()).values()) assert len(backups) == 5 # Wait for the backup to finish, then verify one gets purged. await ha._pending_backup_task await model.sync(time.now()) backups = list((await ha.get()).values()) assert len(backups) == 4
async def test_partial_snapshot(ha, time, server, config: Config): config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 100) for folder in all_folders: config.override(Setting.EXCLUDE_FOLDERS, folder) snapshot: HASnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert snapshot.snapshotType() == "partial" for search in all_folders: if search == folder: assert search not in snapshot.details()['folders'] else: assert search in snapshot.details()['folders'] for addon in all_addons: config.override(Setting.EXCLUDE_ADDONS, addon['slug']) snapshot: HASnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert snapshot.snapshotType() == "partial" list_of_addons = [] for included in snapshot.details()['addons']: list_of_addons.append(included['slug']) for search in list_of_addons: if search == addon: assert search not in list_of_addons else: assert search in list_of_addons # excluding addon/folders that don't exist should actually make a full snapshot config.override(Setting.EXCLUDE_ADDONS, "none,of.these,are.addons") config.override(Setting.EXCLUDE_FOLDERS, "not,folders,either") snapshot: HASnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert snapshot.snapshotType() == "full"
async def test_failed_backup(time, ha: HaSource, supervisor: SimulatedSupervisor, config: Config, interceptor: RequestInterceptor): # create a blocking backup interceptor.setError(URL_MATCH_BACKUP_FULL, 524) config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 0) await supervisor.toggleBlockBackup() backup_immediate = await ha.create(CreateOptions(time.now(), "Some Name")) assert isinstance(backup_immediate, PendingBackup) assert backup_immediate.name() == "Some Name" assert not ha.check() assert not backup_immediate.isFailed() await supervisor.toggleBlockBackup() # let the backup attempt to complete await asyncio.wait({ha._pending_backup_task}) # verify it failed with the expected http error assert backup_immediate.isFailed() assert backup_immediate._exception.status == 524 backups = list((await ha.get()).values()) assert len(backups) == 1 assert backups[0] is backup_immediate # verify we can create a new backup immediately interceptor.clear() await ha.create(CreateOptions(time.now(), "Some Name")) assert len(await ha.get()) == 1
async def test_failed_snapshot(time, ha: HaSource, supervisor: SimulatedSupervisor, config: Config, interceptor: RequestInterceptor): # create a blocking snapshot interceptor.setError(URL_MATCH_SNAPSHOT_FULL, 524) config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 0) await supervisor.toggleBlockSnapshot() snapshot_immediate = await ha.create(CreateOptions(time.now(), "Some Name")) assert isinstance(snapshot_immediate, PendingSnapshot) assert snapshot_immediate.name() == "Some Name" assert not ha.check() assert not snapshot_immediate.isFailed() await supervisor.toggleBlockSnapshot() # let the snapshot attempt to complete await asyncio.wait({ha._pending_snapshot_task}) # verify it failed with the expected http error assert snapshot_immediate.isFailed() assert snapshot_immediate._exception.status == 524 snapshots = list((await ha.get()).values()) assert len(snapshots) == 1 assert snapshots[0] is snapshot_immediate # verify we can create a new snapshot immediately interceptor.clear() await ha.create(CreateOptions(time.now(), "Some Name")) assert len(await ha.get()) == 1
async def test_dont_purge_pending_snapshot(ha: HaSource, time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor): config.override(Setting.MAX_SNAPSHOTS_IN_HASSIO, 4) await ha.create(CreateOptions(time.now(), "Test Name 1")) await ha.create(CreateOptions(time.now(), "Test Name 2")) await ha.create(CreateOptions(time.now(), "Test Name 3")) await ha.create(CreateOptions(time.now(), "Test Name 4")) await model.sync(time.now()) config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 0.1) interceptor.setSleep(URL_MATCH_SNAPSHOT_FULL, sleep=2) await ha.create(CreateOptions(time.now(), "Test Name")) snapshots = list((await ha.get()).values()) assert len(snapshots) == 5 snapshot = snapshots[4] assert isinstance(snapshot, PendingSnapshot) # no snapshot should get purged yet because the ending snapshot isn't considered for purging. await model.sync(time.now()) snapshots = list((await ha.get()).values()) assert len(snapshots) == 5 # Wait for the snapshot to finish, then verify one gets purged. await ha._pending_snapshot_task await model.sync(time.now()) snapshots = list((await ha.get()).values()) assert len(snapshots) == 4
async def test_sync_different_sources(model, time, source, dest): snapshot_source = await source.create(CreateOptions(time.now(), "name")) snapshot_dest = await dest.create(CreateOptions(time.now(), "name")) await model._syncSnapshots([source, dest]) assert len(model.snapshots) == 2 assert model.snapshots[snapshot_source.slug()].getSource( source.name()) is snapshot_source assert model.snapshots[snapshot_dest.slug()].getSource( dest.name()) is snapshot_dest
async def test_sync_different_sources(model: Model, time, source, dest): backup_source = await source.create(CreateOptions(time.now(), "name")) backup_dest = await dest.create(CreateOptions(time.now(), "name")) await model._syncBackups([source, dest]) assert len(model.backups) == 2 assert model.backups[backup_source.slug()].getSource( source.name()) is backup_source assert model.backups[backup_dest.slug()].getSource( dest.name()) is backup_dest
async def test_pending_snapshot_timeout(time: FakeTime, ha: HaSource, config: Config, interceptor: RequestInterceptor): interceptor.setSleep(URL_MATCH_SNAPSHOT_FULL, sleep=5) config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 1) config.override(Setting.FAILED_SNAPSHOT_TIMEOUT_SECONDS, 1) config.override(Setting.PENDING_SNAPSHOT_TIMEOUT_SECONDS, 1) snapshot_immediate: PendingSnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert isinstance(snapshot_immediate, PendingSnapshot) assert snapshot_immediate.name() == "Test Name" assert not ha.check() assert ha.pending_snapshot is snapshot_immediate await asyncio.wait({ha._pending_snapshot_task}) assert ha.pending_snapshot is snapshot_immediate assert ha.check() assert not ha.check() time.advance(minutes=1) assert ha.check() assert len(await ha.get()) == 0 assert not ha.check() assert ha.pending_snapshot is None assert snapshot_immediate.isStale()
async def test_pending_snapshot_replaces_original( time, ha: HaSource, config: Config, supervisor: SimulatedSupervisor): # now configure a snapshto to start outside of the addon config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 100) await supervisor.toggleBlockSnapshot() with pytest.raises(SnapshotInProgress): await ha.create(CreateOptions(time.now(), "Ignored")) snapshot_immediate = (await ha.get())['pending'] await supervisor.toggleBlockSnapshot() assert isinstance(snapshot_immediate, PendingSnapshot) assert snapshot_immediate.name() == "Pending Snapshot" assert ha.check() assert ha.pending_snapshot is snapshot_immediate assert await ha.get() == {snapshot_immediate.slug(): snapshot_immediate} # create a new snapshot behind the scenes, the pending snapshot should get replaced with the new one slug = (await ha.harequests.createSnapshot({ 'name': "Suddenly Appears", "hardlock": True }))['slug'] results = await ha.get() assert len(results) == 1 assert slug in results assert results[slug].name() == "Suddenly Appears" assert not results[slug].retained()
async def test_failed_snapshot_retry(ha: HaSource, time: FakeTime, config: Config, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor): # create a blocking snapshot interceptor.setError(URL_MATCH_SNAPSHOT_FULL, 524) config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 0) await supervisor.toggleBlockSnapshot() snapshot_immediate = await ha.create(CreateOptions(time.now(), "Some Name")) assert isinstance(snapshot_immediate, PendingSnapshot) assert snapshot_immediate.name() == "Some Name" assert not ha.check() assert not snapshot_immediate.isFailed() await supervisor.toggleBlockSnapshot() # let the snapshot attempt to complete await asyncio.wait({ha._pending_snapshot_task}) # verify it failed with the expected http error assert snapshot_immediate.isFailed() assert snapshot_immediate._exception.status == 524 assert ha.check() assert not ha.check() time.advance(seconds=config.get(Setting.FAILED_SNAPSHOT_TIMEOUT_SECONDS)) # should trigger a sync after the failed snapshot timeout assert ha.check() await ha.get() assert not ha.check()
async def test_new_backup(coord: Coordinator, time: FakeTime, source, dest): await coord.startBackup(CreateOptions(time.now(), "Test Name")) backups = coord.backups() assert len(backups) == 1 assert backups[0].name() == "Test Name" assert backups[0].getSource(source.name()) is not None assert backups[0].getSource(dest.name()) is None
async def test_start_on_boot(ha: HaSource, time, interceptor: RequestInterceptor, config: Config, supervisor: SimulatedSupervisor) -> None: boot_slug = "boot_slug" supervisor.installAddon(boot_slug, "Start on boot", boot=True, started=False) no_boot_slug = "no_boot_slug" supervisor.installAddon(no_boot_slug, "Don't start on boot", boot=False, started=False) config.override(Setting.STOP_ADDONS, ",".join([boot_slug, no_boot_slug])) config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 0.001) assert supervisor.addon(boot_slug)["state"] == "stopped" assert supervisor.addon(no_boot_slug)["state"] == "stopped" async with supervisor._snapshot_inner_lock: await ha.create(CreateOptions(time.now(), "Test Name")) assert supervisor.addon(boot_slug)["state"] == "stopped" assert supervisor.addon(no_boot_slug)["state"] == "stopped" await ha._pending_snapshot_task assert supervisor.addon(boot_slug)["state"] == "started" assert supervisor.addon(no_boot_slug)["state"] == "stopped" assert len(await ha.get()) == 1 assert not interceptor.urlWasCalled(URL_MATCH_START_ADDON) assert not interceptor.urlWasCalled(URL_MATCH_STOP_ADDON)
async def test_new_snapshot(coord: Coordinator, time: FakeTime, source, dest): await coord.startSnapshot(CreateOptions(time.now(), "Test Name")) snapshots = coord.snapshots() assert len(snapshots) == 1 assert snapshots[0].name() == "Test Name" assert snapshots[0].getSource(source.name()) is not None assert snapshots[0].getSource(dest.name()) is None
async def test_removal(model, time, source, dest): await source.create(CreateOptions(time.now(), "name")) await model._syncSnapshots([source, dest]) assert len(model.snapshots) == 1 source.current = {} await model._syncSnapshots([source, dest]) assert len(model.snapshots) == 0
async def test_sync_single_source(model: Model, source, dest, time): backup = await source.create(CreateOptions(time.now(), "name")) dest.setEnabled(False) await model.sync(time.now()) assert len(model.backups) == 1 assert backup.slug() in model.backups assert model.backups[backup.slug()].getSource(source.name()) is backup assert model.backups[backup.slug()].getSource(dest.name()) is None
async def test_check_size_new_snapshot(coord: Coordinator, source: HelperTestSource, dest: HelperTestSource, time, fs: FsFaker): skipForWindows() fs.setFreeBytes(0) with raises(LowSpaceError): await coord.startSnapshot(CreateOptions(time.now(), "Test Name"))
async def test_sync_single_source(model, source, dest, time): snapshot = await source.create(CreateOptions(time.now(), "name")) dest.setEnabled(False) await model.sync(time.now()) assert len(model.snapshots) == 1 assert snapshot.slug() in model.snapshots assert model.snapshots[snapshot.slug()].getSource( source.name()) is snapshot assert model.snapshots[snapshot.slug()].getSource(dest.name()) is None
async def test_CRUD(ha: HaSource, time, interceptor: RequestInterceptor, data_cache: DataCache) -> None: snapshot: HASnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert snapshot.name() == "Test Name" assert type(snapshot) is HASnapshot assert not snapshot.retained() assert snapshot.snapshotType() == "full" assert not snapshot.protected() assert snapshot.name() == "Test Name" assert snapshot.source() == SOURCE_HA assert not snapshot.ignore() assert snapshot.madeByTheAddon() assert "pending" not in data_cache.snapshots # read the item directly, its metadata should match from_ha = await ha.harequests.snapshot(snapshot.slug()) assert from_ha.size() == snapshot.size() assert from_ha.slug() == snapshot.slug() assert from_ha.source() == SOURCE_HA snapshots = await ha.get() assert len(snapshots) == 1 assert snapshot.slug() in snapshots full = DummySnapshot(from_ha.name(), from_ha.date(), from_ha.size(), from_ha.slug(), "dummy") full.addSource(snapshot) # download the item, its bytes should match up download = await ha.read(full) await download.setup() direct_download = await ha.harequests.download(snapshot.slug()) await direct_download.setup() while True: from_file = await direct_download.read(1024 * 1024) from_download = await download.read(1024 * 1024) if len(from_file.getbuffer()) == 0: assert len(from_download.getbuffer()) == 0 break assert from_file.getbuffer() == from_download.getbuffer() # update retention assert not snapshot.retained() await ha.retain(full, True) assert (await ha.get())[full.slug()].retained() await ha.retain(full, False) assert not (await ha.get())[full.slug()].retained() # Delete the item, make sure its gone await ha.delete(full) assert full.getSource(ha.name()) is None snapshots = await ha.get() assert len(snapshots) == 0
async def sync(self, now: datetime): if self.simulate_error is not None: if self.simulate_error.startswith("test"): raise Exception(self.simulate_error) else: raise SimulatedError(self.simulate_error) await self._syncBackups([self.source, self.dest]) self.source.checkBeforeChanges() self.dest.checkBeforeChanges() if not self.dest.needsConfiguration(): if self.source.enabled(): await self._purge(self.source) if self.dest.enabled(): await self._purge(self.dest) self._handleBackupDetails() next_backup = self.nextBackup(now) if next_backup and now >= next_backup and self.source.enabled() and not self.dest.needsConfiguration(): if self.config.get(Setting.DELETE_BEFORE_NEW_BACKUP): await self._purge(self.source, pre_purge=True) await self.createBackup(CreateOptions(now, self.config.get(Setting.BACKUP_NAME))) await self._purge(self.source) self._handleBackupDetails() if self.dest.enabled() and self.dest.upload(): # get the backups we should upload uploads = [] for backup in self.backups.values(): if backup.getSource(self.source.name()) is not None and backup.getSource(self.source.name()).uploadable() and backup.getSource(self.dest.name()) is None and not backup.ignore(): uploads.append(backup) uploads.sort(key=lambda s: s.date()) uploads.reverse() for upload in uploads: # only upload if doing so won't result in it being deleted next dummy = DummyBackup( "", upload.date(), self.dest.name(), "dummy_slug_name") proposed = list(self.backups.values()) proposed.append(dummy) if self._nextPurge(self.dest, proposed) != dummy: if self.config.get(Setting.DELETE_BEFORE_NEW_BACKUP): await self._purge(self.dest, pre_purge=True) upload.addSource(await self.dest.save(upload, await self.source.read(upload))) await self._purge(self.dest) self._handleBackupDetails() else: break if self.config.get(Setting.DELETE_AFTER_UPLOAD): await self._purge(self.source) self._handleBackupDetails() self.source.postSync() self.dest.postSync() self._data_cache.saveIfDirty()
async def test_delete_error(time, ha: HaSource, interceptor: RequestInterceptor): snapshot = await ha.create(CreateOptions(time.now(), "Some Name")) full = DummySnapshot(snapshot.name(), snapshot.date(), snapshot.size(), snapshot.slug(), "dummy") full.addSource(snapshot) interceptor.setError(URL_MATCH_SNAPSHOT_DELETE, 400) with pytest.raises(HomeAssistantDeleteError): await ha.delete(full) interceptor.clear() await ha.delete(full)
async def test_delete_error(time, ha: HaSource, interceptor: RequestInterceptor): backup = await ha.create(CreateOptions(time.now(), "Some Name")) full = DummyBackup(backup.name(), backup.date(), backup.size(), backup.slug(), "dummy") full.addSource(backup) interceptor.setError(URL_MATCH_BACKUP_DELETE, 400) with pytest.raises(HomeAssistantDeleteError): await ha.delete(full) interceptor.clear() await ha.delete(full)
async def test_old_delete_path(ha: HaSource, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, time: FakeTime): supervisor._super_version = Version(2020, 8) await ha.get() backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name")) full = DummyBackup(backup.name(), backup.date(), backup.size(), backup.slug(), "dummy") full.addSource(backup) await ha.delete(full) assert interceptor.urlWasCalled("/snapshots/{0}/remove".format( backup.slug()))
async def snapshot(self, request: Request) -> Any: custom_name = request.query.get("custom_name", None) retain_drive = BoolValidator.strToBool( request.query.get("retain_drive", False)) retain_ha = BoolValidator.strToBool( request.query.get("retain_ha", False)) options = CreateOptions(self._time.now(), custom_name, { SOURCE_GOOGLE_DRIVE: retain_drive, SOURCE_HA: retain_ha }) snapshot = await self._coord.startSnapshot(options) return web.json_response({"message": "Requested snapshot '{0}'".format(snapshot.name())})
async def test_pending_snapshot_nowait(ha: HaSource, time, supervisor: SimulatedSupervisor, interceptor: RequestInterceptor, config: Config): interceptor.setSleep(URL_MATCH_SNAPSHOT_FULL, sleep=5) config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 0.1) snapshot_immediate: PendingSnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert isinstance(snapshot_immediate, PendingSnapshot) snapshot_pending: HASnapshot = (await ha.get())['pending'] assert isinstance(snapshot_immediate, PendingSnapshot) assert isinstance(snapshot_pending, PendingSnapshot) assert snapshot_immediate is snapshot_pending assert snapshot_immediate.name() == "Test Name" assert snapshot_immediate.slug() == "pending" assert not snapshot_immediate.uploadable() assert snapshot_immediate.snapshotType() == "Full" assert snapshot_immediate.source() == SOURCE_HA assert snapshot_immediate.date() == time.now() assert not snapshot_immediate.protected() # Might be a little flaky but...whatever await asyncio.wait({ha._pending_snapshot_task}) snapshots = await ha.get() assert 'pending' not in snapshots assert isinstance(next(iter(snapshots.values())), HASnapshot) return # ignroe events for now assert supervisor.getEvents() == [(EVENT_SNAPSHOT_START, { 'snapshot_name': snapshot_immediate.name(), 'snapshot_time': str(snapshot_immediate.date()) })] ha.snapshot_thread.join() assert supervisor.getEvents() == [(EVENT_SNAPSHOT_START, { 'snapshot_name': snapshot_immediate.name(), 'snapshot_time': str(snapshot_immediate.date()) }), (EVENT_SNAPSHOT_END, { 'completed': True, 'snapshot_name': snapshot_immediate.name(), 'snapshot_time': str(snapshot_immediate.date()) })]
async def test_sync_source_and_dest(model: Model, time, source, dest: HelperTestSource): backup_source = await source.create(CreateOptions(time.now(), "name")) await model._syncBackups([source, dest]) assert len(model.backups) == 1 backup_dest = await dest.save(model.backups[backup_source.slug()]) await model._syncBackups([source, dest]) assert len(model.backups) == 1 assert model.backups[backup_source.slug()].getSource( source.name()) is backup_source assert model.backups[backup_source.slug()].getSource( dest.name()) is backup_dest
async def test_bump_last_seen(ha: HaSource, time: Time, config: Config, supervisor: SimulatedSupervisor, model: Model, interceptor, data_cache: DataCache): backup = await ha.create(CreateOptions(time.now(), "Test Name")) time.advance(days=1) assert backup.slug() in await ha.get() assert data_cache.backup( backup.slug())[KEY_LAST_SEEN] == time.now().isoformat() time.advance(days=1) assert backup.slug() in await ha.get() assert data_cache.backup( backup.slug())[KEY_LAST_SEEN] == time.now().isoformat()
async def test_backup_password(ha: HaSource, config: Config, time): config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100) backup: HABackup = await ha.create(CreateOptions(time.now(), "Test Name")) assert not backup.protected() config.override(Setting.BACKUP_PASSWORD, 'test') backup = await ha.create(CreateOptions(time.now(), "Test Name")) assert backup.protected() config.override(Setting.BACKUP_PASSWORD, 'test') assert Password(ha.config).resolve() == 'test' config.override(Setting.BACKUP_PASSWORD, '!secret for_unit_tests') assert Password(ha.config).resolve() == 'password value' config.override(Setting.BACKUP_PASSWORD, '!secret bad_key') with pytest.raises(BackupPasswordKeyInvalid): Password(config).resolve() config.override(Setting.SECRETS_FILE_PATH, "/bad/file/path") config.override(Setting.BACKUP_PASSWORD, '!secret for_unit_tests') with pytest.raises(BackupPasswordKeyInvalid): Password(ha.config).resolve()
async def test_pending_backup_already_in_progress( ha, time, config: Config, supervisor: SimulatedSupervisor): await ha.create(CreateOptions(time.now(), "Test Name")) assert len(await ha.get()) == 1 config.override(Setting.NEW_BACKUP_TIMEOUT_SECONDS, 100) await supervisor.toggleBlockBackup() with pytest.raises(BackupInProgress): await ha.create(CreateOptions(time.now(), "Test Name")) backups = list((await ha.get()).values()) assert len(backups) == 2 backup = backups[1] assert isinstance(backup, PendingBackup) assert backup.name() == "Pending Backup" assert backup.slug() == "pending" assert not backup.uploadable() assert backup.backupType() == "unknown" assert backup.source() == SOURCE_HA assert backup.date() == time.now() assert not backup.protected() with pytest.raises(BackupInProgress): await ha.create(CreateOptions(time.now(), "Test Name"))
async def test_snapshot_password(ha: HaSource, config: Config, time): config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 100) snapshot: HASnapshot = await ha.create( CreateOptions(time.now(), "Test Name")) assert not snapshot.protected() config.override(Setting.SNAPSHOT_PASSWORD, 'test') snapshot = await ha.create(CreateOptions(time.now(), "Test Name")) assert snapshot.protected() config.override(Setting.SNAPSHOT_PASSWORD, 'test') assert Password(ha.config).resolve() == 'test' config.override(Setting.SNAPSHOT_PASSWORD, '!secret for_unit_tests') assert Password(ha.config).resolve() == 'password value' config.override(Setting.SNAPSHOT_PASSWORD, '!secret bad_key') with pytest.raises(SnapshotPasswordKeyInvalid): Password(config).resolve() config.override(Setting.SECRETS_FILE_PATH, "/bad/file/path") config.override(Setting.SNAPSHOT_PASSWORD, '!secret for_unit_tests') with pytest.raises(SnapshotPasswordKeyInvalid): Password(ha.config).resolve()
async def test_pending_snapshot_already_in_progress( ha, time, config: Config, supervisor: SimulatedSupervisor): await ha.create(CreateOptions(time.now(), "Test Name")) assert len(await ha.get()) == 1 config.override(Setting.NEW_SNAPSHOT_TIMEOUT_SECONDS, 100) await supervisor.toggleBlockSnapshot() with pytest.raises(SnapshotInProgress): await ha.create(CreateOptions(time.now(), "Test Name")) snapshots = list((await ha.get()).values()) assert len(snapshots) == 2 snapshot = snapshots[1] assert isinstance(snapshot, PendingSnapshot) assert snapshot.name() == "Pending Snapshot" assert snapshot.slug() == "pending" assert not snapshot.uploadable() assert snapshot.snapshotType() == "unknown" assert snapshot.source() == SOURCE_HA assert snapshot.date() == time.now() assert not snapshot.protected() with pytest.raises(SnapshotInProgress): await ha.create(CreateOptions(time.now(), "Test Name"))