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
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-05 14:02 +0000
1"""Tests for the skills routes."""
3# pylint: disable=redefined-outer-name
4# flake8: noqa: F811
6import os
7import sys
8import uuid
9from passlib.hash import pbkdf2_sha512
11from itsdangerous import URLSafeSerializer
12import pytest
13from dotenv import load_dotenv
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)
20from core import shared
21from core.database_mongo_manager import DatabaseMongoManager
23os.environ["IS_TEST"] = "True"
25load_dotenv()
28@pytest.fixture()
29def client():
30 """Fixture to create a test client."""
31 from ...app import app # pylint: disable=import-outside-toplevel
33 app.config["TESTING"] = True
34 return app.test_client()
37@pytest.fixture()
38def database():
39 """Fixture to create a test database."""
41 database = DatabaseMongoManager(
42 shared.getenv("MONGO_URI"), shared.getenv("MONGO_DB_TEST", "cs3528_testing")
43 )
45 skills = database.get_all("skills")
46 attempted_skills = database.get_all("attempted_skills")
48 database.delete_all("skills")
49 database.delete_all("attempted_skills")
50 yield database
52 for skill in skills:
53 database.insert("skills", skill)
54 for skill in attempted_skills:
55 database.insert("attempted_skills", skill)
57 # Cleanup code
59 database.connection.close()
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")
68 user = {
69 "_id": uuid.uuid4().hex,
70 "name": "dummy",
71 "email": "dummy@dummy.com",
72 "password": pbkdf2_sha512.hash("dummy"),
73 }
75 database.insert("users", user)
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 )
87 yield client
89 # Cleanup code
90 database.delete_all_by_field("skills", "skill_name", "Test Skill")
92 database.delete_all_by_field("users", "email", "dummy@dummy.com")
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")
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 }
109 database.insert("students", student)
111 url = "/students/login"
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")))
122 with client.session_transaction() as session:
123 otp = otp_serializer.loads(session["OTP"])
125 client.post(
126 "/students/otp",
127 data={"otp": otp},
128 content_type="application/x-www-form-urlencoded",
129 )
131 yield client
133 # Cleanup code
134 database.delete_all_by_field("skills", "skill_name", "Test Skill")
136 database.delete_all_by_field("students", "email", "dummy@dummy.com")
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")
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")
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")
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")
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"
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")
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")
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")
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")
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")
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")
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")
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"
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
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"
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"
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"
336 database.delete_by_id("skills", sample_skill["_id"])
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")
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")
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")
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")
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")
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
415 database.delete_all_by_field("attempted_skills", "skill_name", "Test Skill")
416 database.delete_by_id("skills", attempt_skill_id)
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")
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 )
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
438 database.delete_by_id("attempted_skills", attempt_skill_id)
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")
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 )
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"
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")
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"
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
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")
506 url = "/skills/attempted_skill_search"
507 response = user_logged_in_client.get(url)
508 assert response.status_code == 200
510 for skill in current_attempted_skills:
511 database.insert("attempted_skills", skill)
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")
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 )
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
533 database.delete_by_id("attempted_skills", attempt_skill_id)
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")
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 )
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
556 database.delete_by_id("skills", skill_id)
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"
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
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")
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
598 database.delete_all_by_field("attempted_skills", "skill_name", "test skill")
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"
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
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
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"
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"
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 )
658 assert response.status_code == 400
659 assert response.json["error"] == "One of the inputs is blank"
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"