Skip to content

Commit 5fe1dad

Browse files
committed
feat: Added unit tests for user repository
1 parent 2be6e07 commit 5fe1dad

1 file changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
from unittest.mock import AsyncMock
2+
from uuid import uuid4
3+
4+
import pytest
5+
6+
from src.register_ticket_api.entities import User
7+
from src.register_ticket_api.infraestructure import PostgreSQLDbContext
8+
from src.register_ticket_api.repositories import UserRepository
9+
10+
# Test data constants
11+
TEST_USERNAME: str = "test_user"
12+
TEST_PASSWORD: str = ""
13+
TEST_USER_ID: str = str(uuid4())
14+
SP_INSERT_USER: str = "sp_insert_user"
15+
16+
17+
@pytest.fixture
18+
def mock_db_context() -> AsyncMock:
19+
"""Mock PostgreSQL database context."""
20+
return AsyncMock(spec=PostgreSQLDbContext)
21+
22+
23+
@pytest.fixture
24+
def mock_db_connection() -> AsyncMock:
25+
"""Mock database connection."""
26+
return AsyncMock()
27+
28+
29+
@pytest.fixture
30+
def user_repository(mock_db_context: AsyncMock) -> UserRepository:
31+
"""Create UserRepository instance with mocked database context."""
32+
return UserRepository(db_context=mock_db_context)
33+
34+
35+
@pytest.fixture
36+
def sample_user() -> User:
37+
"""Create a sample user for testing."""
38+
return User(id=uuid4(), username=TEST_USERNAME, password=TEST_PASSWORD)
39+
40+
41+
async def test_get_by_username_success(
42+
user_repository: UserRepository,
43+
mock_db_context: AsyncMock,
44+
mock_db_connection: AsyncMock,
45+
) -> None:
46+
"""Test successful user retrieval by username."""
47+
expected_row = {
48+
"id": TEST_USER_ID,
49+
"username": TEST_USERNAME,
50+
"password": TEST_PASSWORD,
51+
}
52+
mock_db_connection.fetchrow.return_value = expected_row
53+
mock_db_context.get_connection.return_value = mock_db_connection
54+
55+
result = await user_repository.get_by_username(TEST_USERNAME)
56+
57+
assert result is not None
58+
assert result.username == TEST_USERNAME
59+
assert result.password == TEST_PASSWORD
60+
assert str(result.id) == TEST_USER_ID
61+
mock_db_context.get_connection.assert_called_once()
62+
mock_db_connection.fetchrow.assert_called_once()
63+
# Verify the query uses LOWER() for case-insensitive search
64+
call_args = mock_db_connection.fetchrow.call_args
65+
assert "LOWER(username)" in call_args[0][0]
66+
assert "LOWER($1)" in call_args[0][0]
67+
assert call_args[0][1] == TEST_USERNAME
68+
69+
70+
async def test_get_by_username_case_insensitive(
71+
user_repository: UserRepository,
72+
mock_db_context: AsyncMock,
73+
mock_db_connection: AsyncMock,
74+
) -> None:
75+
"""Test that username search is case-insensitive."""
76+
expected_row = {
77+
"id": TEST_USER_ID,
78+
"username": TEST_USERNAME,
79+
"password": TEST_PASSWORD,
80+
}
81+
mock_db_connection.fetchrow.return_value = expected_row
82+
mock_db_context.get_connection.return_value = mock_db_connection
83+
84+
# Search with uppercase username
85+
result = await user_repository.get_by_username("TEST_USER")
86+
87+
assert result is not None
88+
assert result.username == TEST_USERNAME
89+
mock_db_connection.fetchrow.assert_called_once()
90+
91+
92+
async def test_get_by_username_not_found(
93+
user_repository: UserRepository,
94+
mock_db_context: AsyncMock,
95+
mock_db_connection: AsyncMock,
96+
) -> None:
97+
"""Test user retrieval returns None when user does not exist."""
98+
mock_db_connection.fetchrow.return_value = None
99+
mock_db_context.get_connection.return_value = mock_db_connection
100+
101+
result = await user_repository.get_by_username("nonexistent_user")
102+
103+
assert result is None
104+
mock_db_context.get_connection.assert_called_once()
105+
mock_db_connection.fetchrow.assert_called_once()
106+
107+
108+
async def test_create_user_success(
109+
user_repository: UserRepository,
110+
mock_db_context: AsyncMock,
111+
mock_db_connection: AsyncMock,
112+
sample_user: User,
113+
) -> None:
114+
"""Test successful user creation."""
115+
mock_db_connection.execute.return_value = "CREATE" # Non-zero rows affected
116+
mock_db_context.get_connection.return_value = mock_db_connection
117+
118+
result = await user_repository.create_user(sample_user)
119+
120+
assert result is True
121+
mock_db_context.get_connection.assert_called_once()
122+
mock_db_connection.execute.assert_called_once()
123+
124+
# Verify stored procedure call
125+
call_args = mock_db_connection.execute.call_args
126+
assert f"CALL {SP_INSERT_USER}($1, $2)" in call_args[0][0]
127+
assert call_args[0][1] == sample_user.username
128+
assert call_args[0][2] == sample_user.password
129+
130+
131+
async def test_create_user_no_rows_affected(
132+
user_repository: UserRepository,
133+
mock_db_context: AsyncMock,
134+
mock_db_connection: AsyncMock,
135+
sample_user: User,
136+
) -> None:
137+
"""Test user creation returns False when no rows are affected."""
138+
mock_db_connection.execute.return_value = 0
139+
mock_db_context.get_connection.return_value = mock_db_connection
140+
141+
result = await user_repository.create_user(sample_user)
142+
143+
assert result is False
144+
mock_db_context.get_connection.assert_called_once()
145+
mock_db_connection.execute.assert_called_once()

0 commit comments

Comments
 (0)