"""CDN: selección por DNS y por anycast, sobre cdn/cdn.clab.yml. Corre después de test_net."""
import subprocess
import time
import unittest

import cdn_demo
from cdn_demo import ANYCAST, CDN, NS, NAME
from netlab import wait_until


def setUpModule():
    CDN.destroy()
    CDN.deploy()
    if not wait_until(cdn_demo.converged, seconds=60):
        CDN.destroy()
        raise RuntimeError("la red no convergió")
    cdn_demo.start_services()


def tearDownModule():
    CDN.destroy()


class DnsSelection(unittest.TestCase):
    def answer(self, node):
        return CDN.out(node, "dig", "+short", f"@{NS}", NAME, "A").strip()

    def test_same_name_different_answer(self):
        self.assertEqual(self.answer("c1"), "10.213.21.10")
        self.assertEqual(self.answer("c2"), "10.213.22.10")

    def test_the_chosen_replica_is_the_near_one(self):
        self.assertLess(cdn_demo.rtt("c1", "10.213.21.10") + 30, cdn_demo.rtt("c1", "10.213.22.10"))


class Anycast(unittest.TestCase):
    def test_same_address_different_replica(self):
        self.assertIn("s1", cdn_demo.who("c1"))
        self.assertIn("s2", cdn_demo.who("c2"))

    def test_both_replicas_are_in_the_bgp_table(self):
        table = CDN.vtysh("r1", "show bgp ipv4 unicast 10.213.50.0/24")
        self.assertIn("2 available", table)
        self.assertIn("65302 65310", table)

    def test_failover_serves_from_the_far_replica_and_comes_back(self):
        near = cdn_demo.rtt("c1", ANYCAST)
        cdn_demo.withdraw("s1")
        try:
            self.assertTrue(wait_until(lambda: "s2" in cdn_demo.who("c1", wait="1"), seconds=30, step=0.3))
            self.assertGreater(cdn_demo.rtt("c1", ANYCAST), near + 30)
        finally:
            cdn_demo.announce("s1")
        self.assertTrue(wait_until(lambda: "s1" in cdn_demo.who("c1", wait="1"), seconds=30, step=0.3))

    def test_an_open_connection_does_not_move(self):
        download = subprocess.Popen(
            ["docker", "exec", CDN.prefix + "c1", "curl", "-sS", "-o", "/dev/null", "--limit-rate", "1M",
             "--max-time", "30", f"http://{ANYCAST}/grande.bin"],
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
        time.sleep(2.0)
        cdn_demo.withdraw("s1")
        try:
            output = download.communicate(timeout=60)[0]
            self.assertEqual(download.returncode, 56, output)
            self.assertIn("reset", output)
        finally:
            cdn_demo.announce("s1")
            wait_until(lambda: "s1" in cdn_demo.who("c1", wait="1"), seconds=30, step=0.3)


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