"""The private /lihont2 portal: real requests against a server on a throw-away database. The credentials are random per run and live only in this process. The pages are checked for the content each role must (and must not) see, for escaping of user-controlled text, and for icon ids that exist in the shared sprite. """ import gc import http.cookiejar import importlib.util import re import secrets import sys import tempfile import threading import unittest import urllib.error import urllib.parse import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SERVER = ROOT / "src" / "web" / "server.py" SPRITE = ROOT / "src" / "web" / "assets" / "site" / "icons.svg" def load_server(): spec = importlib.util.spec_from_file_location("lihont_portal_server", SERVER) module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module spec.loader.exec_module(module) return module class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, *args, **kwargs): return None class PortalTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.portal = load_server() cls.directory = tempfile.TemporaryDirectory(prefix="lihont-portal-test-", ignore_cleanup_errors=True) base = Path(cls.directory.name) cls.apk = base / "lihont2.apk" cls.apk.write_bytes(b"PK\x03\x04" + b"0" * 2048) cls.admin_user = "admin_" + secrets.token_hex(4) cls.admin_password = secrets.token_urlsafe(18) cls.portal.ADMIN_USERNAME = cls.admin_user cls.portal.ADMIN_PASSWORD = cls.admin_password db = str(base / "data.db") cls.portal.init_db(db) handler = cls.portal.LihonTHandler handler.db_path = db handler.apk_path = str(cls.apk) handler._cached_stat = (0.0, 0, "") class Server(cls.portal.socketserver.ThreadingMixIn, cls.portal.http.server.HTTPServer): daemon_threads = True cls.httpd = Server(("127.0.0.1", 0), handler) cls.base_url = "http://127.0.0.1:%d%s" % (cls.httpd.server_address[1], cls.portal.BASE_PATH) threading.Thread(target=cls.httpd.serve_forever, daemon=True).start() cls.sprite_ids = set(re.findall(r'note"}) self.assertEqual(status, 200) self.assertNotIn("note", page) self.assertIn("<img src=x", page) user_id = re.search(r'name="user_id" value="(\d+)"', page).group(1) self.fetch(opener, "/admin/delete-user", {"user_id": user_id}) def test_missing_page_is_styled(self): status, page, _ = self.fetch(self.session(), "/nope") self.assertEqual(status, 404) self.assertIn("Страница не найдена", page) self.assertIn("/assets/site/md3.css", page) self.assert_icons_exist(page) def test_pages_use_only_shared_assets(self): _, login_page, _ = self.fetch(self.session(), "/") self.assertNotIn("