(feature) Capture structured backup statistics
Parse rsync --stats output into structured run metrics for file counts, transferred bytes, literal data, matched data, speedup, and estimated link-dest savings. Store collected stats on backup run results and successful snapshot metadata, including snapshot data usage and backup-root capacity details for future dashboard graphs and disk-full projections. Render the collected metrics on run and snapshot detail pages, with tests covering parsing, metadata persistence, and UI output.
This commit is contained in:
60
src/pobsync_backend/tests/test_run_stats.py
Normal file
60
src/pobsync_backend/tests/test_run_stats.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from pobsync.run_stats import parse_rsync_stats, tree_usage
|
||||
|
||||
|
||||
class RunStatsTests(SimpleTestCase):
|
||||
def test_parse_rsync_stats_extracts_counts_bytes_and_savings(self) -> None:
|
||||
stats = parse_rsync_stats(
|
||||
"""
|
||||
Number of files: 1,234 (reg: 1,200, dir: 34)
|
||||
Number of created files: 12 (reg: 10, dir: 2)
|
||||
Number of deleted files: 3
|
||||
Number of regular files transferred: 8
|
||||
Total file size: 1.50M bytes
|
||||
Total transferred file size: 24.00K bytes
|
||||
Literal data: 24.00K bytes
|
||||
Matched data: 976.00K bytes
|
||||
File list size: 8.00K
|
||||
Total bytes sent: 10.00K
|
||||
Total bytes received: 2.00K
|
||||
sent 10.00K bytes received 2.00K bytes 1.20K bytes/sec
|
||||
total size is 1.50M speedup is 125.00
|
||||
"""
|
||||
)
|
||||
|
||||
self.assertEqual(stats["files_total"], 1234)
|
||||
self.assertEqual(stats["files_created"], 12)
|
||||
self.assertEqual(stats["files_deleted"], 3)
|
||||
self.assertEqual(stats["files_transferred"], 8)
|
||||
self.assertEqual(stats["total_file_size_bytes"], 1_500_000)
|
||||
self.assertEqual(stats["total_transferred_file_size_bytes"], 24_000)
|
||||
self.assertEqual(stats["literal_data_bytes"], 24_000)
|
||||
self.assertEqual(stats["matched_data_bytes"], 976_000)
|
||||
self.assertEqual(stats["bytes_sent_received"], 12_000)
|
||||
self.assertEqual(stats["bytes_per_second"], 1_200)
|
||||
self.assertEqual(stats["speedup"], 125.0)
|
||||
self.assertEqual(stats["link_dest_estimated_savings_bytes"], 976_000)
|
||||
self.assertEqual(stats["link_dest_estimated_savings_ratio"], 0.976)
|
||||
|
||||
def test_tree_usage_reports_hardlinked_files(self) -> None:
|
||||
with TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
source = root / "source"
|
||||
linked = root / "linked"
|
||||
source.write_bytes(b"abc")
|
||||
os.link(source, linked)
|
||||
|
||||
stats = tree_usage(root)
|
||||
|
||||
self.assertEqual(stats["files"], 2)
|
||||
self.assertEqual(stats["apparent_size_bytes"], 6)
|
||||
self.assertEqual(stats["hardlinked_files"], 2)
|
||||
self.assertEqual(stats["hardlinked_apparent_size_bytes"], 6)
|
||||
self.assertEqual(stats["hardlink_apparent_ratio"], 1.0)
|
||||
Reference in New Issue
Block a user