Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ fi
source "$VENV_PATH/bin/activate"

echo "Running integration tests..."
pytest "$PROJECT_ROOT/tests/" "$@"
pytest -v "$PROJECT_ROOT/tests/" "$@"
2 changes: 2 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pytest==8.2.0
requests==2.31.0
websockets==12.0
pytest-asyncio==0.23.6
40 changes: 0 additions & 40 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,13 @@
import pytest
import requests
import uuid
import os

BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:8080")


@pytest.fixture
def unique_user_payload():
"""
Pytest fixture to generate a unique user payload for registration.
"""
unique_id = uuid.uuid4()
return {
"username": f"testuser_{unique_id}",
"password": f"password_{uuid.uuid4()}",
"email": f"test_{unique_id}@example.com",
}


def test_health_check():
"""
Tests if the /health endpoint is working.
"""
response = requests.get(f"{BASE_URL}/health")
assert response.status_code == 200
assert response.text == "OK"


def test_user_registration_success(unique_user_payload):
"""
Tests successful user registration.
"""
response = requests.post(f"{BASE_URL}/register", json=unique_user_payload)
assert response.status_code == 201
response_json = response.json()
assert response_json["status"] == "success"
assert response_json["message"] == "User registered successfully"


def test_user_registration_failure_duplicate_user(unique_user_payload):
"""
Tests that registration fails if the user already exists.
"""
# First registration should succeed
response1 = requests.post(f"{BASE_URL}/register", json=unique_user_payload)
assert response1.status_code == 201

# Second registration with the same data should fail
response2 = requests.post(f"{BASE_URL}/register", json=unique_user_payload)
assert response2.status_code == 409 # Conflict
assert "Username or email already exists" in response2.text
83 changes: 83 additions & 0 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import pytest
import requests
import uuid
import os
import asyncio
import websockets
import json

BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:8080")
WS_URL = f"ws://{BASE_URL.split('//')[1]}/ws" if BASE_URL.startswith("http") else f"ws://{BASE_URL}/ws"


@pytest.fixture
def unique_user_payload():
"""
Pytest fixture to generate a unique user payload for registration.
"""
unique_id = uuid.uuid4()
return {
"username": f"testuser_{unique_id}",
"password": f"password_{unique_id}",
"email": f"test_{unique_id}@example.com",
}


def test_user_registration_success(unique_user_payload):
"""
Tests successful user registration via HTTP.
"""
response = requests.post(f"{BASE_URL}/register", json=unique_user_payload)
assert response.status_code == 201
response_json = response.json()
assert response_json["status"] == "success"
assert response_json["message"] == "User registered successfully"


def test_user_registration_failure_duplicate_user(unique_user_payload):
"""
Tests that registration fails if the user already exists via HTTP.
"""
# First registration should succeed
response1 = requests.post(f"{BASE_URL}/register", json=unique_user_payload)
assert response1.status_code == 201

# Second registration with the same data should fail
response2 = requests.post(f"{BASE_URL}/register", json=unique_user_payload)
assert response2.status_code == 409 # Conflict
assert "Username or email already exists" in response2.text


@pytest.mark.asyncio
async def test_user_registration_and_authentication(unique_user_payload):
"""
Tests that a user can be created and then authenticated via WebSocket.
"""
# Register user via WebSocket to get a token
async with websockets.connect(WS_URL) as websocket:
register_payload = {
"type": "register",
"payload": unique_user_payload
}
await websocket.send(json.dumps(register_payload))

response_str = await websocket.recv()
response_data = json.loads(response_str)

assert response_data.get("type") == "register_success"
token = response_data.get("payload", {}).get("token")
assert token is not None

# Authenticate with the token in a new connection
async with websockets.connect(WS_URL) as websocket:
token_auth_payload = {
"type": "token_auth",
"payload": {"token": token},
}
await websocket.send(json.dumps(token_auth_payload))

response_str = await websocket.recv()
response_data = json.loads(response_str)

assert response_data["type"] == "auth_success"
assert "token" in response_data["payload"]
94 changes: 94 additions & 0 deletions tests/test_queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import requests
import pytest
import uuid
import os
import asyncio
import websockets
import json

BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:8080")
WS_URL = f"ws://{BASE_URL.split('//')[1]}/ws" if BASE_URL.startswith("http") else f"ws://{BASE_URL}/ws"


async def register_and_get_token(username, password, email):
"""Helper function to register a user via WebSocket and get a token."""
async with websockets.connect(WS_URL, open_timeout=30) as websocket:
payload = {"username": username, "password": password, "email": email}
await websocket.send(json.dumps({"type": "register", "payload": payload}))

response = await asyncio.wait_for(websocket.recv(), timeout=30)
data = json.loads(response)

assert data["type"] == "register_success"
assert "token" in data["payload"]
return data["payload"]["token"]


async def player_flow(token):
"""Simulates a player connecting, logging in, and joining the queue."""
# Increased connection timeout
async with websockets.connect(WS_URL, open_timeout=30) as websocket:
# Login with token
await websocket.send(json.dumps({"type": "token_auth", "payload": {"token": token}}))

# Increased receive timeout
login_response = await asyncio.wait_for(websocket.recv(), timeout=30)
login_data = json.loads(login_response)
assert login_data["type"] == "auth_success"

# Join queue
await websocket.send(json.dumps({"type": "join_queue"}))

# Increased receive timeout
queue_response = await asyncio.wait_for(websocket.recv(), timeout=30)
assert json.loads(queue_response) == {"type": "queued"}

# Wait for match
# Increased timeout for match response
match_response = await asyncio.wait_for(websocket.recv(), timeout=40)
match_data = json.loads(match_response)
assert match_data["type"] == "match_found", \
f"Expected match_found, got {match_data.get('type')}. Full payload: {match_data}"
return match_data

@pytest.mark.asyncio
async def test_queue_and_match():
"""
Tests queuing two players and verifying they get matched.
"""
# Generate unique users
uid1 = uuid.uuid4()
user1_username = f"testuser_{uid1}"
user1_password = f"password_{uid1}"
user1_email = f"test_{uid1}@example.com"

uid2 = uuid.uuid4()
user2_username = f"testuser_{uid2}"
user2_password = f"password_{uid2}"
user2_email = f"test_{uid2}@example.com"

# Register users via WebSocket and get tokens
token1 = await register_and_get_token(user1_username, user1_password, user1_email)
token2 = await register_and_get_token(user2_username, user2_password, user2_email)

# Run player flows concurrently
task1 = asyncio.create_task(player_flow(token1))
await asyncio.sleep(1) # a small delay to ensure player 1 joins queue first this is set too high TODO fix
task2 = asyncio.create_task(player_flow(token2))

match_data1, match_data2 = await asyncio.gather(task1, task2)

# Verify match details
# Note: Since the server doesn't tell us our own username back on match,
# we can't easily assert the opponent's name without more complex state tracking.

assert "players" in match_data1["payload"]
assert "players" in match_data2["payload"]
assert match_data1["payload"]["port"] == match_data2["payload"]["port"]
assert "containerID" in match_data1["payload"]
assert "containerID" in match_data2["payload"]

# Check that the players in the match are the correct ones
expected_players = {user1_username, user2_username}
assert set(match_data1["payload"]["players"]) == expected_players
assert set(match_data2["payload"]["players"]) == expected_players