"""Two CPU workers, one pool: the pages spread viewers, the deploy feeds them.

web/worker-pool.js carries the policy (unit-tested with node). These tests pin
how the demo pages and the deploy script use it.
"""
import pathlib
import unittest

ROOT = pathlib.Path(__file__).resolve().parent
INDEX = (ROOT / "poc" / "cpu-test-index.html").read_text(encoding="utf-8")
LIVE = (ROOT / "poc" / "interactive-live.html").read_text(encoding="utf-8")
DEPLOY = (ROOT / "deploy" / "deploy-cpu-server.sh").read_text(encoding="utf-8")
PROVISION = (ROOT / "deploy" / "provision-cpu-server.sh").read_text(encoding="utf-8")
NGINX = (ROOT / "deploy" / "nginx-w3cs.conf").read_text(encoding="utf-8")


class IndexPageTests(unittest.TestCase):
    def test_links_point_at_the_worker_with_the_most_free_seats(self):
        self.assertIn('<script type="module">', INDEX)
        self.assertIn('from "../web/worker-pool.js";', INDEX)
        self.assertIn("loadWorkers({currentHost:location.host})", INDEX)
        self.assertIn("const picked=pickWorker(probes);", INDEX)
        self.assertIn("for(const a of links)a.href=hrefOn(host,a.dataset.path);", INDEX)
        self.assertIn("setInterval(refresh,8000);", INDEX)

    def test_plain_click_reprobes_and_modified_clicks_keep_the_href(self):
        self.assertIn('list.addEventListener("click"', INDEX)
        self.assertIn("event.metaKey||event.ctrlKey||", INDEX)
        self.assertIn("const picked=await refresh();", INDEX)
        self.assertIn("location.href=hrefOn(picked?picked.host:location.host,a.dataset.path);", INDEX)


class LivePageTests(unittest.TestCase):
    def test_full_origin_moves_the_viewer_once(self):
        self.assertIn('from "../web/worker-pool.js";', LIVE)
        self.assertIn('if(!allowHop&&params.get("hop"))return false;', LIVE)
        self.assertIn("return workers.filter(worker=>worker.host!==location.host);", LIVE)
        self.assertIn("location.replace(pageUrlOn(picked.host,location,{extraParams:{hop:1}}));", LIVE)
        connect = LIVE[LIVE.index("const connectWithSeat=async()=>{"):
                       LIVE.index("throw NO_FREE_SEATS;\n};")]
        self.assertIn("if(await moveToWorkerWithFreeSeat())throw MOVING_WORKER;", connect)
        # the per-IP gate check still runs before any seat search
        self.assertLess(connect.index("if(await viewerGateBusy()){"),
                        connect.index("resolveSeat()"))

    def test_waiting_viewer_takes_a_seat_freed_on_any_worker(self):
        self.assertIn("if(error===MOVING_WORKER){", LIVE)
        waiter = LIVE[LIVE.index("}else if(error===NO_FREE_SEATS){"):
                      LIVE.index("}else if(error===VIEWER_BUSY){")]
        self.assertIn("if(await probeSeats()){", waiter)
        self.assertIn("moveToWorkerWithFreeSeat({allowHop:true})", waiter)
        self.assertIn("Every game seat on our servers is in use.", waiter)
        # own origin first, then the others: one reload, never both
        self.assertLess(waiter.index("location.reload();"),
                        waiter.index("moveToWorkerWithFreeSeat({allowHop:true})"))

    def test_lab_http_origins_never_probe_other_workers(self):
        self.assertIn(
            'if(location.protocol!=="https:"||explicitBridgePort)return [];', LIVE)


class DeployTests(unittest.TestCase):
    def test_domain_and_worker_list_are_parameters(self):
        self.assertIn("DOMAIN=${W3CS_CPU_DOMAIN:-london-cpu-worker.war3replays.com}", DEPLOY)
        self.assertIn("WORKER_HOSTS=${W3CS_WORKER_HOSTS:-", DEPLOY)
        self.assertIn("london-cpu-worker-2.war3replays.com", DEPLOY)
        self.assertIn("/home/ubuntu/w3cs-lab/app/workers.json", DEPLOY)
        # remote arguments are quoted: ssh re-parses the command string
        self.assertIn('REMOTE_ARGS=$(printf \'%q \' "$SEAT_COUNT" "$DOMAIN"', DEPLOY)
        self.assertIn('"${SSH[@]}" "sudo bash -s $REMOTE_ARGS"', DEPLOY)

    def test_nginx_server_name_is_rewritten_per_box(self):
        self.assertIn("server_name london-cpu-worker.war3replays.com;", NGINX)
        self.assertIn('sed "s/london-cpu-worker\\.war3replays\\.com/$DOMAIN/"', DEPLOY)
        self.assertNotIn('install -m 0644 "$APP/deploy/nginx-w3cs.conf" /etc/nginx/conf.d/w3cs.conf', DEPLOY)

    def test_certbot_runs_once_after_nginx_answers_on_port_80(self):
        certbot = DEPLOY.index("certbot certonly --webroot")
        self.assertLess(DEPLOY.index("systemctl reload nginx"), certbot)
        self.assertIn('[ ! -d "/etc/letsencrypt/live/$DOMAIN" ]', DEPLOY)
        self.assertIn('--deploy-hook "systemctl reload nginx"', DEPLOY)
        self.assertIn('"${W3CS_CERTBOT:-1}" != 0', DEPLOY)


class ProvisionTests(unittest.TestCase):
    def test_sibling_box_mirrors_the_source_box(self):
        for needle in (
                "useradd -m -u 1000 -s /bin/bash -G users",
                "useradd -m -u 1001 -s /bin/bash -G users -c \"war3 service\"",
                "loginctl enable-linger",
                "apt-mark hold wine-staging wine-staging-amd64 wine-staging-i386:i386",
                "--exclude /classic/w3cs-criu/images/",
                "00-war3-hardening.conf",
                "ufw --force enable",
                "fallocate -l 4G /swapfile"):
            self.assertIn(needle, PROVISION, needle)
        # the temporary clone key is removed on both sides
        self.assertIn("rm -f /root/.ssh/w3cs-clone-tmp /root/.ssh/w3cs-clone-tmp.pub", PROVISION)
        self.assertIn("sed -i '/ w3cs-clone-tmp\\$/d' /root/.ssh/authorized_keys", PROVISION)
        # root login closes only after the admin login was verified
        self.assertLess(PROVISION.index("admin login + passwordless sudo OK"),
                        PROVISION.index("=== harden"))


if __name__ == "__main__":
    unittest.main()
