#!/usr/bin/python3
#
# This test suite runner runs some integration tests for uwsgi, that is
# each test launches a test server with a specific configuration and
# verifies (usually using a HTTP request) that this test server behaves as
# expected.
#
# buildconf/integration-tests.ini holds the build configuration for this
# to run fine.


import os
import requests
import signal
import socket
import subprocess
import sys
import time
import unittest


TESTS_DIR = os.path.dirname(__file__)
UWSGI_BINARY = os.getenv("UWSGI_BINARY", os.path.join(TESTS_DIR, "..", "uwsgi"))
UWSGI_PLUGINS = os.getenv("UWSGI_PLUGINS", "all").split(" ")
UWSGI_ADDR = "127.0.0.1"
UWSGI_PORT = 8000
UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}"


def plugins_available(plugins):
    available = False
    if "all" in UWSGI_PLUGINS:
        available = True
    else:
        available = all([plugin in UWSGI_PLUGINS for plugin in plugins])
    return available, f"{plugins} plugins not available but required for this test case"


class UwsgiTest(unittest.TestCase):

    def start_server(self, args):
        self.testserver = subprocess.Popen(
            [UWSGI_BINARY] + args,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True,
        )

    def uwsgi_ready(self):
        try:
            s = socket.socket()
            s.connect(
                (
                    UWSGI_ADDR,
                    UWSGI_PORT,
                )
            )
        except socket.error:
            return False
        else:
            return True
        finally:
            s.close()

    def start_listen_server(self, args):
        self.start_server(["--http-socket", UWSGI_HTTP] + args)

        # ensure server is ready
        retries = 10
        while not self.uwsgi_ready() and retries > 0:
            time.sleep(0.1)
            retries = retries - 1
            if retries == 0:
                raise RuntimeError("uwsgi test server is not available")

    def tearDown(self):
        if hasattr(self._outcome, "errors"):
            # Python 3.4 - 3.10  (These two methods have no side effects)
            result = self.defaultTestResult()
            self._feedErrorsToResult(result, self._outcome.errors)
        else:
            # Python 3.11+
            result = self._outcome.result
        ok = not (result.errors + result.failures)

        self.testserver.send_signal(signal.SIGTERM)
        if not ok:
            print(self.testserver.stdout.read(), file=sys.stderr)

        self.testserver.wait()
        self.testserver.stdout.close()

    @unittest.skipUnless(*plugins_available(["python"]))
    def test_static_expires(self):
        self.start_listen_server(
            [
                "--plugin",
                "python",  # provide a request plugin
                os.path.join(TESTS_DIR, "static", "config.ini"),
            ]
        )

        with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r:
            self.assertTrue("Expires" in r.headers)

    @unittest.skipUnless(*plugins_available(["python"]))
    def test_python3_helloworld(self):
        self.start_listen_server(
            [
                "--plugin",
                "python",
                "--wsgi-file",
                os.path.join(TESTS_DIR, "python", "helloapp.py"),
            ]
        )

        with requests.get(f"http://{UWSGI_HTTP}/") as r:
            self.assertEqual(r.text, "Hello World")

    @unittest.skipUnless(*plugins_available(["pypy"]))
    def test_pypy3_helloworld(self):
        self.start_listen_server(
            [
                os.path.join(TESTS_DIR, "pypy", "config.ini"),
            ]
        )

        with requests.get(f"http://{UWSGI_HTTP}/") as r:
            self.assertEqual(r.text, "Hello World")

    @unittest.skipUnless(*plugins_available(["php"]))
    def test_php_session(self):
        self.start_listen_server(
            [
                os.path.join(TESTS_DIR, "php", "config.ini"),
            ]
        )

        with requests.get(f"http://{UWSGI_HTTP}/test.php") as r:
            self.assertEqual(r.text, "PASS\n")


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