From dd34e6e573410ca941f2d27c0ebb8e44af5610fb Mon Sep 17 00:00:00 2001 From: Anton Engelhardt Date: Sun, 5 Nov 2023 14:28:04 +0100 Subject: [PATCH] Set up Integration Tests Signed-off-by: Anton Engelhardt --- .github/workflows/test.yml | 90 +++++++++++++++++++------ integration-tests/.env.template | 3 + integration-tests/docker-compose.yaml | 24 +++++++ integration-tests/envoy.yaml | 95 +++++++++++++++++++++++++++ integration-tests/requirements.txt | 2 + integration-tests/test.py | 83 +++++++++++++++++++++++ 6 files changed, 276 insertions(+), 21 deletions(-) create mode 100644 integration-tests/.env.template create mode 100644 integration-tests/docker-compose.yaml create mode 100644 integration-tests/envoy.yaml create mode 100644 integration-tests/requirements.txt create mode 100644 integration-tests/test.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd6bc00f..b593bae6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,24 +5,72 @@ on: jobs: test: - runs-on: ubuntu-latest - container: - image: antonengelhardt/rust-docker-tools - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Cache dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Cargo Deny - uses: EmbarkStudios/cargo-deny-action@v1 - - - name: Run tests - run: | - rustc --version && cargo --version - cargo clippy - cargo test --workspace --verbose + runs-on: ubuntu-latest + container: + image: antonengelhardt/rust-docker-tools + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Cargo Deny + uses: EmbarkStudios/cargo-deny-action@v1 + + - name: Run Rust tests + run: | + rustc --version && cargo --version + cargo clippy + cargo test --workspace --verbose + + integration-test: + runs-on: ubuntu-latest + container: + image: antonengelhardt/rust-docker-tools + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Create .env for Integration tests + run: | + echo "CI=true" >> integration-tests/.env + echo "WASM_OIDC_PLUGIN_TEST_EMAIL={{ secrets.WASM_OIDC_PLUGIN_TEST_EMAIL }}" >> integration-tests/.env + echo "WASM_OIDC_PLUGIN_TEST_PASSWORD={{ secrets.WASM_OIDC_PLUGIN_TEST_PASSWORD }}" >> integration-tests/.env + sed -i "client_secret: redacted/client_secret: ${{ secrets.WASM_OIDC_PLUGIN_TEST_CLIENT_SECRET }}" integration-tests/.env + + - name: Install Chrome + run: | + apt-get update + apt-get install -y libnss3-tools + wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb + apt install -y ./google-chrome-stable_current_amd64.deb + + - name: Setup Chromedriver + uses: nanasess/setup-chromedriver@v2 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Install Requirements + run: | + pip install -r integration-tests/requirements.txt + + - name: Build + run: | + cargo build --target wasm32-wasi --release + + - name: Docker-Compose up + run: | + cd integration-tests + docker-compose up -d + + - name: Run Integration tests + run: | + pytest test.py diff --git a/integration-tests/.env.template b/integration-tests/.env.template new file mode 100644 index 00000000..3e5c486c --- /dev/null +++ b/integration-tests/.env.template @@ -0,0 +1,3 @@ +CI=false +WASM_OIDC_PLUGIN_TEST_EMAIL=xxx +WASM_OIDC_PLUGIN_TEST_PASSWORD=xxx diff --git a/integration-tests/docker-compose.yaml b/integration-tests/docker-compose.yaml new file mode 100644 index 00000000..91bd79a2 --- /dev/null +++ b/integration-tests/docker-compose.yaml @@ -0,0 +1,24 @@ +version: '3.8' + +services: + envoy: + image: envoyproxy/envoy:v1.24-latest + hostname: envoy + ports: + - "10000:10000" + volumes: + - ./envoy.yaml:/etc/envoy/envoy.yaml + - ../target/wasm32-wasi/release:/etc/envoy/proxy-wasm-plugins + networks: + - envoymesh + command: envoy -c /etc/envoy/envoy.yaml --concurrency 1 + + httpbin: + image: kennethreitz/httpbin + hostname: httpbin + ports: + - "80:80" + networks: + - envoymesh +networks: + envoymesh: {} diff --git a/integration-tests/envoy.yaml b/integration-tests/envoy.yaml new file mode 100644 index 00000000..9b20bdee --- /dev/null +++ b/integration-tests/envoy.yaml @@ -0,0 +1,95 @@ +static_resources: + listeners: + address: + socket_address: + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: AUTO + route_config: + name: local_routes + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: httpbin + http_filters: + - name: envoy.filters.http.wasm + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm + config: + name: "oidc-wasm-plugin" + configuration: + "@type": "type.googleapis.com/google.protobuf.StringValue" + value: | + config_endpoint: "https://dev-wivqmfr2j1pfl1nt.us.auth0.com/.well-known/openid-configuration" + reload_interval_in_h: 1 + + exclude_hosts: [] + exclude_paths: [] + exclude_urls: [] + + cookie_name: "oidcSession" + cookie_duration: 86400 + token_validation: true + aes_key: "SFDUGDbOsRzSZbv+mvnZdu2x6+Hqe2WRaBABvfxmh3Q" + + authority: "dev-wivqmfr2j1pfl1nt.us.auth0.com" + redirect_uri: "http://localhost:10000/oidc/callback" + client_id: "qzj3n3w1cANXRdGYqw4j4OMEUQph2SVO" + scope: "openid" + claims: "{\"id_token\":{\"groups\":null,\"username\":null}}" + + client_secret: "redacted" + audience: "qzj3n3w1cANXRdGYqw4j4OMEUQph2SVO" + vm_config: + runtime: "envoy.wasm.runtime.v8" + code: + local: + filename: "/etc/envoy/proxy-wasm-plugins/wasm_oidc_plugin.wasm" + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: httpbin + connect_timeout: 5s + type: STRICT_DNS + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: httpbin + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: httpbin + port_value: 80 + hostname: "httpbin.org" + - name: oidc + connect_timeout: 5s + type: LOGICAL_DNS + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: oidc + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: dev-wivqmfr2j1pfl1nt.us.auth0.com + port_value: 443 + # hostname: "dev-wivqmfr2j1pfl1nt.us.auth0.com" + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: "dev-wivqmfr2j1pfl1nt.us.auth0.com" diff --git a/integration-tests/requirements.txt b/integration-tests/requirements.txt new file mode 100644 index 00000000..ec8ad96b --- /dev/null +++ b/integration-tests/requirements.txt @@ -0,0 +1,2 @@ +python-dotenv>=1.0.0 +selenium>=4.8.2 diff --git a/integration-tests/test.py b/integration-tests/test.py new file mode 100644 index 00000000..b5c2fdd0 --- /dev/null +++ b/integration-tests/test.py @@ -0,0 +1,83 @@ +from selenium import webdriver +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By + +from time import sleep +import os +from dotenv import load_dotenv + +import pytest + +# Global variables + +load_dotenv() + +BASE_URL = "http://localhost:10000" +WASM_OIDC_PLUGIN_TEST_EMAIL = os.getenv("WASM_OIDC_PLUGIN_TEST_EMAIL") +WASM_OIDC_PLUGIN_TEST_PASSWORD = os.getenv("WASM_OIDC_PLUGIN_TEST_PASSWORD") + +# Helper functions + +def set_chrome_options() -> None: + """Sets chrome options for Selenium. + Chrome options for headless browser is enabled. + """ + chrome_options = Options() + chrome_options.add_argument("--headless") + chrome_options.add_argument("--no-sandbox") + chrome_options.add_argument("--disable-dev-shm-usage") + chrome_prefs = {} + chrome_options.experimental_options["prefs"] = chrome_prefs + chrome_prefs["profile.default_content_settings"] = {"images": 2} + return chrome_options + +def set_up() -> None: + """Sets up the Selenium driver.""" + global driver + if os.getenv("CI") == "true": + driver = webdriver.Chrome(options=set_chrome_options()) + else: + driver = webdriver.Chrome() + driver.get(BASE_URL) + +def tear_down() -> None: + """Tears down the Selenium driver.""" + driver.quit() + +# Tests + +def test_home_page() -> None: + """Tests if the home page is accessible.""" + set_up() + assert driver.title == "Log in | Wasm Plugin" + tear_down() + +def test_success() -> None: + """Tests if the login is successful.""" + set_up() + + # Login + driver.find_element(By.ID, "username").send_keys(WASM_OIDC_PLUGIN_TEST_EMAIL) + driver.find_element(By.ID, "password").send_keys(WASM_OIDC_PLUGIN_TEST_PASSWORD) + driver.find_element(By.XPATH, "/html/body/div/main/section/div/div[2]/div/form/div[3]/button").click() + + # Assert title + assert driver.title == "httpbin.org" + + # Assert headers + assert driver.get_cookie("oidcSession-0") is not None + tear_down() + +def test_unsuccessful() -> None: + """Test if the login fails when the wrong credentials are entered.""" + set_up() + + # Login + driver.find_element(By.ID, "username").send_keys(WASM_OIDC_PLUGIN_TEST_EMAIL) + driver.find_element(By.ID, "password").send_keys("nottherightpassword") + driver.find_element(By.XPATH, "/html/body/div/main/section/div/div[2]/div/form/div[3]/button").click() + + assert driver.title != "httpbin.org" + +