Files
RexiMemo-OSE/tests/test_deployment.py
T
rekushi 51db1e4329 test(deploy): cover configuration, setup and backups
Check rejected settings, administrator replacement and restorable database/media backups.
2026-10-29 04:43:04 +00:00

109 lines
5.5 KiB
Python

from contextlib import ExitStack, closing
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch
import json
import os
import sqlite3
import subprocess
import sys
import unittest
import zipfile
import manage
import server_config
class ConfigurationTests(unittest.TestCase):
def setUp(self):
self.stack = ExitStack()
self.directory = Path(self.stack.enter_context(TemporaryDirectory()))
self.stack.enter_context(patch.dict(os.environ, {"REXIMEMO_CONFIG": str(self.directory / "settings.json")}))
def tearDown(self):
self.stack.close()
def test_defaults_and_partial_save_preserve_unrelated_settings(self):
self.assertEqual(server_config.load_config()["port"], 8080)
saved = server_config.save_config({"site_name": "My Flipnotes", "port": 8088, "unknown_secret": "not saved"})
self.assertEqual(saved["site_name"], "My Flipnotes")
self.assertNotIn("unknown_secret", saved)
saved = server_config.save_config({"welcome_message": "Hello!", "guest_posting": "0"})
self.assertEqual(saved["site_name"], "My Flipnotes")
self.assertEqual(saved["port"], 8088)
self.assertFalse(saved["guest_posting"])
self.assertEqual(list(self.directory.glob("*.tmp")), [])
def test_bad_settings_leave_existing_configuration_intact(self):
original = server_config.save_config({"site_name": "Keep this"})
for field in ({"accent_colour": "red;background:url(evil)"}, {"port": 80},
{"host": "someone else's host"}, {"max_upload_bytes": 1},
{"logo_url": "https://other.example/picture.png"},
{"logo_url": "/static/../private.png"},
{"logo_url": "/static/%2e%2e/private.png"}):
with self.subTest(field=field), self.assertRaises(ValueError):
server_config.save_config(field)
self.assertEqual(server_config.load_config(), original)
self.assertEqual(server_config.save_config({"logo_url": "/branding/logo.png"})["logo_url"], "/branding/logo.png")
def test_corrupt_settings_do_not_silently_reset_the_server(self):
server_config.config_path().write_text("not json", encoding="utf-8")
with self.assertRaisesRegex(ValueError, "valid JSON"):
server_config.load_config()
def test_backup_contains_snapshot_media_and_branding_without_logs(self):
database_path = self.directory / "database" / "reximemo.sqlite3"
database_path.parent.mkdir()
with closing(sqlite3.connect(database_path)) as database:
with database:
database.execute("CREATE TABLE example(value TEXT)")
database.execute("INSERT INTO example VALUES('preserved')")
for name, data in (("database/Creators/SAMPLE/note.ppm", b"PARA"),
("database/Comments/note.ppm", b"mini"),
("web/static/logo.png", b"logo"),
("database/site-logo.png", b"custom logo"),
("logs/private.log", b"do not export")):
path = self.directory / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(data)
server_config.save_config({"site_name": "Backup server"})
destination = self.directory / "copy.zip"
with patch.object(manage, "ROOT", self.directory), patch.object(manage, "database_path", return_value=database_path):
self.assertEqual(manage.backup_server(destination), destination)
with self.assertRaises(ValueError):
manage.backup_server(destination)
with zipfile.ZipFile(destination) as archive:
self.assertEqual(archive.read("database/Creators/SAMPLE/note.ppm"), b"PARA")
self.assertEqual(archive.read("database/Comments/note.ppm"), b"mini")
self.assertEqual(archive.read("database/site-logo.png"), b"custom logo")
self.assertEqual(json.loads(archive.read("server-settings.json"))["site_name"], "Backup server")
self.assertFalse(any(name.startswith("logs/") for name in archive.namelist()))
restored = self.directory / "restored.sqlite3"
restored.write_bytes(archive.read("database/reximemo.sqlite3"))
with closing(sqlite3.connect(restored)) as database:
self.assertEqual(database.execute("SELECT value FROM example").fetchone()[0], "preserved")
def test_setup_chooses_an_admin_and_disables_the_default_login(self):
script = '''
import json
import manage
answers = iter(["A friendly server", "8099", "127.0.0.1", "friendlyadmin"])
manage.setup_server(input_fn=lambda prompt: next(answers), password_fn=lambda prompt: "a strong setup password")
from database import Database
assert Database.verify_account("friendlyadmin", "a strong setup password")["is_admin"] == 1
assert Database.verify_account("reximemo", "alpine") is None
from server_config import load_config
assert load_config()["setup_complete"] is True
assert load_config()["port"] == 8099
Database.close()
'''
environment = {**os.environ, "REXIMEMO_DB": str(self.directory / "setup.sqlite3")}
result = subprocess.run([sys.executable, "-c", script], cwd=Path(manage.__file__).parent,
env=environment, capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertNotIn("a strong setup password", server_config.config_path().read_text())
if __name__ == "__main__":
unittest.main()