Coverage for tests/route_tests/test_skills.py: 99%

297 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-05 14:02 +0000

1"""Tests for the skills routes.""" 

2 

3# pylint: disable=redefined-outer-name 

4# flake8: noqa: F811 

5 

6import os 

7import sys 

8import uuid 

9from passlib.hash import pbkdf2_sha512 

10 

11from itsdangerous import URLSafeSerializer 

12import pytest 

13from dotenv import load_dotenv 

14 

15# Add the root directory to the Python path 

16sys.path.append( 

17 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 

18) 

19 

20from core import shared 

21from core.database_mongo_manager import DatabaseMongoManager 

22 

23os.environ["IS_TEST"] = "True" 

24 

25load_dotenv() 

26 

27 

28@pytest.fixture() 

29def client(): 

30 """Fixture to create a test client.""" 

31 from ...app import app # pylint: disable=import-outside-toplevel 

32 

33 app.config["TESTING"] = True 

34 return app.test_client() 

35 

36 

37@pytest.fixture() 

38def database(): 

39 """Fixture to create a test database.""" 

40 

41 database = DatabaseMongoManager( 

42 shared.getenv("MONGO_URI"), shared.getenv("MONGO_DB_TEST", "cs3528_testing") 

43 ) 

44 

45 skills = database.get_all("skills") 

46 attempted_skills = database.get_all("attempted_skills") 

47 

48 database.delete_all("skills") 

49 database.delete_all("attempted_skills") 

50 yield database 

51 

52 for skill in skills: 

53 database.insert("skills", skill) 

54 for skill in attempted_skills: 

55 database.insert("attempted_skills", skill) 

56 

57 # Cleanup code 

58 

59 database.connection.close() 

60 

61 

62@pytest.fixture() 

63def user_logged_in_client(client, database: DatabaseMongoManager): 

64 """Fixture to login a user.""" 

65 database.add_table("users") 

66 database.delete_all_by_field("users", "email", "dummy@dummy.com") 

67 

68 user = { 

69 "_id": uuid.uuid4().hex, 

70 "name": "dummy", 

71 "email": "dummy@dummy.com", 

72 "password": pbkdf2_sha512.hash("dummy"), 

73 } 

74 

75 database.insert("users", user) 

76 

77 url = "/user/login" 

78 client.post( 

79 url, 

80 data={ 

81 "email": "dummy@dummy.com", 

82 "password": "dummy", 

83 }, 

84 content_type="application/x-www-form-urlencoded", 

85 ) 

86 

87 yield client 

88 

89 # Cleanup code 

90 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

91 

92 database.delete_all_by_field("users", "email", "dummy@dummy.com") 

93 

94 

95@pytest.fixture() 

96def student_logged_in_client(client, database: DatabaseMongoManager): 

97 """Fixture to login a student.""" 

98 database.add_table("students") 

99 database.delete_all_by_field("students", "email", "dummy@dummy.com") 

100 

101 student = { 

102 "_id": uuid.uuid4().hex, 

103 "first_name": "dummy", 

104 "last_name": "dummy", 

105 "email": "dummy@dummy.com", 

106 "student_id": "123456", 

107 } 

108 

109 database.insert("students", student) 

110 

111 url = "/students/login" 

112 

113 client.post( 

114 url, 

115 data={ 

116 "student_id": "123456", 

117 }, 

118 content_type="application/x-www-form-urlencoded", 

119 ) 

120 otp_serializer = URLSafeSerializer(str(shared.getenv("SECRET_KEY", "secret"))) 

121 

122 with client.session_transaction() as session: 

123 otp = otp_serializer.loads(session["OTP"]) 

124 

125 client.post( 

126 "/students/otp", 

127 data={"otp": otp}, 

128 content_type="application/x-www-form-urlencoded", 

129 ) 

130 

131 yield client 

132 

133 # Cleanup code 

134 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

135 

136 database.delete_all_by_field("students", "email", "dummy@dummy.com") 

137 

138 

139@pytest.fixture() 

140def sample_skill(database): 

141 """Fixture to create a sample course.""" 

142 yield { 

143 "_id": "123", 

144 "skill_name": "Test Skill", 

145 "skill_description": "Test Skill Description", 

146 } 

147 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

148 

149 

150def test_add_skill(user_logged_in_client, database, sample_skill): 

151 """Test adding a skill.""" 

152 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

153 url = "/skills/add_skill" 

154 response = user_logged_in_client.post( 

155 url, 

156 data={ 

157 "skill_name": sample_skill["skill_name"], 

158 "skill_description": sample_skill["skill_description"], 

159 }, 

160 content_type="application/x-www-form-urlencoded", 

161 ) 

162 assert response.status_code == 200 

163 assert database.get_one_by_field("skills", "skill_name", "Test Skill") is not None 

164 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

165 

166 

167def test_delete_skill(user_logged_in_client, database, sample_skill): 

168 """Test deleting a skill.""" 

169 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

170 database.delete_all_by_field("skills", "_id", "123") 

171 

172 database.insert("skills", sample_skill) 

173 url = f"/skills/delete?skill_id={sample_skill['_id']}" 

174 response = user_logged_in_client.delete(url) 

175 assert response.status_code == 200 

176 assert database.get_one_by_id("skills", sample_skill["_id"]) is None 

177 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

178 database.delete_all_by_field("skills", "_id", "123") 

179 

180 

181def test_delete_skill_nonexistent(user_logged_in_client): 

182 """Test deleting a nonexistent skill.""" 

183 url = "/skills/delete?skill_id=nonexistent_id" 

184 response = user_logged_in_client.delete(url) 

185 assert response.status_code == 404 

186 assert response.json["error"] == "Skill not found" 

187 

188 

189def test_add_skill_missing_name_field(user_logged_in_client, database): 

190 """Test adding a skill with missing fields.""" 

191 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

192 

193 url = "/skills/add_skill" 

194 response = user_logged_in_client.post( 

195 url, 

196 data={ 

197 "skill_name": "", 

198 "skill_description": "Programming language", 

199 }, 

200 content_type="application/x-www-form-urlencoded", 

201 ) 

202 assert response.status_code == 400 

203 assert response.json["error"] == "One of the inputs is blank" 

204 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

205 

206 

207def test_add_skill_missing_description_field(user_logged_in_client, database): 

208 """Test adding a skill with missing fields.""" 

209 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

210 url = "/skills/add_skill" 

211 response = user_logged_in_client.post( 

212 url, 

213 data={ 

214 "skill_name": "python", 

215 "skill_description": "", 

216 }, 

217 content_type="application/x-www-form-urlencoded", 

218 ) 

219 assert response.status_code == 400 

220 assert response.json["error"] == "One of the inputs is blank" 

221 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

222 

223 

224def test_add_skill_missing_fields(user_logged_in_client, database): 

225 """Test adding a skill with missing fields.""" 

226 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

227 url = "/skills/add_skill" 

228 response = user_logged_in_client.post( 

229 url, 

230 data={ 

231 "skill_name": "", 

232 "skill_description": "", 

233 }, 

234 content_type="application/x-www-form-urlencoded", 

235 ) 

236 assert response.status_code == 400 

237 assert response.json["error"] == "One of the inputs is blank" 

238 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

239 

240 

241def test_list_skills(user_logged_in_client, database, sample_skill): 

242 """Test listing skills.""" 

243 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

244 database.insert("skills", sample_skill) 

245 url = "/skills/search" 

246 response = user_logged_in_client.get(url) 

247 assert response.status_code == 200 

248 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

249 

250 

251def test_add_skill_duplicate(user_logged_in_client, database, sample_skill): 

252 """Test adding a duplicate skill.""" 

253 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

254 database.delete_all_by_field("skills", "_id", "123") 

255 

256 database.insert("skills", sample_skill) 

257 url = "/skills/add_skill" 

258 response = user_logged_in_client.post( 

259 url, 

260 data={ 

261 "skill_name": sample_skill["skill_name"], 

262 "skill_description": sample_skill["skill_description"], 

263 }, 

264 content_type="application/x-www-form-urlencoded", 

265 ) 

266 assert response.status_code == 400 

267 assert response.json["error"] == "Skill already in database" 

268 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

269 database.delete_all_by_field("skills", "_id", "123") 

270 

271 

272def test_update_skill_nonexistent(user_logged_in_client): 

273 """Test updating a nonexistent skill.""" 

274 url = "/skills/update" 

275 response = user_logged_in_client.post( 

276 url, 

277 data={ 

278 "skill_id": "nonexistent_id", 

279 "skill_name": "Nonexistent Skill", 

280 "skill_description": "Nonexistent description", 

281 }, 

282 content_type="application/x-www-form-urlencoded", 

283 ) 

284 assert response.status_code == 404 

285 assert response.json["error"] == "Skill not found" 

286 

287 

288def test_list_skills_empty(user_logged_in_client, database): 

289 """Test listing skills when no skills exist.""" 

290 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

291 url = "/skills/search" 

292 response = user_logged_in_client.get(url) 

293 assert response.status_code == 200 

294 

295 

296def test_approve_skill_nonexistent(user_logged_in_client): 

297 """Test approving a nonexistent skill.""" 

298 url = "/skills/approve_skill?attempt_skill_id=nonexistent_id" 

299 response = user_logged_in_client.post( 

300 url, 

301 json={"skill_description": "Approved description"}, 

302 content_type="application/json", 

303 ) 

304 assert response.status_code == 404 

305 assert response.json["error"] == "Attempted skill not found" 

306 

307 

308def test_reject_skill_nonexistent(user_logged_in_client): 

309 """Test rejecting a nonexistent skill.""" 

310 url = "/skills/reject_skill?attempt_skill_id=nonexistent_id" 

311 response = user_logged_in_client.post(url) 

312 assert response.status_code == 404 

313 assert response.json["error"] == "Attempted skill not found" 

314 

315 

316def test_update_skill(user_logged_in_client, database, sample_skill): 

317 """Test updating a skill.""" 

318 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

319 database.delete_all_by_field("skills", "skill_name", "Python updated") 

320 database.insert("skills", sample_skill) 

321 url = "/skills/update" 

322 response = user_logged_in_client.post( 

323 url, 

324 data={ 

325 "skill_id": sample_skill["_id"], 

326 "skill_name": "Python Updated", 

327 "skill_description": "Updated description", 

328 }, 

329 content_type="application/x-www-form-urlencoded", 

330 ) 

331 assert response.status_code == 200 

332 updated_skill = database.get_one_by_id("skills", sample_skill["_id"]) 

333 assert updated_skill["skill_name"] == "Python updated" 

334 assert updated_skill["skill_description"] == "Updated description" 

335 

336 database.delete_by_id("skills", sample_skill["_id"]) 

337 

338 

339def test_already_has_description_approve_skill(user_logged_in_client, database): 

340 """Test approving a skill.""" 

341 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

342 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

343 

344 attempt_skill_id = uuid.uuid4().hex 

345 database.insert( 

346 "attempted_skills", 

347 { 

348 "_id": attempt_skill_id, 

349 "skill_name": "Test skill", 

350 "skill_description": "Programming language", 

351 "used": 6, 

352 }, 

353 ) 

354 url = f"/skills/approve_skill?attempt_skill_id={attempt_skill_id}" 

355 response = user_logged_in_client.post( 

356 url, 

357 json={"skill_description": "Approved description"}, 

358 content_type="application/json", 

359 ) 

360 assert response.status_code == 200 

361 approved_skill = database.get_one_by_id("skills", attempt_skill_id) 

362 assert approved_skill is not None 

363 assert approved_skill["skill_description"] == "Approved description" 

364 database.delete_by_id("skills", attempt_skill_id) 

365 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

366 

367 

368def test_already_has_no_description_approve_skill(user_logged_in_client, database): 

369 """Test approving a skill.""" 

370 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

371 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

372 

373 attempt_skill_id = uuid.uuid4().hex 

374 database.insert( 

375 "attempted_skills", 

376 { 

377 "_id": attempt_skill_id, 

378 "skill_name": "Test skill", 

379 "used": 6, 

380 }, 

381 ) 

382 url = f"/skills/approve_skill?attempt_skill_id={attempt_skill_id}" 

383 response = user_logged_in_client.post( 

384 url, 

385 json={"skill_description": "Approved description"}, 

386 content_type="application/json", 

387 ) 

388 assert response.status_code == 200 

389 approved_skill = database.get_one_by_id("skills", attempt_skill_id) 

390 assert approved_skill is not None 

391 assert approved_skill["skill_description"] == "Approved description" 

392 database.delete_by_id("skills", attempt_skill_id) 

393 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

394 

395 

396def test_reject_skill(user_logged_in_client, database): 

397 """Test rejecting a skill.""" 

398 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

399 

400 attempt_skill_id = uuid.uuid4().hex 

401 database.insert( 

402 "attempted_skills", 

403 { 

404 "_id": attempt_skill_id, 

405 "skill_name": "Test Skill", 

406 "skill_description": "Programming language", 

407 }, 

408 ) 

409 url = f"/skills/reject_skill?attempt_skill_id={attempt_skill_id}" 

410 response = user_logged_in_client.post(url) 

411 assert response.status_code == 200 

412 rejected_skill = database.get_one_by_id("attempted_skills", attempt_skill_id) 

413 assert rejected_skill is None 

414 

415 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

416 database.delete_by_id("skills", attempt_skill_id) 

417 

418 

419def test_update_attempted_skill_get(user_logged_in_client, database): 

420 """Test GET request to update attempted skill.""" 

421 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

422 

423 attempt_skill_id = uuid.uuid4().hex 

424 database.insert( 

425 "attempted_skills", 

426 { 

427 "_id": attempt_skill_id, 

428 "skill_name": "Test Skill", 

429 "skill_description": "Test description", 

430 }, 

431 ) 

432 

433 url = f"/skills/update_attempted_skill?attempt_skill_id={attempt_skill_id}" 

434 response = user_logged_in_client.get(url) 

435 assert response.status_code == 200 

436 assert b"Test Skill" in response.data 

437 

438 database.delete_by_id("attempted_skills", attempt_skill_id) 

439 

440 

441def test_update_attempted_skill_post(user_logged_in_client, database): 

442 """Test POST request to update attempted skill.""" 

443 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

444 database.delete_all_by_field("attempted_skills", "skill_name", "Updated Skill") 

445 

446 attempt_skill_id = uuid.uuid4().hex 

447 database.insert( 

448 "attempted_skills", 

449 { 

450 "_id": attempt_skill_id, 

451 "skill_name": "Test Skill", 

452 "skill_description": "Test description", 

453 }, 

454 ) 

455 

456 url = "/skills/update_attempted_skill" 

457 response = user_logged_in_client.post( 

458 url, 

459 data={ 

460 "skill_id": attempt_skill_id, 

461 "skill_name": "Updated Skill", 

462 "skill_description": "Updated description", 

463 }, 

464 content_type="application/x-www-form-urlencoded", 

465 ) 

466 assert response.status_code == 200 

467 updated_skill = database.get_one_by_id("attempted_skills", attempt_skill_id) 

468 assert updated_skill is not None 

469 assert updated_skill["skill_name"] == "updated skill" 

470 assert updated_skill["skill_description"] == "Updated description" 

471 

472 database.delete_by_id("attempted_skills", attempt_skill_id) 

473 database.delete_all_by_field("attempted_skills", "skill_name", "Updated Skill") 

474 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

475 database.delete_all_by_field("skills", "skill_name", "Updated Skill") 

476 

477 

478def test_update_attempted_skill_post_missing_fields(user_logged_in_client): 

479 """Test updating an attempted skill with missing fields.""" 

480 url = "/skills/update_attempted_skill" 

481 response = user_logged_in_client.post( 

482 url, 

483 data={ 

484 "skill_id": "some_id", 

485 "skill_name": "", 

486 "skill_description": "", 

487 }, 

488 content_type="application/x-www-form-urlencoded", 

489 ) 

490 assert response.status_code == 400 

491 assert response.json["error"] == "One of the inputs is blank" 

492 

493 

494def test_update_attempted_skill_get_nonexistent(user_logged_in_client): 

495 """Test GET request for a nonexistent attempted skill.""" 

496 url = "/skills/update_attempted_skill?attempt_skill_id=nonexistent_id" 

497 response = user_logged_in_client.get(url, follow_redirects=True) 

498 assert response.status_code == 404 

499 

500 

501def test_search_attempt_skills_empty(user_logged_in_client, database): 

502 """Test searching for attempted skills when none exist.""" 

503 current_attempted_skills = database.get_all("attempted_skills") 

504 database.delete_all("attempted_skills") 

505 

506 url = "/skills/attempted_skill_search" 

507 response = user_logged_in_client.get(url) 

508 assert response.status_code == 200 

509 

510 for skill in current_attempted_skills: 

511 database.insert("attempted_skills", skill) 

512 

513 

514def test_search_attempt_skills(user_logged_in_client, database): 

515 """Test searching for attempted skills.""" 

516 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill") 

517 

518 attempt_skill_id = uuid.uuid4().hex 

519 database.insert( 

520 "attempted_skills", 

521 { 

522 "_id": attempt_skill_id, 

523 "skill_name": "Test Skill", 

524 "skill_description": "Test description", 

525 }, 

526 ) 

527 

528 url = "/skills/attempted_skill_search" 

529 response = user_logged_in_client.get(url) 

530 assert response.status_code == 200 

531 assert b"Test Skill" in response.data 

532 

533 database.delete_by_id("attempted_skills", attempt_skill_id) 

534 

535 

536def test_update_skill_get(user_logged_in_client, database): 

537 """Test GET request for updating a skill.""" 

538 database.delete_all_by_field("skills", "skill_name", "Test Skill") 

539 

540 skill_id = uuid.uuid4().hex 

541 database.insert( 

542 "skills", 

543 { 

544 "_id": skill_id, 

545 "skill_name": "Test Skill", 

546 "skill_description": "Test description", 

547 }, 

548 ) 

549 

550 url = f"/skills/update?skill_id={skill_id}" 

551 response = user_logged_in_client.get(url) 

552 print(response.data) 

553 assert response.status_code == 200 

554 assert b"Test Skill" in response.data 

555 

556 database.delete_by_id("skills", skill_id) 

557 

558 

559def test_update_skill_post_missing_fields(user_logged_in_client): 

560 """Test updating a skill with missing fields.""" 

561 url = "/skills/update" 

562 response = user_logged_in_client.post( 

563 url, 

564 data={ 

565 "skill_id": "some_id", 

566 "skill_name": "", 

567 "skill_description": "", 

568 }, 

569 content_type="application/x-www-form-urlencoded", 

570 ) 

571 assert response.status_code == 400 

572 assert response.json["error"] == "Missing fields" 

573 

574 

575def test_update_skill_get_nonexistent(user_logged_in_client): 

576 """Test GET request for a nonexistent skill.""" 

577 url = "/skills/update?skill_id=nonexistent_id" 

578 response = user_logged_in_client.get(url, follow_redirects=True) 

579 assert response.status_code == 404 

580 

581 

582def test_attempt_add_skill_student(student_logged_in_client, database): 

583 """Test attempting to add a skill to a student.""" 

584 database.delete_all_by_field("attempted_skills", "skill_name", "test skill") 

585 

586 url = "/skills/attempt_add_skill_student" 

587 response = student_logged_in_client.post( 

588 url, 

589 json={"skill_name": "Test Skill"}, 

590 content_type="application/json", 

591 ) 

592 assert response.status_code == 200 

593 attempted_skill = database.get_one_by_field( 

594 "attempted_skills", "skill_name", "test skill" 

595 ) 

596 assert attempted_skill is not None 

597 

598 database.delete_all_by_field("attempted_skills", "skill_name", "test skill") 

599 

600 

601def test_attempt_add_skill_student_missing_fields(student_logged_in_client): 

602 """Test attempting to add a skill to a student with missing fields.""" 

603 url = "/skills/attempt_add_skill_student" 

604 response = student_logged_in_client.post( 

605 url, 

606 json={"skill_name": ""}, 

607 content_type="application/json", 

608 ) 

609 assert response.status_code == 400 

610 assert response.json["error"] == "Missing skill name" 

611 

612 

613def test_get_skill_page(user_logged_in_client): 

614 """Test getting the skill page.""" 

615 url = "/skills/add_skill" 

616 response = user_logged_in_client.get(url) 

617 assert response.status_code == 200 

618 

619 

620def test_get_skills_page(user_logged_in_client): 

621 """Test getting the skill page.""" 

622 url = "/skills/search" 

623 response = user_logged_in_client.get(url) 

624 assert response.status_code == 200 

625 

626 

627def test_delete_skill_no_skill_id(user_logged_in_client): 

628 """Test deleting a skill without providing skill ID.""" 

629 url = "/skills/delete" 

630 response = user_logged_in_client.delete(url) 

631 assert response.status_code == 400 

632 assert response.json["error"] == "Missing skill ID" 

633 

634 

635def test_one_empty_field_add_skill(user_logged_in_client): 

636 """Test adding a skill with one empty field.""" 

637 url = "/skills/add_skill" 

638 response = user_logged_in_client.post( 

639 url, 

640 data={ 

641 "skill_name": "Test Skill", 

642 "skill_description": "", 

643 }, 

644 content_type="application/x-www-form-urlencoded", 

645 ) 

646 assert response.status_code == 400 

647 assert response.json["error"] == "One of the inputs is blank" 

648 

649 response = user_logged_in_client.post( 

650 url, 

651 data={ 

652 "skill_name": "", 

653 "skill_description": "Test Skill Description", 

654 }, 

655 content_type="application/x-www-form-urlencoded", 

656 ) 

657 

658 assert response.status_code == 400 

659 assert response.json["error"] == "One of the inputs is blank" 

660 

661 

662def test_empty_field_add_skill(user_logged_in_client): 

663 """Test adding a skill with empty fields.""" 

664 url = "/skills/add_skill" 

665 response = user_logged_in_client.post( 

666 url, 

667 data={ 

668 "skill_name": "", 

669 "skill_description": "", 

670 }, 

671 content_type="application/x-www-form-urlencoded", 

672 ) 

673 assert response.status_code == 400 

674 assert response.json["error"] == "One of the inputs is blank"