arbret/backend/tests/test_jobs.py

177 lines
5.3 KiB
Python
Raw Normal View History

"""Tests for job handler logic."""
import json
from contextlib import asynccontextmanager
from unittest.mock import AsyncMock, MagicMock
import pytest
from worker import process_random_number_job
def create_mock_pool(mock_conn: AsyncMock) -> MagicMock:
"""Create a mock asyncpg pool with proper async context manager behavior."""
mock_pool = MagicMock()
@asynccontextmanager
async def mock_acquire():
yield mock_conn
mock_pool.acquire = mock_acquire
return mock_pool
class TestRandomNumberJobHandler:
"""Tests for the random number job handler logic."""
@pytest.mark.asyncio
async def test_generates_random_number_in_range(self):
"""Verify random number is in range [0, 100]."""
# Create mock job
job = MagicMock()
job.id = 123
job.payload = json.dumps({"user_id": 1}).encode()
# Create mock db pool
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
# Run the job handler
await process_random_number_job(job, mock_pool)
# Verify execute was called
mock_conn.execute.assert_called_once()
call_args = mock_conn.execute.call_args
# Extract the value argument (position 3 in the args)
# Args: (query, job_id, user_id, value, duration_ms, status)
value = call_args[0][3]
assert 0 <= value <= 100, f"Value {value} is not in range [0, 100]"
@pytest.mark.asyncio
async def test_stores_correct_user_id(self):
"""Verify the correct user_id is stored in the outcome."""
user_id = 42
job = MagicMock()
job.id = 123
job.payload = json.dumps({"user_id": user_id}).encode()
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
mock_conn.execute.assert_called_once()
call_args = mock_conn.execute.call_args
# Args: (query, job_id, user_id, value, duration_ms, status)
stored_user_id = call_args[0][2]
assert stored_user_id == user_id
@pytest.mark.asyncio
async def test_stores_job_id(self):
"""Verify the job_id is stored in the outcome."""
job_id = 456
job = MagicMock()
job.id = job_id
job.payload = json.dumps({"user_id": 1}).encode()
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
mock_conn.execute.assert_called_once()
call_args = mock_conn.execute.call_args
# Args: (query, job_id, user_id, value, duration_ms, status)
stored_job_id = call_args[0][1]
assert stored_job_id == job_id
@pytest.mark.asyncio
async def test_stores_status_completed(self):
"""Verify the status is set to 'completed'."""
job = MagicMock()
job.id = 123
job.payload = json.dumps({"user_id": 1}).encode()
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
mock_conn.execute.assert_called_once()
call_args = mock_conn.execute.call_args
# Args: (query, job_id, user_id, value, duration_ms, status)
status = call_args[0][5]
assert status == "completed"
@pytest.mark.asyncio
async def test_records_duration_ms(self):
"""Verify duration_ms is recorded (should be >= 0)."""
job = MagicMock()
job.id = 123
job.payload = json.dumps({"user_id": 1}).encode()
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
mock_conn.execute.assert_called_once()
call_args = mock_conn.execute.call_args
# Args: (query, job_id, user_id, value, duration_ms, status)
duration_ms = call_args[0][4]
assert isinstance(duration_ms, int)
assert duration_ms >= 0
@pytest.mark.asyncio
async def test_missing_user_id_does_not_insert(self):
"""Verify no insert happens if user_id is missing from payload."""
job = MagicMock()
job.id = 123
job.payload = json.dumps({}).encode() # Missing user_id
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
# Should not have called execute
mock_conn.execute.assert_not_called()
@pytest.mark.asyncio
async def test_empty_payload_does_not_insert(self):
"""Verify no insert happens with empty payload."""
job = MagicMock()
job.id = 123
job.payload = None
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
# Should not have called execute
mock_conn.execute.assert_not_called()
@pytest.mark.asyncio
async def test_malformed_json_payload_does_not_insert(self):
"""Verify no insert happens with malformed JSON payload."""
job = MagicMock()
job.id = 123
job.payload = b"not valid json {"
mock_conn = AsyncMock()
mock_pool = create_mock_pool(mock_conn)
await process_random_number_job(job, mock_pool)
# Should not have called execute
mock_conn.execute.assert_not_called()