test(deploy): cover configuration, setup and backups
Check rejected settings, administrator replacement and restorable database/media backups.
This commit is contained in:
1 parent
e20850a498
commit
51db1e4329
1 file changed
+108
@@ -0,0 +1,108 @@
|
||||
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()
|
||||
Reference in new issue
Block a user
