Skip to content

Commit

Permalink
Reorganize tests (#10)
Browse files Browse the repository at this point in the history
Create new test scenarios and adapt scenarios of load balancing to be
parametrized, given that the tests were the same for the round robin and
least connections configuration.

Also add markers on every test case so that it's possible to run tests
for a specific feature without running every test.
  • Loading branch information
eaneto authored Nov 24, 2024
1 parent 8e93200 commit fd3c76c
Show file tree
Hide file tree
Showing 17 changed files with 507 additions and 391 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ load balancer.
- Enhance HTTP parsing implementation, parse headers over 4kB, support
chunked transfer, support all methods.
- Enhance error handling making sure the server won't crash.
- Write better tests cases, the ones written right now(can be found at
`tests` directory) only check basic things and there are only a few
crash scenarios.

## Features

Expand Down
5 changes: 5 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[pytest]
markers =
command: Test for the command server.
round_robin: Test ekilibri with the round robin configuration.
least_connections: Test ekilibri with the least connections configuration.
4 changes: 4 additions & 0 deletions src/bin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ async fn main() {

let config = Arc::new(config);

if config.pool_size == 0 {
panic!("Pool size has to be greater than zero.")
}

let connections_counters = Arc::new(RwLock::new(Vec::with_capacity(config.servers.len())));
for _ in &config.servers {
connections_counters.write().await.push(AtomicU64::new(0));
Expand Down
7 changes: 7 additions & 0 deletions tests/command_server_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import pytest
import requests

URL = "http://localhost:8081"


@pytest.mark.command
def test_echo_server():
payload = "test parsing this info"

Expand All @@ -16,6 +18,7 @@ def test_echo_server():
assert response.text == payload


@pytest.mark.command
def test_echo_server_with_content_type():
payload = """{"key": "value"}"""

Expand All @@ -29,6 +32,7 @@ def test_echo_server_with_content_type():
assert response.text == payload


@pytest.mark.command
def test_echo_server_with_big_body():
payload = "test parsing this info" * 1000

Expand All @@ -42,6 +46,7 @@ def test_echo_server_with_big_body():
assert response.text == payload


@pytest.mark.command
def test_echo_server_with_huge_body():
payload = "test parsing this info" * 10000

Expand All @@ -55,12 +60,14 @@ def test_echo_server_with_huge_body():
assert response.text == payload


@pytest.mark.command
def test_get_not_found():
response = requests.get(URL + "/not-found")
assert response.status_code == 404
assert response.text == ""


@pytest.mark.command
def test_post_not_found():
response = requests.post(URL + "/not-found", data="data")
assert response.status_code == 404
Expand Down
25 changes: 25 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from time import sleep
from typing import Set

import requests

EKILIBRI_URL = "http://localhost:8080"


def assert_health(n: int, status: int):
for _ in range(n):
response = requests.get(EKILIBRI_URL + "/health")
assert response.status_code == status


def assert_health_multiple_status(n: int, statuses: Set[int]):
"""Checks the response for the health endpoint validating the status code
could be of different values."""
for _ in range(n):
response = requests.get(EKILIBRI_URL + "/health")
assert response.status_code in statuses


def wait_fail_window():
"""Waits the fail window for ekilibri to remove the server from the healthy servers list."""
sleep(5)
13 changes: 13 additions & 0 deletions tests/ekilibri-least-connections-timeout.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
servers = [
"127.0.0.1:8081",
"127.0.0.1:8082",
"127.0.0.1:8083"
]
strategy = "LeastConnections"
max_fails = 1
fail_window = 5
connection_timeout = 1000
write_timeout = 1000
read_timeout = 1000
health_check_path = "/sleep"
pool_size = 10
2 changes: 1 addition & 1 deletion tests/ekilibri-least-connections.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ servers = [
]
strategy = "LeastConnections"
max_fails = 1
fail_window = 10
fail_window = 5
connection_timeout = 1000
write_timeout = 1000
read_timeout = 1000
Expand Down
4 changes: 2 additions & 2 deletions tests/ekilibri-round-robin-timeout.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ servers = [
"127.0.0.1:8083"
]
strategy = "RoundRobin"
max_fails = 5
fail_window = 60
max_fails = 1
fail_window = 5
connection_timeout = 1000
write_timeout = 1000
read_timeout = 1000
Expand Down
2 changes: 1 addition & 1 deletion tests/ekilibri-round-robin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ servers = [
]
strategy = "RoundRobin"
max_fails = 1
fail_window = 10
fail_window = 5
connection_timeout = 1000
write_timeout = 1000
read_timeout = 1000
Expand Down
2 changes: 1 addition & 1 deletion tests/ekilibri_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def initialize_command_server(
def kill_process(pid):
if pid != -1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.5)
time.sleep(0.1)


def setup_ekilibri_server(request, config_path: str) -> int:
Expand Down
54 changes: 54 additions & 0 deletions tests/get_ekilibri_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import pytest
import requests

from common import EKILIBRI_URL, assert_health
from ekilibri_setup import kill_process, setup_ekilibri_server


@pytest.mark.parametrize(
"configuration",
[
pytest.param("least-connections", marks=pytest.mark.least_connections),
pytest.param("round-robin", marks=pytest.mark.round_robin),
],
)
def test_multiple_get_request_to_three_servers(request, configuration):
pid = setup_ekilibri_server(request, f"tests/ekilibri-{configuration}.toml")
try:
assert_health(100, status=200)
finally:
kill_process(pid)


@pytest.mark.parametrize(
"configuration",
[
pytest.param("least-connections", marks=pytest.mark.least_connections),
pytest.param("round-robin", marks=pytest.mark.round_robin),
],
)
def test_multiple_get_request_with_timeout(request, configuration):
pid = setup_ekilibri_server(request, f"tests/ekilibri-{configuration}.toml")
try:
for _ in range(10):
response = requests.get(EKILIBRI_URL + "/sleep")
assert response.status_code == 504
finally:
kill_process(pid)


@pytest.mark.parametrize(
"configuration",
[
pytest.param("least-connections", marks=pytest.mark.least_connections),
pytest.param("round-robin", marks=pytest.mark.round_robin),
],
)
def test_get_not_found(request, configuration):
pid = setup_ekilibri_server(request, f"tests/ekilibri-{configuration}.toml")
try:
response = requests.get(EKILIBRI_URL + "/not-found")
assert response.status_code == 404
assert response.text == ""
finally:
kill_process(pid)
25 changes: 25 additions & 0 deletions tests/health_check_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from common import assert_health, wait_fail_window
from ekilibri_setup import kill_process, setup_ekilibri_server


@pytest.mark.parametrize(
"configuration",
[
pytest.param("least-connections", marks=pytest.mark.least_connections),
pytest.param("round-robin", marks=pytest.mark.round_robin),
],
)
def test_get_requests_when_the_health_check_path_is_timing_out(request, configuration):
pid = setup_ekilibri_server(request, f"tests/ekilibri-{configuration}-timeout.toml")
try:
# First requests should be ok, given that Ekilibri still hasn't removed
# the servers from healthy servers list.
assert_health(100, status=200)

wait_fail_window()

assert_health(10, status=502)
finally:
kill_process(pid)
Loading

0 comments on commit fd3c76c

Please sign in to comment.