fix: update test fixtures for SQLite storage migration
Some checks failed
Lint & Test / test (push) Failing after 1m33s
Some checks failed
Lint & Test / test (push) Failing after 1m33s
All store tests were passing file paths instead of Database objects after the JSON-to-SQLite migration. Updated fixtures to create temp Database instances, rewrote backup e2e tests for binary .db format, and fixed config tests for the simplified StorageConfig.
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
"""E2E: Backup and restore flow.
|
||||
|
||||
Tests creating entities, backing up, deleting, then restoring from backup.
|
||||
Tests creating entities, backing up (SQLite .db file), deleting, then restoring.
|
||||
"""
|
||||
|
||||
import io
|
||||
import json
|
||||
|
||||
|
||||
|
||||
class TestBackupRestoreFlow:
|
||||
@@ -42,20 +40,12 @@ class TestBackupRestoreFlow:
|
||||
resp = client.get("/api/v1/color-strip-sources")
|
||||
assert resp.json()["count"] == 1
|
||||
|
||||
# 2. Create a backup (GET returns a JSON file)
|
||||
# 2. Create a backup (GET returns a SQLite .db file)
|
||||
resp = client.get("/api/v1/system/backup")
|
||||
assert resp.status_code == 200
|
||||
backup_data = resp.json()
|
||||
assert backup_data["meta"]["format"] == "ledgrab-backup"
|
||||
assert "stores" in backup_data
|
||||
assert "devices" in backup_data["stores"]
|
||||
assert "color_strip_sources" in backup_data["stores"]
|
||||
|
||||
# Verify device is in the backup.
|
||||
# Store files have structure: {"version": "...", "devices": {id: {...}}}
|
||||
devices_store = backup_data["stores"]["devices"]
|
||||
assert "devices" in devices_store
|
||||
assert len(devices_store["devices"]) == 1
|
||||
backup_bytes = resp.content
|
||||
# SQLite files start with this magic header
|
||||
assert backup_bytes[:16].startswith(b"SQLite format 3")
|
||||
|
||||
# 3. Delete all created entities
|
||||
resp = client.delete(f"/api/v1/color-strip-sources/{css_id}")
|
||||
@@ -69,52 +59,37 @@ class TestBackupRestoreFlow:
|
||||
resp = client.get("/api/v1/color-strip-sources")
|
||||
assert resp.json()["count"] == 0
|
||||
|
||||
# 4. Restore from backup (POST with the backup JSON as a file upload)
|
||||
backup_bytes = json.dumps(backup_data).encode("utf-8")
|
||||
# 4. Restore from backup (POST with the .db file upload)
|
||||
resp = client.post(
|
||||
"/api/v1/system/restore",
|
||||
files={"file": ("backup.json", io.BytesIO(backup_bytes), "application/json")},
|
||||
files={"file": ("backup.db", io.BytesIO(backup_bytes), "application/octet-stream")},
|
||||
)
|
||||
assert resp.status_code == 200, f"Restore failed: {resp.text}"
|
||||
restore_result = resp.json()
|
||||
assert restore_result["status"] == "restored"
|
||||
assert restore_result["stores_written"] > 0
|
||||
|
||||
# 5. After restore, stores are written to disk but the in-memory
|
||||
# stores haven't been re-loaded (normally a server restart does that).
|
||||
# Verify the backup file was written correctly by reading it back.
|
||||
# The restore endpoint writes JSON files; we check the response confirms success.
|
||||
assert restore_result["restart_scheduled"] is True
|
||||
|
||||
def test_backup_contains_all_store_keys(self, client):
|
||||
"""Backup response includes entries for all known store types."""
|
||||
def test_backup_is_valid_sqlite(self, client):
|
||||
"""Backup response is a valid SQLite database file."""
|
||||
resp = client.get("/api/v1/system/backup")
|
||||
assert resp.status_code == 200
|
||||
stores = resp.json()["stores"]
|
||||
# At minimum, these critical stores should be present
|
||||
expected_keys = {
|
||||
"devices", "output_targets", "color_strip_sources",
|
||||
"capture_templates", "value_sources",
|
||||
}
|
||||
assert expected_keys.issubset(set(stores.keys()))
|
||||
assert resp.content[:16].startswith(b"SQLite format 3")
|
||||
# Should have Content-Disposition header for download
|
||||
assert "attachment" in resp.headers.get("content-disposition", "")
|
||||
|
||||
def test_restore_rejects_invalid_format(self, client):
|
||||
"""Uploading a non-backup JSON file should fail validation."""
|
||||
bad_data = json.dumps({"not": "a backup"}).encode("utf-8")
|
||||
"""Uploading a non-SQLite file should fail validation."""
|
||||
bad_data = b"not a database file at all, just random text content"
|
||||
resp = client.post(
|
||||
"/api/v1/system/restore",
|
||||
files={"file": ("bad.json", io.BytesIO(bad_data), "application/json")},
|
||||
files={"file": ("bad.db", io.BytesIO(bad_data), "application/octet-stream")},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
def test_restore_rejects_empty_stores(self, client):
|
||||
"""A backup with no recognized stores should fail."""
|
||||
bad_backup = {
|
||||
"meta": {"format": "ledgrab-backup", "format_version": 1},
|
||||
"stores": {"unknown_store": {}},
|
||||
}
|
||||
def test_restore_rejects_empty_file(self, client):
|
||||
"""A tiny file should fail validation."""
|
||||
resp = client.post(
|
||||
"/api/v1/system/restore",
|
||||
files={"file": ("bad.json", io.BytesIO(json.dumps(bad_backup).encode()), "application/json")},
|
||||
files={"file": ("tiny.db", io.BytesIO(b"x" * 50), "application/octet-stream")},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
Reference in New Issue
Block a user