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

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 

7 

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) 

11 

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) 

21 

22@pytest.fixture 

23def client(db): 

24 def override_get_db(): 

25 try: 

26 yield db 

27 finally: 

28 pass 

29 

30 app.dependency_overrides[get_db] = override_get_db 

31 yield TestClient(app) 

32 app.dependency_overrides.clear() 

33 

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'] 

39 

40def test_password_hashing(): 

41 password = 'mypass123' 

42 hashed = get_password_hash(password) 

43 assert hashed != password 

44 assert isinstance(hashed, str) 

45 

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() 

50 

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 

55 

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() 

61 

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 

66 

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 

71 

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' 

77 

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' 

92 

93def test_create_task_unauthorized(client): 

94 response = client.post('/tasks', params={'title': 'No Auth'}) 

95 assert response.status_code == 401 

96 

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) 

101 

102 response = client.get('/tasks', headers=headers) 

103 assert response.status_code == 200 

104 assert len(response.json()) == 2 

105 

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'] 

110 

111 response = client.get(f'/tasks/{task_id}', headers=headers) 

112 assert response.status_code == 200 

113 assert response.json()['title'] == 'Specific' 

114 

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 

119 

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'] 

124 

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' 

129 

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'] 

136 

137 r1 = client.put(f'/tasks/{task_id}', params={'priority': 'high'}, headers=headers) 

138 assert r1.json()['priority'] == 'high' 

139 

140 r2 = client.put(f'/tasks/{task_id}', params={'description': 'New desc'}, headers=headers) 

141 assert r2.json()['description'] == 'New desc' 

142 

143 r3 = client.put(f'/tasks/{task_id}', params={'status': 'completed'}, headers=headers) 

144 assert r3.json()['status'] == 'completed' 

145 

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 

150 

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'] 

155 

156 response = client.delete(f'/tasks/{task_id}', headers=headers) 

157 assert response.status_code == 200 

158 

159 get_response = client.get(f'/tasks/{task_id}', headers=headers) 

160 assert get_response.status_code == 404 

161 

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 

166 

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) 

172 

173 response = client.get('/tasks/top/2', headers=headers) 

174 assert response.status_code == 200 

175 assert len(response.json()) == 2 

176 

177 response2 = client.get('/tasks/top/10', headers=headers) 

178 assert response2.status_code == 200 

179 assert len(response2.json()) == 3 

180 

181 response3 = client.get('/tasks/top/0', headers=headers) 

182 assert response3.status_code == 200 

183 assert len(response3.json()) == 0 

184 

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) 

188 

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 

195 

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) 

203 

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) 

207 

208 response_status = client.get('/tasks', params={'sort_by': 'status'}, headers=headers) 

209 assert response_status.status_code == 200 

210 

211 response_created = client.get('/tasks', params={'sort_by': 'created_at'}, headers=headers) 

212 assert response_created.status_code == 200 

213 

214 

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}'} 

219 

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}'} 

223 

224 task = client.post('/tasks', params={'title': 'Secret'}, headers=headers1) 

225 task_id = task.json()['id'] 

226 

227 response = client.get(f'/tasks/{task_id}', headers=headers2) 

228 assert response.status_code == 404 

229 

230 

231def test_get_current_user_with_mock(): 

232 mock_db = Mock() 

233 mock_token = 'fake_token' 

234 

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 

239 

240 from src.main import get_current_user 

241 user = get_current_user(mock_token, mock_db) 

242 

243 assert user.username == 'testuser' 

244 mock_decode.assert_called_once() 

245 

246 

247def test_verify_password_with_mock(): 

248 from src.main import verify_password 

249 

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')