Make the time for automated backups configurable (#135825)
* Make the time for automated backups configurable * Store time as a string, use None to indicate default time * Don't add jitter if the time is set by user * Include time of next automatic backup in response to backup/info * Update tests * Rename recurrence to state * Include scheduled backup time in backup/config/info response * Address review comments * Update cloud test * Add test for store migration * Address review commentspull/128997/merge
parent
8020bec47b
commit
43da828a51
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field, replace
|
||||
import datetime as dt
|
||||
from datetime import datetime, timedelta
|
||||
from enum import StrEnum
|
||||
import random
|
||||
|
@ -23,11 +24,13 @@ from .models import BackupManagerError, Folder
|
|||
if TYPE_CHECKING:
|
||||
from .manager import BackupManager, ManagerBackup
|
||||
|
||||
# The time of the automatic backup event should be compatible with
|
||||
# the time of the recorder's nightly job which runs at 04:12.
|
||||
# Run the backup at 04:45.
|
||||
CRON_PATTERN_DAILY = "45 4 * * *"
|
||||
CRON_PATTERN_WEEKLY = "45 4 * * {}"
|
||||
CRON_PATTERN_DAILY = "{m} {h} * * *"
|
||||
CRON_PATTERN_WEEKLY = "{m} {h} * * {d}"
|
||||
|
||||
# The default time for automatic backups to run is at 04:45.
|
||||
# This time is chosen to be compatible with the time of the recorder's
|
||||
# nightly job which runs at 04:12.
|
||||
DEFAULT_BACKUP_TIME = dt.time(4, 45)
|
||||
|
||||
# Randomize the start time of the backup by up to 60 minutes to avoid
|
||||
# all backups running at the same time.
|
||||
|
@ -74,6 +77,11 @@ class BackupConfigData:
|
|||
else:
|
||||
last_completed = None
|
||||
|
||||
if time_str := data["schedule"]["time"]:
|
||||
time = dt_util.parse_time(time_str)
|
||||
else:
|
||||
time = None
|
||||
|
||||
return cls(
|
||||
create_backup=CreateBackupConfig(
|
||||
agent_ids=data["create_backup"]["agent_ids"],
|
||||
|
@ -90,7 +98,9 @@ class BackupConfigData:
|
|||
copies=retention["copies"],
|
||||
days=retention["days"],
|
||||
),
|
||||
schedule=BackupSchedule(state=ScheduleState(data["schedule"]["state"])),
|
||||
schedule=BackupSchedule(
|
||||
state=ScheduleState(data["schedule"]["state"]), time=time
|
||||
),
|
||||
)
|
||||
|
||||
def to_dict(self) -> StoredBackupConfig:
|
||||
|
@ -137,7 +147,7 @@ class BackupConfig:
|
|||
*,
|
||||
create_backup: CreateBackupParametersDict | UndefinedType = UNDEFINED,
|
||||
retention: RetentionParametersDict | UndefinedType = UNDEFINED,
|
||||
schedule: ScheduleState | UndefinedType = UNDEFINED,
|
||||
schedule: ScheduleParametersDict | UndefinedType = UNDEFINED,
|
||||
) -> None:
|
||||
"""Update config."""
|
||||
if create_backup is not UNDEFINED:
|
||||
|
@ -148,7 +158,7 @@ class BackupConfig:
|
|||
self.data.retention = new_retention
|
||||
self.data.retention.apply(self._manager)
|
||||
if schedule is not UNDEFINED:
|
||||
new_schedule = BackupSchedule(state=schedule)
|
||||
new_schedule = BackupSchedule(**schedule)
|
||||
if new_schedule.to_dict() != self.data.schedule.to_dict():
|
||||
self.data.schedule = new_schedule
|
||||
self.data.schedule.apply(self._manager)
|
||||
|
@ -243,10 +253,18 @@ class StoredBackupSchedule(TypedDict):
|
|||
"""Represent the stored backup schedule configuration."""
|
||||
|
||||
state: ScheduleState
|
||||
time: str | None
|
||||
|
||||
|
||||
class ScheduleParametersDict(TypedDict, total=False):
|
||||
"""Represent parameters for backup schedule."""
|
||||
|
||||
state: ScheduleState
|
||||
time: dt.time | None
|
||||
|
||||
|
||||
class ScheduleState(StrEnum):
|
||||
"""Represent the schedule state."""
|
||||
"""Represent the schedule recurrence."""
|
||||
|
||||
NEVER = "never"
|
||||
DAILY = "daily"
|
||||
|
@ -264,7 +282,9 @@ class BackupSchedule:
|
|||
"""Represent the backup schedule."""
|
||||
|
||||
state: ScheduleState = ScheduleState.NEVER
|
||||
time: dt.time | None = None
|
||||
cron_event: CronSim | None = field(init=False, default=None)
|
||||
next_automatic_backup: datetime | None = field(init=False, default=None)
|
||||
|
||||
@callback
|
||||
def apply(
|
||||
|
@ -279,11 +299,17 @@ class BackupSchedule:
|
|||
self._unschedule_next(manager)
|
||||
return
|
||||
|
||||
time = self.time if self.time is not None else DEFAULT_BACKUP_TIME
|
||||
if self.state is ScheduleState.DAILY:
|
||||
self._schedule_next(CRON_PATTERN_DAILY, manager)
|
||||
self._schedule_next(
|
||||
CRON_PATTERN_DAILY.format(m=time.minute, h=time.hour),
|
||||
manager,
|
||||
)
|
||||
else:
|
||||
self._schedule_next(
|
||||
CRON_PATTERN_WEEKLY.format(self.state.value),
|
||||
CRON_PATTERN_WEEKLY.format(
|
||||
m=time.minute, h=time.hour, d=self.state.value
|
||||
),
|
||||
manager,
|
||||
)
|
||||
|
||||
|
@ -304,7 +330,10 @@ class BackupSchedule:
|
|||
if next_time < now:
|
||||
# schedule a backup at next daily time once
|
||||
# if we missed the last scheduled backup
|
||||
cron_event = CronSim(CRON_PATTERN_DAILY, now)
|
||||
time = self.time if self.time is not None else DEFAULT_BACKUP_TIME
|
||||
cron_event = CronSim(
|
||||
CRON_PATTERN_DAILY.format(m=time.minute, h=time.hour), now
|
||||
)
|
||||
next_time = next(cron_event)
|
||||
# reseed the cron event attribute
|
||||
# add a day to the next time to avoid scheduling at the same time again
|
||||
|
@ -334,19 +363,27 @@ class BackupSchedule:
|
|||
except Exception: # noqa: BLE001
|
||||
LOGGER.exception("Unexpected error creating automatic backup")
|
||||
|
||||
next_time += timedelta(seconds=random.randint(0, BACKUP_START_TIME_JITTER))
|
||||
if self.time is None:
|
||||
# randomize the start time of the backup by up to 60 minutes if the time is
|
||||
# not set to avoid all backups running at the same time
|
||||
next_time += timedelta(seconds=random.randint(0, BACKUP_START_TIME_JITTER))
|
||||
LOGGER.debug("Scheduling next automatic backup at %s", next_time)
|
||||
self.next_automatic_backup = next_time
|
||||
manager.remove_next_backup_event = async_track_point_in_time(
|
||||
manager.hass, _create_backup, next_time
|
||||
)
|
||||
|
||||
def to_dict(self) -> StoredBackupSchedule:
|
||||
"""Convert backup schedule to a dict."""
|
||||
return StoredBackupSchedule(state=self.state)
|
||||
return StoredBackupSchedule(
|
||||
state=self.state,
|
||||
time=self.time.isoformat() if self.time else None,
|
||||
)
|
||||
|
||||
@callback
|
||||
def _unschedule_next(self, manager: BackupManager) -> None:
|
||||
"""Unschedule the next backup."""
|
||||
self.next_automatic_backup = None
|
||||
if (remove_next_event := manager.remove_next_backup_event) is not None:
|
||||
remove_next_event()
|
||||
manager.remove_next_backup_event = None
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, TypedDict
|
||||
from typing import TYPE_CHECKING, Any, TypedDict
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.storage import Store
|
||||
|
@ -16,6 +16,7 @@ if TYPE_CHECKING:
|
|||
STORE_DELAY_SAVE = 30
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
STORAGE_VERSION_MINOR = 2
|
||||
|
||||
|
||||
class StoredBackupData(TypedDict):
|
||||
|
@ -25,14 +26,44 @@ class StoredBackupData(TypedDict):
|
|||
config: StoredBackupConfig
|
||||
|
||||
|
||||
class _BackupStore(Store[StoredBackupData]):
|
||||
"""Class to help storing backup data."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize storage class."""
|
||||
super().__init__(
|
||||
hass,
|
||||
STORAGE_VERSION,
|
||||
STORAGE_KEY,
|
||||
minor_version=STORAGE_VERSION_MINOR,
|
||||
)
|
||||
|
||||
async def _async_migrate_func(
|
||||
self,
|
||||
old_major_version: int,
|
||||
old_minor_version: int,
|
||||
old_data: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Migrate to the new version."""
|
||||
data = old_data
|
||||
if old_major_version == 1:
|
||||
if old_minor_version < 2:
|
||||
# Version 1.2 adds configurable backup time
|
||||
data["config"]["schedule"]["time"] = None
|
||||
|
||||
if old_major_version > 1:
|
||||
raise NotImplementedError
|
||||
return data
|
||||
|
||||
|
||||
class BackupStore:
|
||||
"""Store backup config."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, manager: BackupManager) -> None:
|
||||
"""Initialize the backup manager."""
|
||||
"""Initialize the backup store."""
|
||||
self._hass = hass
|
||||
self._manager = manager
|
||||
self._store: Store[StoredBackupData] = Store(hass, STORAGE_VERSION, STORAGE_KEY)
|
||||
self._store = _BackupStore(hass)
|
||||
|
||||
async def load(self) -> StoredBackupData | None:
|
||||
"""Load the store."""
|
||||
|
|
|
@ -6,6 +6,7 @@ import voluptuous as vol
|
|||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from .config import ScheduleState
|
||||
from .const import DATA_MANAGER, LOGGER
|
||||
|
@ -59,6 +60,7 @@ async def handle_info(
|
|||
"backups": [backup.as_frontend_json() for backup in backups.values()],
|
||||
"last_attempted_automatic_backup": manager.config.data.last_attempted_automatic_backup,
|
||||
"last_completed_automatic_backup": manager.config.data.last_completed_automatic_backup,
|
||||
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -321,7 +323,10 @@ async def handle_config_info(
|
|||
connection.send_result(
|
||||
msg["id"],
|
||||
{
|
||||
"config": manager.config.data.to_dict(),
|
||||
"config": manager.config.data.to_dict()
|
||||
| {
|
||||
"next_automatic_backup": manager.config.data.schedule.next_automatic_backup
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -351,7 +356,12 @@ async def handle_config_info(
|
|||
vol.Optional("days"): vol.Any(int, None),
|
||||
},
|
||||
),
|
||||
vol.Optional("schedule"): vol.All(str, vol.Coerce(ScheduleState)),
|
||||
vol.Optional("schedule"): vol.Schema(
|
||||
{
|
||||
vol.Optional("state"): vol.All(str, vol.Coerce(ScheduleState)),
|
||||
vol.Optional("time"): vol.Any(cv.time, None),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -112,6 +113,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -141,6 +143,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -170,6 +173,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -199,6 +203,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# serializer version: 1
|
||||
# name: test_store_migration
|
||||
dict({
|
||||
'data': dict({
|
||||
'backups': list([
|
||||
dict({
|
||||
'backup_id': 'abc123',
|
||||
'failed_agent_ids': list([
|
||||
'test.remote',
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
'config': dict({
|
||||
'create_backup': dict({
|
||||
'agent_ids': list([
|
||||
]),
|
||||
'include_addons': None,
|
||||
'include_all_addons': False,
|
||||
'include_database': True,
|
||||
'include_folders': None,
|
||||
'name': None,
|
||||
'password': None,
|
||||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
|
@ -244,12 +244,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -279,12 +281,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': '2024-10-26T04:45:00+01:00',
|
||||
'last_completed_automatic_backup': '2024-10-26T04:45:00+01:00',
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': 3,
|
||||
'days': 7,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -310,12 +314,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': 3,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -341,12 +347,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': '2024-10-27T04:45:00+01:00',
|
||||
'last_completed_automatic_backup': '2024-10-26T04:45:00+01:00',
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': 7,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -372,12 +380,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-18T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'mon',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -403,12 +413,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-16T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'sat',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -433,12 +445,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -464,12 +478,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': 7,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -502,11 +518,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -527,12 +544,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -558,12 +577,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': 7,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -596,11 +617,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -621,12 +643,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -652,12 +676,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T06:00:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': '06:00:00',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -690,11 +716,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': '06:00:00',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -715,12 +742,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -746,12 +775,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-18T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'mon',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -784,11 +815,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'mon',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -809,12 +841,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -840,12 +874,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -878,11 +914,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -903,12 +940,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -938,12 +977,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -980,11 +1021,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -1005,12 +1047,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1036,12 +1080,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': 3,
|
||||
'days': 7,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1074,11 +1120,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -1099,12 +1146,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1130,12 +1179,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1168,11 +1219,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -1193,12 +1245,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1224,12 +1278,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': 3,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1262,11 +1318,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -1287,12 +1344,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1318,12 +1377,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': 7,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1356,11 +1417,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -1381,12 +1443,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1412,12 +1476,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': '2024-11-14T04:55:00+01:00',
|
||||
'retention': dict({
|
||||
'copies': 3,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1450,11 +1516,12 @@
|
|||
}),
|
||||
'schedule': dict({
|
||||
'state': 'daily',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'key': 'backup',
|
||||
'minor_version': 1,
|
||||
'minor_version': 2,
|
||||
'version': 1,
|
||||
})
|
||||
# ---
|
||||
|
@ -1475,12 +1542,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1505,12 +1574,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1535,12 +1606,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1565,12 +1638,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1595,12 +1670,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1625,12 +1702,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1655,12 +1734,14 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1685,12 +1766,142 @@
|
|||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_config_update_errors[command4]
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': dict({
|
||||
'config': dict({
|
||||
'create_backup': dict({
|
||||
'agent_ids': list([
|
||||
]),
|
||||
'include_addons': None,
|
||||
'include_all_addons': False,
|
||||
'include_database': True,
|
||||
'include_folders': None,
|
||||
'name': None,
|
||||
'password': None,
|
||||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_config_update_errors[command4].1
|
||||
dict({
|
||||
'id': 3,
|
||||
'result': dict({
|
||||
'config': dict({
|
||||
'create_backup': dict({
|
||||
'agent_ids': list([
|
||||
]),
|
||||
'include_addons': None,
|
||||
'include_all_addons': False,
|
||||
'include_database': True,
|
||||
'include_folders': None,
|
||||
'name': None,
|
||||
'password': None,
|
||||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_config_update_errors[command5]
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': dict({
|
||||
'config': dict({
|
||||
'create_backup': dict({
|
||||
'agent_ids': list([
|
||||
]),
|
||||
'include_addons': None,
|
||||
'include_all_addons': False,
|
||||
'include_database': True,
|
||||
'include_folders': None,
|
||||
'name': None,
|
||||
'password': None,
|
||||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_config_update_errors[command5].1
|
||||
dict({
|
||||
'id': 3,
|
||||
'result': dict({
|
||||
'config': dict({
|
||||
'create_backup': dict({
|
||||
'agent_ids': list([
|
||||
]),
|
||||
'include_addons': None,
|
||||
'include_all_addons': False,
|
||||
'include_database': True,
|
||||
'include_folders': None,
|
||||
'name': None,
|
||||
'password': None,
|
||||
}),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
'retention': dict({
|
||||
'copies': None,
|
||||
'days': None,
|
||||
}),
|
||||
'schedule': dict({
|
||||
'state': 'never',
|
||||
'time': None,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -1708,6 +1919,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1734,6 +1946,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1776,6 +1989,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1802,6 +2016,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1844,6 +2059,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1897,6 +2113,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1934,6 +2151,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -1982,6 +2200,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2025,6 +2244,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2078,6 +2298,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2132,6 +2353,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2187,6 +2409,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2240,6 +2463,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2293,6 +2517,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2346,6 +2571,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2400,6 +2626,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2844,6 +3071,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2886,6 +3114,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2929,6 +3158,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -2993,6 +3223,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
@ -3036,6 +3267,7 @@
|
|||
]),
|
||||
'last_attempted_automatic_backup': None,
|
||||
'last_completed_automatic_backup': None,
|
||||
'next_automatic_backup': None,
|
||||
}),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
|
|
|
@ -274,6 +274,7 @@ async def test_async_initiate_backup(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -519,6 +520,7 @@ async def test_async_initiate_backup_with_agent_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id(
|
||||
|
@ -613,6 +615,7 @@ async def test_async_initiate_backup_with_agent_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
@ -880,6 +883,7 @@ async def test_async_initiate_backup_non_agent_upload_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -990,6 +994,7 @@ async def test_async_initiate_backup_with_task_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -1094,6 +1099,7 @@ async def test_initiate_backup_file_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -1614,6 +1620,7 @@ async def test_receive_backup_agent_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id(
|
||||
|
@ -1691,6 +1698,7 @@ async def test_receive_backup_agent_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1751,6 +1759,7 @@ async def test_receive_backup_non_agent_upload_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -1871,6 +1880,7 @@ async def test_receive_backup_file_write_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -1979,6 +1989,7 @@ async def test_receive_backup_read_tar_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
@ -2146,6 +2157,7 @@ async def test_receive_backup_file_read_error(
|
|||
"agent_errors": {},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
await ws_client.send_json_auto_id({"type": "backup/subscribe_events"})
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
"""Tests for the Backup integration."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.backup.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import setup_backup_integration
|
||||
|
||||
|
||||
async def test_store_migration(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test migrating the backup store."""
|
||||
hass_storage[DOMAIN] = {
|
||||
"data": {
|
||||
"backups": [
|
||||
{
|
||||
"backup_id": "abc123",
|
||||
"failed_agent_ids": ["test.remote"],
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"create_backup": {
|
||||
"agent_ids": [],
|
||||
"include_addons": None,
|
||||
"include_all_addons": False,
|
||||
"include_database": True,
|
||||
"include_folders": None,
|
||||
"name": None,
|
||||
"password": None,
|
||||
},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"retention": {
|
||||
"copies": None,
|
||||
"days": None,
|
||||
},
|
||||
"schedule": {
|
||||
"state": "never",
|
||||
},
|
||||
},
|
||||
},
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
}
|
||||
await setup_backup_integration(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass_storage[DOMAIN] == snapshot
|
|
@ -14,6 +14,7 @@ from homeassistant.components.backup import (
|
|||
BackupAgentPlatformProtocol,
|
||||
BackupReaderWriterError,
|
||||
Folder,
|
||||
store,
|
||||
)
|
||||
from homeassistant.components.backup.agent import BackupAgentUnreachableError
|
||||
from homeassistant.components.backup.const import DATA_MANAGER, DOMAIN
|
||||
|
@ -70,9 +71,7 @@ DEFAULT_STORAGE_DATA: dict[str, Any] = {
|
|||
"copies": None,
|
||||
"days": None,
|
||||
},
|
||||
"schedule": {
|
||||
"state": "never",
|
||||
},
|
||||
"schedule": {"state": "never", "time": None},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -305,7 +304,8 @@ async def test_delete_with_errors(
|
|||
hass_storage[DOMAIN] = {
|
||||
"data": storage_data,
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
}
|
||||
await setup_backup_integration(
|
||||
hass, with_hassio=False, backups={LOCAL_AGENT_ID: [TEST_BACKUP_ABC123]}
|
||||
|
@ -924,11 +924,12 @@ async def test_agents_info(
|
|||
"retention": {"copies": 3, "days": 7},
|
||||
"last_attempted_automatic_backup": "2024-10-26T04:45:00+01:00",
|
||||
"last_completed_automatic_backup": "2024-10-26T04:45:00+01:00",
|
||||
"schedule": {"state": "daily"},
|
||||
"schedule": {"state": "daily", "time": None},
|
||||
},
|
||||
},
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -948,11 +949,12 @@ async def test_agents_info(
|
|||
"retention": {"copies": 3, "days": None},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"schedule": {"state": "never"},
|
||||
"schedule": {"state": "never", "time": None},
|
||||
},
|
||||
},
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -972,11 +974,12 @@ async def test_agents_info(
|
|||
"retention": {"copies": None, "days": 7},
|
||||
"last_attempted_automatic_backup": "2024-10-27T04:45:00+01:00",
|
||||
"last_completed_automatic_backup": "2024-10-26T04:45:00+01:00",
|
||||
"schedule": {"state": "never"},
|
||||
"schedule": {"state": "never", "time": None},
|
||||
},
|
||||
},
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -996,11 +999,12 @@ async def test_agents_info(
|
|||
"retention": {"copies": None, "days": None},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"schedule": {"state": "mon"},
|
||||
"schedule": {"state": "mon", "time": None},
|
||||
},
|
||||
},
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1020,30 +1024,35 @@ async def test_agents_info(
|
|||
"retention": {"copies": None, "days": None},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"schedule": {"state": "sat"},
|
||||
"schedule": {"state": "sat", "time": None},
|
||||
},
|
||||
},
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
@patch("homeassistant.components.backup.config.random.randint", Mock(return_value=600))
|
||||
async def test_config_info(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
hass_storage: dict[str, Any],
|
||||
storage_data: dict[str, Any] | None,
|
||||
) -> None:
|
||||
"""Test getting backup config info."""
|
||||
client = await hass_ws_client(hass)
|
||||
await hass.config.async_set_time_zone("Europe/Amsterdam")
|
||||
freezer.move_to("2024-11-13T12:01:00+01:00")
|
||||
|
||||
hass_storage.update(storage_data)
|
||||
|
||||
await setup_backup_integration(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await client.send_json_auto_id({"type": "backup/config/info"})
|
||||
assert await client.receive_json() == snapshot
|
||||
|
||||
|
@ -1060,17 +1069,17 @@ async def test_config_info(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily", "time": "06:00"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"schedule": "mon",
|
||||
"schedule": {"state": "mon"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
|
@ -1081,59 +1090,63 @@ async def test_config_info(
|
|||
"name": "test-name",
|
||||
"password": "test-password",
|
||||
},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": 3, "days": 7},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": 3, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 7},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": 3},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"days": 7},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
],
|
||||
)
|
||||
@patch("homeassistant.components.backup.config.random.randint", Mock(return_value=600))
|
||||
async def test_config_update(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
command: dict[str, Any],
|
||||
hass_storage: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test updating the backup config."""
|
||||
client = await hass_ws_client(hass)
|
||||
await hass.config.async_set_time_zone("Europe/Amsterdam")
|
||||
freezer.move_to("2024-11-13T12:01:00+01:00")
|
||||
|
||||
await setup_backup_integration(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await client.send_json_auto_id({"type": "backup/config/info"})
|
||||
assert await client.receive_json() == snapshot
|
||||
|
||||
|
@ -1146,6 +1159,11 @@ async def test_config_update(
|
|||
assert await client.receive_json() == snapshot
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Trigger store write
|
||||
freezer.tick(60)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass_storage[DOMAIN] == snapshot
|
||||
|
||||
|
||||
|
@ -1156,7 +1174,17 @@ async def test_config_update(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"schedule": "someday",
|
||||
"schedule": "blah",
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"schedule": {"state": "someday"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"schedule": {"time": "early"},
|
||||
},
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
|
@ -1205,6 +1233,7 @@ async def test_config_update_errors(
|
|||
"time_2",
|
||||
"attempted_backup_time",
|
||||
"completed_backup_time",
|
||||
"scheduled_backup_time",
|
||||
"backup_calls_1",
|
||||
"backup_calls_2",
|
||||
"call_args",
|
||||
|
@ -1215,10 +1244,11 @@ async def test_config_update_errors(
|
|||
# No config update
|
||||
[],
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-13T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-13T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
|
@ -1230,14 +1260,15 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-13T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-13T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
|
@ -1248,14 +1279,15 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "mon",
|
||||
"schedule": {"state": "mon"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-18T04:45:00+01:00",
|
||||
"2024-11-25T04:45:00+01:00",
|
||||
"2024-11-18T04:45:00+01:00",
|
||||
"2024-11-18T04:45:00+01:00",
|
||||
"2024-11-18T04:55:00+01:00",
|
||||
"2024-11-25T04:55:00+01:00",
|
||||
"2024-11-18T04:55:00+01:00",
|
||||
"2024-11-18T04:55:00+01:00",
|
||||
"2024-11-18T04:55:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
|
@ -1266,7 +1298,45 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "mon", "time": "03:45"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T03:45:00+01:00",
|
||||
"2024-11-18T03:45:00+01:00",
|
||||
"2024-11-25T03:45:00+01:00",
|
||||
"2024-11-18T03:45:00+01:00",
|
||||
"2024-11-18T03:45:00+01:00",
|
||||
"2024-11-18T03:45:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
None,
|
||||
),
|
||||
(
|
||||
[
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": {"state": "daily", "time": "03:45"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T03:45:00+01:00",
|
||||
"2024-11-12T03:45:00+01:00",
|
||||
"2024-11-13T03:45:00+01:00",
|
||||
"2024-11-12T03:45:00+01:00",
|
||||
"2024-11-12T03:45:00+01:00",
|
||||
"2024-11-12T03:45:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
None,
|
||||
),
|
||||
(
|
||||
[
|
||||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
|
@ -1274,6 +1344,7 @@ async def test_config_update_errors(
|
|||
"2034-11-11T13:00:00+01:00",
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
|
@ -1284,14 +1355,15 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
}
|
||||
],
|
||||
"2024-10-26T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-13T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-13T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
|
@ -1302,14 +1374,15 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "mon",
|
||||
"schedule": {"state": "mon"},
|
||||
}
|
||||
],
|
||||
"2024-10-26T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-13T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00", # missed event uses daily schedule once
|
||||
"2024-11-12T04:45:00+01:00", # missed event uses daily schedule once
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-13T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00", # missed event uses daily schedule once
|
||||
"2024-11-12T04:55:00+01:00", # missed event uses daily schedule once
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
1,
|
||||
1,
|
||||
BACKUP_CALL,
|
||||
|
@ -1320,7 +1393,7 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
"2024-10-26T04:45:00+01:00",
|
||||
|
@ -1328,6 +1401,7 @@ async def test_config_update_errors(
|
|||
"2034-11-12T12:00:00+01:00",
|
||||
"2024-10-26T04:45:00+01:00",
|
||||
"2024-10-26T04:45:00+01:00",
|
||||
None,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
|
@ -1338,14 +1412,15 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-13T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00", # attempted to create backup but failed
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-13T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00", # attempted to create backup but failed
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
|
@ -1356,14 +1431,15 @@ async def test_config_update_errors(
|
|||
{
|
||||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
}
|
||||
],
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00",
|
||||
"2024-11-13T04:45:00+01:00",
|
||||
"2024-11-12T04:45:00+01:00", # attempted to create backup but failed
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
"2024-11-13T04:55:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00", # attempted to create backup but failed
|
||||
"2024-11-11T04:45:00+01:00",
|
||||
"2024-11-12T04:55:00+01:00",
|
||||
1,
|
||||
2,
|
||||
BACKUP_CALL,
|
||||
|
@ -1371,7 +1447,7 @@ async def test_config_update_errors(
|
|||
),
|
||||
],
|
||||
)
|
||||
@patch("homeassistant.components.backup.config.BACKUP_START_TIME_JITTER", 0)
|
||||
@patch("homeassistant.components.backup.config.random.randint", Mock(return_value=600))
|
||||
async def test_config_schedule_logic(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
|
@ -1384,6 +1460,7 @@ async def test_config_schedule_logic(
|
|||
time_2: str,
|
||||
attempted_backup_time: str,
|
||||
completed_backup_time: str,
|
||||
scheduled_backup_time: str,
|
||||
backup_calls_1: int,
|
||||
backup_calls_2: int,
|
||||
call_args: Any,
|
||||
|
@ -1406,13 +1483,14 @@ async def test_config_schedule_logic(
|
|||
"retention": {"copies": None, "days": None},
|
||||
"last_attempted_automatic_backup": last_completed_automatic_backup,
|
||||
"last_completed_automatic_backup": last_completed_automatic_backup,
|
||||
"schedule": {"state": "daily"},
|
||||
"schedule": {"state": "daily", "time": None},
|
||||
},
|
||||
}
|
||||
hass_storage[DOMAIN] = {
|
||||
"data": storage_data,
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
}
|
||||
create_backup.side_effect = create_backup_side_effect
|
||||
await hass.config.async_set_time_zone("Europe/Amsterdam")
|
||||
|
@ -1426,6 +1504,10 @@ async def test_config_schedule_logic(
|
|||
result = await client.receive_json()
|
||||
assert result["success"]
|
||||
|
||||
await client.send_json_auto_id({"type": "backup/info"})
|
||||
result = await client.receive_json()
|
||||
assert result["result"]["next_automatic_backup"] == scheduled_backup_time
|
||||
|
||||
freezer.move_to(time_1)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1471,7 +1553,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": None, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1510,7 +1592,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 3, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1549,7 +1631,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 3, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1578,7 +1660,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 3, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1622,7 +1704,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 2, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1666,7 +1748,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 2, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1705,7 +1787,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 2, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1744,7 +1826,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 0, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1788,7 +1870,7 @@ async def test_config_schedule_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 0, "days": None},
|
||||
"schedule": "daily",
|
||||
"schedule": {"state": "daily"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1852,13 +1934,14 @@ async def test_config_retention_copies_logic(
|
|||
"retention": {"copies": None, "days": None},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": last_backup_time,
|
||||
"schedule": {"state": "daily"},
|
||||
"schedule": {"state": "daily", "time": None},
|
||||
},
|
||||
}
|
||||
hass_storage[DOMAIN] = {
|
||||
"data": storage_data,
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
}
|
||||
get_backups.return_value = (backups, get_backups_agent_errors)
|
||||
delete_backup.return_value = delete_backup_agent_errors
|
||||
|
@ -1922,7 +2005,7 @@ async def test_config_retention_copies_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": None, "days": None},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1958,7 +2041,7 @@ async def test_config_retention_copies_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 3, "days": None},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -1994,7 +2077,7 @@ async def test_config_retention_copies_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 3, "days": None},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -2035,7 +2118,7 @@ async def test_config_retention_copies_logic(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test.test-agent"]},
|
||||
"retention": {"copies": 2, "days": None},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
},
|
||||
{
|
||||
"backup-1": MagicMock(
|
||||
|
@ -2109,13 +2192,14 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"retention": {"copies": None, "days": None},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"schedule": {"state": "daily"},
|
||||
"schedule": {"state": "daily", "time": None},
|
||||
},
|
||||
}
|
||||
hass_storage[DOMAIN] = {
|
||||
"data": storage_data,
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
}
|
||||
get_backups.return_value = (backups, get_backups_agent_errors)
|
||||
delete_backup.return_value = delete_backup_agent_errors
|
||||
|
@ -2236,7 +2320,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 2},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2272,7 +2356,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 2},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2308,7 +2392,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 3},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2344,7 +2428,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 2},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2385,7 +2469,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 2},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2421,7 +2505,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 2},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2457,7 +2541,7 @@ async def test_config_retention_copies_logic_manual_backup(
|
|||
"type": "backup/config/update",
|
||||
"create_backup": {"agent_ids": ["test-agent"]},
|
||||
"retention": {"copies": None, "days": 0},
|
||||
"schedule": "never",
|
||||
"schedule": {"state": "never"},
|
||||
}
|
||||
],
|
||||
{
|
||||
|
@ -2529,13 +2613,14 @@ async def test_config_retention_days_logic(
|
|||
"retention": {"copies": None, "days": stored_retained_days},
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": last_backup_time,
|
||||
"schedule": {"state": "never"},
|
||||
"schedule": {"state": "never", "time": None},
|
||||
},
|
||||
}
|
||||
hass_storage[DOMAIN] = {
|
||||
"data": storage_data,
|
||||
"key": DOMAIN,
|
||||
"version": 1,
|
||||
"version": store.STORAGE_VERSION,
|
||||
"minor_version": store.STORAGE_VERSION_MINOR,
|
||||
}
|
||||
get_backups.return_value = (backups, get_backups_agent_errors)
|
||||
delete_backup.return_value = delete_backup_agent_errors
|
||||
|
|
|
@ -204,6 +204,7 @@ async def test_agents_list_backups_fail_cloud(
|
|||
"backups": [],
|
||||
"last_attempted_automatic_backup": None,
|
||||
"last_completed_automatic_backup": None,
|
||||
"next_automatic_backup": None,
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue