Coverage for tests \ test_main.py: 100%
189 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-06 20:52 +0300
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-06 20:52 +0300
1import pytest
2from fastapi.testclient import TestClient
3from sqlalchemy import create_engine
4from sqlalchemy.orm import sessionmaker
5from src.main import app, get_db, Base, get_password_hash
6from unittest.mock import Mock, patch
8SQLALCHEMY_TEST_URL = 'sqlite:///./test.db'
9engine = create_engine(SQLALCHEMY_TEST_URL, connect_args={'check_same_thread': False})
10TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
12@pytest.fixture
13def db():
14 Base.metadata.create_all(bind=engine)
15 db = TestingSessionLocal()
16 try:
17 yield db
18 finally:
19 db.close()
20 Base.metadata.drop_all(bind=engine)
22@pytest.fixture
23def client(db):
24 def override_get_db():
25 try:
26 yield db
27 finally:
28 pass
30 app.dependency_overrides[get_db] = override_get_db
31 yield TestClient(app)
32 app.dependency_overrides.clear()
34@pytest.fixture
35def auth_token(client):
36 client.post('/register', params={'username': 'testuser', 'password': 'test123'})
37 response = client.post('/token', data={'username': 'testuser', 'password': 'test123'})
38 return response.json()['access_token']
40def test_password_hashing():
41 password = 'mypass123'
42 hashed = get_password_hash(password)
43 assert hashed != password
44 assert isinstance(hashed, str)
46def test_register_success(client):
47 response = client.post('/register', params={'username': 'newuser', 'password': 'pass123'})
48 assert response.status_code == 200
49 assert 'id' in response.json()
51def test_register_duplicate(client):
52 client.post('/register', params={'username': 'duplicate', 'password': 'pass'})
53 response = client.post('/register', params={'username': 'duplicate', 'password': 'pass'})
54 assert response.status_code == 400
56def test_login_success(client):
57 client.post('/register', params={'username': 'logintest', 'password': 'pass123'})
58 response = client.post('/token', data={'username': 'logintest', 'password': 'pass123'})
59 assert response.status_code == 200
60 assert 'access_token' in response.json()
62def test_login_wrong_password(client):
63 client.post('/register', params={'username': 'wrongpass', 'password': 'correct'})
64 response = client.post('/token', data={'username': 'wrongpass', 'password': 'wrong'})
65 assert response.status_code == 401
67def test_invalid_token(client):
68 headers = {'Authorization': 'Bearer invalid_token_12345'}
69 response = client.get('/tasks', headers=headers)
70 assert response.status_code == 401
72def test_create_task(client, auth_token):
73 headers = {'Authorization': f'Bearer {auth_token}'}
74 response = client.post('/tasks', params={'title': 'My Task'}, headers=headers)
75 assert response.status_code == 200
76 assert response.json()['title'] == 'My Task'
78def test_create_task_with_all_params(client, auth_token):
79 headers = {'Authorization': f'Bearer {auth_token}'}
80 response = client.post(
81 '/tasks',
82 params={
83 'title': 'Full Task',
84 'description': 'Full description',
85 'priority': 'high'
86 },
87 headers=headers
88 )
89 assert response.status_code == 200
90 assert response.json()['description'] == 'Full description'
91 assert response.json()['priority'] == 'high'
93def test_create_task_unauthorized(client):
94 response = client.post('/tasks', params={'title': 'No Auth'})
95 assert response.status_code == 401
97def test_get_tasks(client, auth_token):
98 headers = {'Authorization': f'Bearer {auth_token}'}
99 client.post('/tasks', params={'title': 'Task 1'}, headers=headers)
100 client.post('/tasks', params={'title': 'Task 2'}, headers=headers)
102 response = client.get('/tasks', headers=headers)
103 assert response.status_code == 200
104 assert len(response.json()) == 2
106def test_get_task(client, auth_token):
107 headers = {'Authorization': f'Bearer {auth_token}'}
108 create = client.post('/tasks', params={'title': 'Specific'}, headers=headers)
109 task_id = create.json()['id']
111 response = client.get(f'/tasks/{task_id}', headers=headers)
112 assert response.status_code == 200
113 assert response.json()['title'] == 'Specific'
115def test_get_task_not_found(client, auth_token):
116 headers = {'Authorization': f'Bearer {auth_token}'}
117 response = client.get('/tasks/99999', headers=headers)
118 assert response.status_code == 404
120def test_update_task(client, auth_token):
121 headers = {'Authorization': f'Bearer {auth_token}'}
122 create = client.post('/tasks', params={'title': 'Old'}, headers=headers)
123 task_id = create.json()['id']
125 response = client.put(f'/tasks/{task_id}', params={'title': 'New', 'status': 'completed'}, headers=headers)
126 assert response.status_code == 200
127 assert response.json()['title'] == 'New'
128 assert response.json()['status'] == 'completed'
130def test_update_task_partial(client, auth_token):
131 headers = {'Authorization': f'Bearer {auth_token}'}
132 create = client.post('/tasks',
133 params={'title': 'Test', 'description': 'Old desc', 'status': 'pending', 'priority': 'low'},
134 headers=headers)
135 task_id = create.json()['id']
137 r1 = client.put(f'/tasks/{task_id}', params={'priority': 'high'}, headers=headers)
138 assert r1.json()['priority'] == 'high'
140 r2 = client.put(f'/tasks/{task_id}', params={'description': 'New desc'}, headers=headers)
141 assert r2.json()['description'] == 'New desc'
143 r3 = client.put(f'/tasks/{task_id}', params={'status': 'completed'}, headers=headers)
144 assert r3.json()['status'] == 'completed'
146def test_update_nonexistent_task(client, auth_token):
147 headers = {'Authorization': f'Bearer {auth_token}'}
148 response = client.put('/tasks/99999', params={'title': 'New'}, headers=headers)
149 assert response.status_code == 404
151def test_delete_task(client, auth_token):
152 headers = {'Authorization': f'Bearer {auth_token}'}
153 create = client.post('/tasks', params={'title': 'To Delete'}, headers=headers)
154 task_id = create.json()['id']
156 response = client.delete(f'/tasks/{task_id}', headers=headers)
157 assert response.status_code == 200
159 get_response = client.get(f'/tasks/{task_id}', headers=headers)
160 assert get_response.status_code == 404
162def test_delete_nonexistent_task(client, auth_token):
163 headers = {'Authorization': f'Bearer {auth_token}'}
164 response = client.delete('/tasks/99999', headers=headers)
165 assert response.status_code == 404
167def test_get_top_tasks(client, auth_token):
168 headers = {'Authorization': f'Bearer {auth_token}'}
169 client.post('/tasks', params={'title': 'High', 'priority': 'high'}, headers=headers)
170 client.post('/tasks', params={'title': 'Medium', 'priority': 'medium'}, headers=headers)
171 client.post('/tasks', params={'title': 'Low', 'priority': 'low'}, headers=headers)
173 response = client.get('/tasks/top/2', headers=headers)
174 assert response.status_code == 200
175 assert len(response.json()) == 2
177 response2 = client.get('/tasks/top/10', headers=headers)
178 assert response2.status_code == 200
179 assert len(response2.json()) == 3
181 response3 = client.get('/tasks/top/0', headers=headers)
182 assert response3.status_code == 200
183 assert len(response3.json()) == 0
185def test_search_tasks(client, auth_token):
186 headers = {'Authorization': f'Bearer {auth_token}'}
187 client.post('/tasks', params={'title': 'Special Task', 'description': 'Important'}, headers=headers)
189 response1 = client.get('/tasks', params={'search': 'Special'}, headers=headers)
190 assert response1.status_code == 200
191 assert len(response1.json()) == 1
192 response2 = client.get('/tasks', params={'search': 'nonexistentword'}, headers=headers)
193 assert response2.status_code == 200
194 assert len(response2.json()) == 0
196def test_sort_tasks(client, auth_token):
197 headers = {'Authorization': f'Bearer {auth_token}'}
198 client.post('/tasks', params={'title': 'B Task'}, headers=headers)
199 client.post('/tasks', params={'title': 'A Task'}, headers=headers)
200 response_asc = client.get('/tasks', params={'sort_by': 'title'}, headers=headers)
201 titles_asc = [t['title'] for t in response_asc.json()]
202 assert titles_asc == sorted(titles_asc)
204 response_desc = client.get('/tasks', params={'sort_by': 'title', 'sort_desc': True}, headers=headers)
205 titles_desc = [t['title'] for t in response_desc.json()]
206 assert titles_desc == sorted(titles_desc, reverse=True)
208 response_status = client.get('/tasks', params={'sort_by': 'status'}, headers=headers)
209 assert response_status.status_code == 200
211 response_created = client.get('/tasks', params={'sort_by': 'created_at'}, headers=headers)
212 assert response_created.status_code == 200
215def test_user_isolation(client):
216 client.post('/register', params={'username': 'user1', 'password': 'pass1'})
217 token1 = client.post('/token', data={'username': 'user1', 'password': 'pass1'}).json()['access_token']
218 headers1 = {'Authorization': f'Bearer {token1}'}
220 client.post('/register', params={'username': 'user2', 'password': 'pass2'})
221 token2 = client.post('/token', data={'username': 'user2', 'password': 'pass2'}).json()['access_token']
222 headers2 = {'Authorization': f'Bearer {token2}'}
224 task = client.post('/tasks', params={'title': 'Secret'}, headers=headers1)
225 task_id = task.json()['id']
227 response = client.get(f'/tasks/{task_id}', headers=headers2)
228 assert response.status_code == 404
231def test_get_current_user_with_mock():
232 mock_db = Mock()
233 mock_token = 'fake_token'
235 with patch('src.main.jwt.decode') as mock_decode:
236 mock_decode.return_value = {'sub': 'testuser'}
237 mock_user = Mock(username='testuser', id=1)
238 mock_db.query.return_value.filter.return_value.first.return_value = mock_user
240 from src.main import get_current_user
241 user = get_current_user(mock_token, mock_db)
243 assert user.username == 'testuser'
244 mock_decode.assert_called_once()
247def test_verify_password_with_mock():
248 from src.main import verify_password
250 with patch('src.main.pwd_context.verify') as mock_verify:
251 mock_verify.return_value = True
252 result = verify_password('testpass', 'hashed')
253 assert result is True
254 mock_verify.assert_called_once_with('testpass', 'hashed')