update repository references and improve script handling

This commit is contained in:
2025-03-19 20:36:36 +01:00
parent 51b0252b0e
commit 1d90749486
160 changed files with 14361 additions and 18 deletions

View File

@@ -0,0 +1,442 @@
from datetime import datetime, timedelta
import pytest
from dateutil.tz import tzutc
from pytest import fail
from backup.model import GenConfig, GenerationalScheme, DummyBackup, Backup
from backup.time import Time
def test_timezone(time) -> None:
assert time.local_tz is not None
def test_trivial(time) -> None:
config = GenConfig(days=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("single", time.local(1928, 12, 6))
]
assert scheme.getOldest(backups)[1].date() == time.local(1928, 12, 6)
def test_trivial_empty(time):
config = GenConfig(days=1)
scheme = GenerationalScheme(time, config, count=0)
assert scheme.getOldest([])[1] is None
def test_trivial_oldest(time: Time) -> None:
config = GenConfig(days=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 6, 10)),
makeBackup("test", time.local(1985, 12, 6, 12)),
makeBackup("test", time.local(1985, 12, 6, 13))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 12, 6, 10),
time.local(1985, 12, 6, 12),
time.local(1985, 12, 6, 13)
])
def test_duplicate_weeks(time):
config = GenConfig(weeks=1, day_of_week='wed')
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 5)),
makeBackup("test", time.local(1985, 12, 4)),
makeBackup("test", time.local(1985, 12, 1)),
makeBackup("test", time.local(1985, 12, 2))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 12, 1),
time.local(1985, 12, 2),
time.local(1985, 12, 5),
time.local(1985, 12, 4)
])
def test_duplicate_months(time) -> None:
config = GenConfig(months=2, day_of_month=15)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 6)),
makeBackup("test", time.local(1985, 12, 15)),
makeBackup("test", time.local(1985, 11, 20)),
makeBackup("test", time.local(1985, 11, 15))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 11, 20),
time.local(1985, 12, 6),
time.local(1985, 11, 15),
time.local(1985, 12, 15)
])
def test_duplicate_years(time):
config = GenConfig(years=2, day_of_year=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 31)),
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 12, 31)),
makeBackup("test", time.local(1984, 1, 1))
]
assertRemovalOrder(scheme, backups, [
time.local(1984, 12, 31),
time.local(1985, 12, 31),
time.local(1984, 1, 1),
time.local(1985, 1, 1)
])
def test_removal_order(time) -> None:
config = GenConfig(days=5, weeks=2, months=2, years=2,
day_of_week='mon', day_of_month=15, day_of_year=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
# 5 days, week 1
makeBackup("test", time.local(1985, 12, 7)), # day 1
makeBackup("test", time.local(1985, 12, 6)), # day 2
makeBackup("test", time.local(1985, 12, 5)), # day 3
makeBackup("test", time.local(1985, 12, 4)), # day 4
makeBackup("test", time.local(1985, 12, 3)), # day 5
makeBackup("test", time.local(1985, 12, 1)), # 1st week pref
# week 2
makeBackup("test", time.local(1985, 11, 25)), # 1st month pref
# month2
makeBackup("test", time.local(1985, 11, 15)), # 2nd month pref
# year 1
makeBackup("test", time.local(1985, 1, 1)), # 1st year preference
makeBackup("test", time.local(1985, 1, 2)),
# year 2
makeBackup("test", time.local(1984, 6, 1)), # 2nd year pref
makeBackup("test", time.local(1984, 7, 1)),
# year 3
makeBackup("test", time.local(1983, 1, 1)),
]
assertRemovalOrder(scheme, backups, [
time.local(1983, 1, 1),
time.local(1984, 7, 1),
time.local(1985, 1, 2),
time.local(1984, 6, 1),
time.local(1985, 1, 1),
time.local(1985, 11, 15),
time.local(1985, 11, 25),
time.local(1985, 12, 1),
time.local(1985, 12, 3),
time.local(1985, 12, 4),
time.local(1985, 12, 5),
time.local(1985, 12, 6),
time.local(1985, 12, 7)
])
@pytest.mark.timeout(60)
def test_simulate_daily_backup_for_4_years(time):
config = GenConfig(days=4, weeks=4, months=4, years=4,
day_of_week='mon', day_of_month=1, day_of_year=1)
scheme = GenerationalScheme(time, config, count=16)
backups = simulate(time.local(2019, 1, 1),
time.local(2022, 12, 31),
scheme)
assertRemovalOrder(GenerationalScheme(time, config, count=0), backups, [
# 4 years
time.local(2019, 1, 1),
time.local(2020, 1, 1),
time.local(2021, 1, 1),
time.local(2022, 1, 1),
# 4 months
time.local(2022, 9, 1),
time.local(2022, 10, 1),
time.local(2022, 11, 1),
time.local(2022, 12, 1),
# 4 weeks
time.local(2022, 12, 5),
time.local(2022, 12, 12),
time.local(2022, 12, 19),
time.local(2022, 12, 26),
# 4 days
time.local(2022, 12, 28),
time.local(2022, 12, 29),
time.local(2022, 12, 30),
time.local(2022, 12, 31)
])
@pytest.mark.timeout(60)
def test_simulate_agressive_daily_backup_for_4_years(time):
config = GenConfig(days=4, weeks=4, months=4, years=4,
day_of_week='mon', day_of_month=1, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=16)
backups = simulate(time.local(2019, 1, 1),
time.local(2022, 12, 31),
scheme)
assertRemovalOrder(GenerationalScheme(time, config, count=0), backups, [
# 4 years
time.local(2019, 1, 1),
time.local(2020, 1, 1),
time.local(2021, 1, 1),
time.local(2022, 1, 1),
# 4 months
time.local(2022, 9, 1),
time.local(2022, 10, 1),
time.local(2022, 11, 1),
time.local(2022, 12, 1),
# 4 weeks
time.local(2022, 12, 5),
time.local(2022, 12, 12),
time.local(2022, 12, 19),
time.local(2022, 12, 26),
# 4 days
time.local(2022, 12, 28),
time.local(2022, 12, 29),
time.local(2022, 12, 30),
time.local(2022, 12, 31),
])
def test_count_limit(time):
config = GenConfig(years=2, day_of_year=1)
scheme = GenerationalScheme(time, config, count=1)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 1, 1))
]
assertRemovalOrder(scheme, backups, [
time.local(1984, 1, 1)
])
def test_aggressive_removal_below_limit(time):
config = GenConfig(years=2, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=5)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1985, 1, 2))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 1, 2)
])
def test_aggressive_removal_at_limit_ok(time):
config = GenConfig(years=2, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=2)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 1, 1))
]
assertRemovalOrder(scheme, backups, [])
def test_aggressive_removal_over_limit(time):
config = GenConfig(years=2, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=2)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 1, 1)),
makeBackup("test", time.local(1983, 1, 1)),
makeBackup("test", time.local(1983, 1, 2))
]
assertRemovalOrder(scheme, backups, [
time.local(1983, 1, 1),
time.local(1983, 1, 2)
])
def test_removal_order_week(time: Time):
config = GenConfig(weeks=1, day_of_week='wed', aggressive=True)
scheme = GenerationalScheme(time, config, count=1)
backups = [
makeBackup("test", time.local(2019, 10, 28)),
makeBackup("test", time.local(2019, 10, 29)),
makeBackup("test", time.local(2019, 10, 30, 1)),
makeBackup("test", time.local(2019, 10, 30, 2)),
makeBackup("test", time.local(2019, 10, 31)),
makeBackup("test", time.local(2019, 11, 1)),
makeBackup("test", time.local(2019, 11, 2)),
makeBackup("test", time.local(2019, 11, 3)),
]
assertRemovalOrder(scheme, backups, [
time.local(2019, 10, 28),
time.local(2019, 10, 29),
time.local(2019, 10, 30, 1),
time.local(2019, 10, 31),
time.local(2019, 11, 1),
time.local(2019, 11, 2),
time.local(2019, 11, 3)
])
def test_removal_order_month(time):
config = GenConfig(months=1, day_of_month=20, aggressive=True)
scheme = GenerationalScheme(time, config, count=1)
backups = [
makeBackup("test", time.local(2019, 1, 1)),
makeBackup("test", time.local(2019, 1, 2)),
makeBackup("test", time.local(2019, 1, 20, 1)),
makeBackup("test", time.local(2019, 1, 20, 2)),
makeBackup("test", time.local(2019, 1, 21)),
makeBackup("test", time.local(2019, 1, 25)),
makeBackup("test", time.local(2019, 1, 26)),
makeBackup("test", time.local(2019, 1, 27)),
]
assertRemovalOrder(scheme, backups, [
time.local(2019, 1, 1),
time.local(2019, 1, 2),
time.local(2019, 1, 20, 1),
time.local(2019, 1, 21),
time.local(2019, 1, 25),
time.local(2019, 1, 26),
time.local(2019, 1, 27)
])
def test_removal_order_many_months(time):
config = GenConfig(months=70, day_of_month=20, aggressive=True)
scheme = GenerationalScheme(time, config, count=10)
backups = [
makeBackup("test", time.local(2019, 7, 20)), # preferred
makeBackup("test", time.local(2018, 7, 18)), # preferred
makeBackup("test", time.local(2018, 7, 21)),
makeBackup("test", time.local(2017, 1, 19)),
makeBackup("test", time.local(2017, 1, 20)), # preferred
makeBackup("test", time.local(2017, 1, 31)),
makeBackup("test", time.local(2016, 12, 1)), # preferred
makeBackup("test", time.local(2014, 1, 31)),
makeBackup("test", time.local(2014, 1, 1)), # preferred
]
assertRemovalOrder(scheme, backups, [
time.local(2014, 1, 31),
time.local(2017, 1, 19),
time.local(2017, 1, 31),
time.local(2018, 7, 21),
])
def test_removal_order_years(time):
config = GenConfig(years=2, day_of_year=15, aggressive=True)
scheme = GenerationalScheme(time, config, count=10)
backups = [
makeBackup("test", time.local(2019, 2, 15)),
makeBackup("test", time.local(2019, 1, 15)), # keep
makeBackup("test", time.local(2018, 1, 14)),
makeBackup("test", time.local(2018, 1, 15)), # keep
makeBackup("test", time.local(2018, 1, 16)),
makeBackup("test", time.local(2017, 1, 15)),
]
assertRemovalOrder(scheme, backups, [
time.local(2017, 1, 15),
time.local(2018, 1, 14),
time.local(2018, 1, 16),
time.local(2019, 2, 15),
])
@pytest.mark.asyncio
async def test_ignored_generational_labels(time):
config = GenConfig(days=2)
scheme = GenerationalScheme(time, config, count=10)
backup1 = makeBackup("test", time.local(2019, 2, 15))
backup2 = makeBackup("test", time.local(2019, 2, 14))
backup3 = makeBackup("test", time.local(2019, 2, 13), ignore=True)
backups = [backup1, backup2, backup3]
scheme.handleNaming(backups)
assert backup1.getStatusDetail() == ['Day 1 of 2']
assert backup2.getStatusDetail() == ['Day 2 of 2']
assert backup3.getStatusDetail() is None
def getRemovalOrder(scheme, toCheck):
backups = list(toCheck)
removed = []
while True:
oldest = scheme.getOldest(backups)
if not oldest:
break
removed.append(oldest.date())
backups.remove(oldest)
return removed
def assertRemovalOrder(scheme, toCheck, expected):
backups = list(toCheck)
removed = []
index = 0
time = scheme.time
while True:
reason, oldest = scheme.getOldest(backups)
if index >= len(expected):
if oldest is not None:
fail("at index {0}, expected 'None' but got {1}".format(
index, time.toLocal(oldest.date())))
break
if oldest.date() != expected[index]:
fail("at index {0}, expected {1} but got {2}".format(
index, time.toLocal(expected[index]), time.toLocal(oldest.date())))
removed.append(oldest.date())
backups.remove(oldest)
index += 1
return removed
def makeBackup(slug, date, name=None, ignore=False) -> Backup:
if not name:
name = slug
return DummyBackup(name, date.astimezone(tzutc()), "src", slug, ignore=ignore)
def simulate(start: datetime, end: datetime, scheme: GenerationalScheme, backups=[]):
today = start
while today <= end:
backups.append(makeBackup("test", today))
test = scheme.getOldest(backups)
if test is None:
pass
reason, oldest = test
while oldest is not None:
backups.remove(oldest)
test = scheme.getOldest(backups)
if test is None:
pass
reason, oldest = test
today = today + timedelta(hours=27)
today = scheme.time.local(today.year, today.month, today.day)
return backups