Skip to content
Snippets Groups Projects
conftest.py 4.66 KiB
Newer Older
kaiyou's avatar
kaiyou committed
import pytest
import tempfile
import subprocess
import random
import os
import time
import requests
# Generic usefule fixtures
@pytest.fixture    
def temp():
    """Write data to a temporary file and return the handle
    This is a fixture instead of a utility function so that
    temporary files have a test-scoped lifetime
    """
    handles = []
    def fun(data):
        handle = tempfile.NamedTemporaryFile(delete=False)
        if type(data) is str:
            data = data.encode("utf8")
        handle.write(data)
        handle.flush()
        handles.append(handle)
        return handle.name
    yield fun
    del handles  # Useless but explicit


# Test application instance 
@pytest.fixture(scope="module")
def app(username, password):
    """Run an isolated application instance and return the URL"""
    data = tempfile.TemporaryDirectory()
    port = 5000 + random.randint(1, 999)  # Port = 5XXX
    url = f"http://localhost:{port}"
    env = os.environ
    env.update(FLASK_APP="hiboo", SQLALCHEMY_DATABASE_URI=f"sqlite:///{data.name}/hiboo.db")
    subprocess.run(["flask", "db", "upgrade"], env=env)
    subprocess.run(["flask", "user", "create", username, password], env=env)
    subprocess.run(["flask", "user", "promote", username], env=env)
    proc = subprocess.Popen(["flask", "run", "-p", str(port)], env=env)
    # Wait for the server to be ready
    for _ in range(30):
        try:
            assert requests.get(url).status_code == 200
        except Exception as e:
            print(e)
            time.sleep(1)
        else:
            yield url
            break
    proc.terminate()
    proc.wait()
    del data


@pytest.fixture(scope="session")
kaiyou's avatar
kaiyou committed
def username():
    """Default username for tests"""
    return "admin"


@pytest.fixture(scope="session")
kaiyou's avatar
kaiyou committed
def password():
    """Default password for tests"""
    return "admin"


@pytest.fixture
def service_name():
    """A randomly generated service name"""
    return "Test service " + random.randbytes(3).hex()


# Test webserver
@pytest.fixture
def httpd(temp):
    """Test httpd server"""
    proc = []
    def start(config):
        print(config)
        proc.append(subprocess.Popen(["httpd", "-DFOREGROUND", "-f", temp(config)]))
        time.sleep(1)  # Sleep so apache can start
    yield start
    for pid in proc:
        pid.terminate()
        pid.wait()


@pytest.fixture
def httpd_minimal():
    """Minimal httpd config"""
    rootDirectory = tempfile.TemporaryDirectory()
    with open(os.path.join(rootDirectory.name, "index.html"), "w") as handle:
        handle.write("Hello world!")
    yield f"""
        ServerName              localhost
        ServerRoot              /usr/lib/httpd
        PidFile                 /tmp/apache.pid
        
        LoadModule              mpm_event_module        modules/mod_mpm_event.so
        LoadModule              unixd_module            modules/mod_unixd.so
        LoadModule              env_module              modules/mod_env.so
        LoadModule              dir_module              modules/mod_dir.so
        LoadModule              log_config_module       modules/mod_log_config.so
        LoadModule              authn_core_module       modules/mod_authn_core.so
        LoadModule              authz_core_module       modules/mod_authz_core.so
        LoadModule              authz_user_module       modules/mod_authz_user.so
        
        LogLevel                debug
        ErrorLog                /dev/stderr
        TransferLog             /dev/stdout
        
        Listen                  127.0.0.1:8123
        DocumentRoot            {rootDirectory.name}
        DirectoryIndex          index.html
    """
    del rootDirectory


@pytest.fixture
def httpd_saml(httpd_minimal):
    return (httpd_minimal + """
        LoadModule              auth_mellon_module      modules/mod_auth_mellon.so
        <Location />
            Require valid-user
            AuthType "Mellon"
            MellonEnable "auth"
            MellonEndpointPath "/mellon"
            MellonSPPrivateKeyFile {key}
            MellonSPCertFile {cert}
            MellonIdPMetadataFile {metadata}
            SetEnv MELLON_DISABLE_SAMESITE 1
        </Location>
    """)

@pytest.fixture
def httpd_oidc(httpd_minimal):
    return (httpd_minimal + """
        LoadModule              auth_openidc_module     modules/mod_auth_openidc.so

        OIDCProviderMetadataURL {metadata}
        OIDCClientID {client_id}
        OIDCClientSecret {client_secret}
        OIDCRedirectURI http://localhost:8123/redirect_uri
        OIDCCryptoPassphrase changeme
        OIDCScope "openid email profile"
        
        <Location />
            Require valid-user
            AuthType "openid-connect"
        </Location>
    """)