Initial commit
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
from datetime import datetime
|
||||
|
||||
from pve_backup_report.models import (
|
||||
BackupCoverage,
|
||||
BackupJob,
|
||||
Guest,
|
||||
PbsAccessUser,
|
||||
PbsBackupSnapshotSummary,
|
||||
PbsDatastoreUsage,
|
||||
PbsGarbageCollectionStatus,
|
||||
PbsRetentionPolicy,
|
||||
PbsStorage,
|
||||
ReportData,
|
||||
ReportSummary,
|
||||
)
|
||||
from pve_backup_report.report_weasy_pdf import build_template_context
|
||||
from pve_backup_report.report_weasy_pdf import render_html
|
||||
|
||||
|
||||
def test_build_template_context_contains_sections() -> None:
|
||||
report_data = ReportData(
|
||||
pbs_storages=[
|
||||
PbsStorage(
|
||||
storage_id="BACKUP-PROD",
|
||||
username="backup@pbs",
|
||||
server="192.0.2.10",
|
||||
datastore="RAID5",
|
||||
namespace="serveurs",
|
||||
enabled=True,
|
||||
),
|
||||
PbsStorage(
|
||||
storage_id="BACKUP-LAB",
|
||||
username="backup@pbs",
|
||||
server="192.0.2.10",
|
||||
datastore="RAID5",
|
||||
namespace="lab",
|
||||
enabled=True,
|
||||
)
|
||||
],
|
||||
pbs_datastore_usages=[
|
||||
PbsDatastoreUsage(
|
||||
server_name="PBS01",
|
||||
datastore="RAID5",
|
||||
total_bytes=100,
|
||||
used_bytes=60,
|
||||
available_bytes=40,
|
||||
)
|
||||
],
|
||||
pbs_access_users=[
|
||||
PbsAccessUser(
|
||||
server_name="PBS01",
|
||||
auth_id="backup@pbs",
|
||||
user_id="backup@pbs",
|
||||
storage_id="BACKUP-PROD",
|
||||
datastore="RAID5",
|
||||
namespace="serveurs",
|
||||
enabled=True,
|
||||
expire=0,
|
||||
email="admin@example.invalid",
|
||||
permissions={"Datastore.Backup": True, "Datastore.Modify": False},
|
||||
comment="Compte PVE",
|
||||
)
|
||||
],
|
||||
pbs_gc_statuses=[
|
||||
PbsGarbageCollectionStatus(
|
||||
server_name="PBS01",
|
||||
datastore="RAID5",
|
||||
status="en_cours",
|
||||
)
|
||||
],
|
||||
pbs_retention_policies=[
|
||||
PbsRetentionPolicy(
|
||||
policy_id="prune-serveurs",
|
||||
server_name="PBS01",
|
||||
datastore="RAID5",
|
||||
namespace="serveurs",
|
||||
keep_daily=3,
|
||||
),
|
||||
PbsRetentionPolicy(
|
||||
policy_id="prune-lab",
|
||||
server_name="PBS01",
|
||||
datastore="RAID5",
|
||||
namespace="lab",
|
||||
keep_daily=3,
|
||||
),
|
||||
],
|
||||
guests=[
|
||||
Guest(vmid=100, name="srv", guest_type="qemu", notes="note production"),
|
||||
Guest(vmid=101, name="lab", guest_type="qemu"),
|
||||
],
|
||||
coverage=[
|
||||
BackupCoverage(
|
||||
guest=Guest(vmid=100, name="srv", guest_type="qemu", notes="note production"),
|
||||
status="sauvegarde_pbs_planifiee",
|
||||
jobs=[BackupJob(job_id="backup-prod", schedule="22:00")],
|
||||
storages=["BACKUP-PROD"],
|
||||
),
|
||||
BackupCoverage(
|
||||
guest=Guest(vmid=101, name="lab", guest_type="qemu"),
|
||||
status="sauvegarde_pbs_planifiee",
|
||||
jobs=[BackupJob(job_id="backup-lab", schedule="21:00")],
|
||||
storages=["BACKUP-LAB"],
|
||||
)
|
||||
],
|
||||
pbs_snapshot_summaries={
|
||||
("PBS01", "RAID5", "serveurs", "qemu", 100): PbsBackupSnapshotSummary(
|
||||
server_name="PBS01",
|
||||
vmid=100,
|
||||
guest_type="qemu",
|
||||
datastore="RAID5",
|
||||
namespace="serveurs",
|
||||
snapshot_count=3,
|
||||
oldest_backup_at=datetime(2026, 5, 7, 22, 0),
|
||||
newest_backup_at=datetime(2026, 5, 9, 22, 0),
|
||||
),
|
||||
("PBS01", "RAID5", "lab", "qemu", 101): PbsBackupSnapshotSummary(
|
||||
server_name="PBS01",
|
||||
vmid=101,
|
||||
guest_type="qemu",
|
||||
datastore="RAID5",
|
||||
namespace="lab",
|
||||
snapshot_count=2,
|
||||
oldest_backup_at=datetime(2026, 5, 8, 21, 0),
|
||||
newest_backup_at=datetime(2026, 5, 9, 21, 0),
|
||||
),
|
||||
},
|
||||
summary=ReportSummary(generated_at=datetime(2026, 5, 9, 2, 0)),
|
||||
)
|
||||
|
||||
context = build_template_context(
|
||||
report_data,
|
||||
{"192.0.2.10": "backup-display"},
|
||||
)
|
||||
|
||||
assert context["generated_at"] == "2026-05-09 02:00"
|
||||
assert [section.title for section in context["sections"]][:4] == [
|
||||
"Resume",
|
||||
"Stockages PBS déclarés sur PVE",
|
||||
"Utilisateurs PBS - Audit des accès",
|
||||
"Espaces de stockage PBS",
|
||||
]
|
||||
access_section = next(
|
||||
section for section in context["sections"] if section.title == "Utilisateurs PBS - Audit des accès"
|
||||
)
|
||||
assert access_section.headers == [
|
||||
"Serveur PBS",
|
||||
"Auth-id",
|
||||
"Storage PVE",
|
||||
"Datastore",
|
||||
"Namespace",
|
||||
"Actif",
|
||||
"Expiration",
|
||||
"Email",
|
||||
"Permissions",
|
||||
"Commentaire",
|
||||
]
|
||||
assert access_section.rows[0] == [
|
||||
"PBS01",
|
||||
"backup@pbs",
|
||||
"BACKUP-PROD",
|
||||
"RAID5",
|
||||
"serveurs",
|
||||
"oui",
|
||||
"aucune",
|
||||
"admin@example.invalid",
|
||||
"Datastore.Backup",
|
||||
"Compte PVE",
|
||||
]
|
||||
missing_section = next(
|
||||
section for section in context["sections"] if section.title == "VM/CT non sauvegardees"
|
||||
)
|
||||
assert missing_section.headers == ["VMID", "Nom", "Notes", "Type", "Noeud", "Etat", "Detail"]
|
||||
coverage_group = next(
|
||||
section for section in context["sections"] if section.title == "Sauvegarde des VM/CT"
|
||||
)
|
||||
assert coverage_group.level == 1
|
||||
coverage_sections = [
|
||||
section
|
||||
for section in context["sections"]
|
||||
if section.title.startswith("Sauvegarde des VM/CT -")
|
||||
]
|
||||
assert [section.title for section in coverage_sections] == [
|
||||
"Sauvegarde des VM/CT - lab",
|
||||
"Sauvegarde des VM/CT - serveurs",
|
||||
]
|
||||
coverage_section = next(
|
||||
section for section in coverage_sections if section.title == "Sauvegarde des VM/CT - serveurs"
|
||||
)
|
||||
assert "Namespace" not in coverage_section.headers
|
||||
assert coverage_section.headers[2] == "Notes"
|
||||
assert coverage_section.rows[0][2] == "note production"
|
||||
assert coverage_section.rows[0][7] == "192.0.2.10 (backup-display)"
|
||||
assert coverage_section.rows[0][9] == "non renseigne"
|
||||
retention_group = next(
|
||||
section for section in context["sections"] if section.title == "Retention des sauvegardes VM/CT"
|
||||
)
|
||||
assert retention_group.level == 1
|
||||
assert retention_group.warning is None
|
||||
retention_sections = [
|
||||
section
|
||||
for section in context["sections"]
|
||||
if section.title.startswith("Retention des sauvegardes VM/CT PBS01")
|
||||
]
|
||||
assert [section.title for section in retention_sections] == [
|
||||
"Retention des sauvegardes VM/CT PBS01 - lab",
|
||||
"Retention des sauvegardes VM/CT PBS01 - serveurs",
|
||||
]
|
||||
assert not any(
|
||||
section.title.startswith("Retention des sauvegardes VM/CT PBS02")
|
||||
for section in context["sections"]
|
||||
)
|
||||
assert not any(
|
||||
section.title.startswith("Retention des sauvegardes VM/CT PBS03")
|
||||
for section in context["sections"]
|
||||
)
|
||||
assert all("Namespace" not in section.headers for section in retention_sections)
|
||||
assert all("Datastore" in section.headers for section in retention_sections)
|
||||
assert all("Nombre attendu de versions" in section.headers for section in retention_sections)
|
||||
assert all("Delta" in section.headers for section in retention_sections)
|
||||
assert all("garbage collector" in (section.warning or "") for section in retention_sections)
|
||||
assert retention_sections[0].rows[0][0] == 101
|
||||
assert retention_sections[0].rows[0][2] == "RAID5"
|
||||
assert retention_sections[0].rows[0][3] == "Active sur PVE"
|
||||
assert retention_sections[0].rows[0][5] == "3"
|
||||
assert retention_sections[0].rows[0][6] == "-1"
|
||||
|
||||
|
||||
def test_render_html_keeps_css_unescaped() -> None:
|
||||
html = render_html(ReportData())
|
||||
|
||||
assert 'content: "Rapport des sauvegardes Proxmox VE"' in html
|
||||
assert """ not in html
|
||||
assert '<h1 class="section-group-title">' in html
|
||||
assert "Sauvegarde des VM/CT" in html
|
||||
assert '<h1 class="section-group-title">Retention des sauvegardes VM/CT</h1>' not in html
|
||||
|
||||
|
||||
def test_pdf_pbs_access_users_table_keeps_expected_fields_without_raw_secrets() -> None:
|
||||
report_data = ReportData(
|
||||
pbs_access_users=[
|
||||
PbsAccessUser(
|
||||
server_name="PBS01",
|
||||
auth_id="backup@pbs",
|
||||
user_id="backup@pbs",
|
||||
storage_id="BACKUP-PROD",
|
||||
datastore="RAID5",
|
||||
namespace="serveurs",
|
||||
enabled=True,
|
||||
expire=0,
|
||||
email="admin@example.invalid",
|
||||
permissions={"Datastore.Backup": True},
|
||||
comment="Compte PVE",
|
||||
raw={
|
||||
"Authorization": "PBSAPIToken=abc:secret",
|
||||
"api_token_secret": "secret-value",
|
||||
"password": "secret-password",
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
html = render_html(report_data)
|
||||
|
||||
assert "Utilisateurs PBS - Audit des accès" in html
|
||||
assert "backup@pbs" in html
|
||||
assert "BACKUP-PROD" in html
|
||||
assert "RAID5" in html
|
||||
assert "serveurs" in html
|
||||
assert "Datastore.Backup" in html
|
||||
assert "Compte PVE" in html
|
||||
assert "PBSAPIToken=abc:secret" not in html
|
||||
assert "secret-value" not in html
|
||||
assert "secret-password" not in html
|
||||
assert "api_token_secret" not in html
|
||||
|
||||
|
||||
def test_build_template_context_omits_unconfigured_retention_servers() -> None:
|
||||
report_data = ReportData(pbs_server_names=["PBS01"])
|
||||
|
||||
context = build_template_context(report_data)
|
||||
|
||||
titles = [section.title for section in context["sections"]]
|
||||
assert "Retention des sauvegardes VM/CT PBS01 - non renseigne" in titles
|
||||
assert "Retention des sauvegardes VM/CT PBS02 - non renseigne" not in titles
|
||||
assert "Retention des sauvegardes VM/CT PBS03 - non renseigne" not in titles
|
||||
Reference in New Issue
Block a user