From 0452d7ebc17e77ec037fb477b167de74fd397dc1 Mon Sep 17 00:00:00 2001 From: Sander Johansen Date: Fri, 4 Apr 2025 00:03:53 +0200 Subject: [PATCH] Tests --- backend/tests/Dockerfile.test | 2 +- backend/tests/test_athletes_files.py | 54 ++++++++++++++ backend/tests/test_signup.py | 106 +++++++++++++++++++++++++++ backend/tests/test_template.py | 16 ---- backend/tests/test_workouts.py | 93 +++++++++++++++++++++++ docker-compose.stag.yml | 6 +- 6 files changed, 257 insertions(+), 20 deletions(-) create mode 100644 backend/tests/test_athletes_files.py create mode 100644 backend/tests/test_signup.py delete mode 100644 backend/tests/test_template.py create mode 100644 backend/tests/test_workouts.py diff --git a/backend/tests/Dockerfile.test b/backend/tests/Dockerfile.test index 7c28661..9d40813 100644 --- a/backend/tests/Dockerfile.test +++ b/backend/tests/Dockerfile.test @@ -13,4 +13,4 @@ WORKDIR /app EXPOSE 8000 # Runs migrations and starts the server -CMD ["sh", "-c", "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"] +CMD ["sh", "-c", "python manage.py migrate && python manage.py test tests && python manage.py runserver 0.0.0.0:8000"] diff --git a/backend/tests/test_athletes_files.py b/backend/tests/test_athletes_files.py new file mode 100644 index 0000000..4a8b5d9 --- /dev/null +++ b/backend/tests/test_athletes_files.py @@ -0,0 +1,54 @@ +from django.test import TestCase +from rest_framework.test import APIClient +from django.urls import reverse +from django.contrib.auth import get_user_model +from django.core.files.uploadedfile import SimpleUploadedFile +from users.models import AthleteFile + +class TestAthleteFileUpload(TestCase): + def setUp(self): + self.client = APIClient() + User = get_user_model() + self.coach = User.objects.create_user(username="coachuser", password="password") + self.coach.isCoach = True + self.coach.save() + self.athlete = User.objects.create_user(username="athleteuser", password="password") + self.athlete.coach = self.coach + self.athlete.save() + self.upload_url = reverse("athlete-file-list") + + def create_dummy_file(self, size_kb, filename): + size_bytes = int(size_kb * 1024 * 1024) + file_content = b'\x00' * size_bytes + return SimpleUploadedFile(name=filename, content=file_content, content_type="application/pdf") + + def get_user_url(self, user): + relative_url = reverse("user-detail", kwargs={"pk": user.id}) + return "http://testserver" + relative_url + + def test_coach_can_upload_for_athlete_valid_file(self): + """ + Verify that a file with exactly 5 MB is accepted and a file with 5.1 MB is rejected. + """ + self.client.force_authenticate(user=self.coach) + valid_file = self.create_dummy_file(5, "valid.pdf") # 5 MB + data = { + "file": valid_file, + "athlete": self.get_user_url(self.athlete), + "owner": self.coach.id + } + response = self.client.post("/api/athlete-files/", data, format="multipart") + print("Response data (coach upload):", response.data) + print("Response status code (coach upload):", response.status_code) + self.assertEqual(response.status_code, 201) + + invalid_file = self.create_dummy_file(5.1, "invalid.pdf") # 5.1 MB + data = { + "file": invalid_file, + "athlete": self.get_user_url(self.athlete), + "owner": self.coach.id + } + response = self.client.post("/api/athlete-files/", data, format="multipart") + print("Response data (coach upload):", response.data) + print("Response status code (coach upload):", response.status_code) + self.assertEqual(response.status_code, 400) diff --git a/backend/tests/test_signup.py b/backend/tests/test_signup.py new file mode 100644 index 0000000..2c3c733 --- /dev/null +++ b/backend/tests/test_signup.py @@ -0,0 +1,106 @@ +from django.test import TestCase +from rest_framework.test import APIClient +from django.contrib.auth import get_user_model + +class TestSignupAPI(TestCase): + def setUp(self): + self.client = APIClient() + + def test_signup_valid(self): + """ + Test that a new user can successfully sign up with a valid email. + """ + payload = { + "username": "newuser", + "email": "newuser@example.com", + "isCoach": "false", + "password": "testpassword!", + "password1": "testpassword!", + "athletes": [], + "workouts": [], + "coach_files": [], + "athlete_files": [] + } + response = self.client.post("/api/users/", payload, format="json") + print(f"Valid signup response data: {response.data}") + print(f"Valid signup status code: {response.status_code}") + self.assertEqual(response.status_code, 201) + self.assertTrue(get_user_model().objects.filter(username="newuser").exists()) + + def test_signup_invalid_email(self): + """ + Test that a new user cannot sign up with an invalid email. + """ + payload = { + "username": "newuser", + "email": "newuserexample.com", + "isCoach": "false", + "password": "testpassword!", + "password1": "testpassword!", + "athletes": [], + "workouts": [], + "coach_files": [], + "athlete_files": [] + } + response = self.client.post("/api/users/", payload, format="json") + print(f"Invalid signup response data: {response.data}") + print(f"Invalid signup status code: {response.status_code}") + self.assertEqual(response.status_code, 400) + + def test_signup_duplicate_username(self): + """ + Test that a new user cannot sign up if the username is already registered. + """ + payload = { + "username": "username", + "email": "user1@example.com", + "isCoach": "false", + "password": "testpassword!", + "password1": "testpassword!", + "athletes": [], + "workouts": [], + "coach_files": [], + "athlete_files": [] + } + # First signup should succeed. + response1 = self.client.post("/api/users/", payload, format="json") + print(f"First signup response data: {response1.data}") + print(f"First signup status code: {response1.status_code}") + self.assertEqual(response1.status_code, 201) + self.assertTrue(get_user_model().objects.filter(username="username").exists()) + + # Attempt to sign up with the same username but a different email. + payload["email"] = "user2@example.com" # Change email, keep username same. + response2 = self.client.post("/api/users/", payload, format="json") + print(f"Duplicate username signup response data: {response2.data}") + print(f"Duplicate username signup status code: {response2.status_code}") + self.assertEqual(response2.status_code, 400) + + def test_signup_duplicate_email(self): + """ + Test that a new user cannot sign up if the email is already registered. + """ + payload = { + "username": "username", + "email": "email@example.com", + "isCoach": "false", + "password": "testpassword!", + "password1": "testpassword!", + "athletes": [], + "workouts": [], + "coach_files": [], + "athlete_files": [] + } + # First signup should succeed. + response1 = self.client.post("/api/users/", payload, format="json") + print(f"First signup response data: {response1.data}") + print(f"First signup status code: {response1.status_code}") + self.assertEqual(response1.status_code, 201) + self.assertTrue(get_user_model().objects.filter(username="username").exists()) + + # Attempt to sign up with the same email but a different username. + payload["username"] = "username2" # Change username, keep email same. + response2 = self.client.post("/api/users/", payload, format="json") + print(f"Duplicate username signup response data: {response2.data}") + print(f"Duplicate username signup status code: {response2.status_code}") + self.assertEqual(response2.status_code, 400) diff --git a/backend/tests/test_template.py b/backend/tests/test_template.py deleted file mode 100644 index 4a3dda4..0000000 --- a/backend/tests/test_template.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.test import TestCase -from workouts.models import Exercise - - -class User(TestCase): - def setUp(self): - Exercise.objects.create(name="Pushup", description="Pushup", unit="reps") - Exercise.objects.create(name="Running", description="Running", unit="minutes") - Exercise.objects.create(name="Dumbbell curl", description="Lifting dumbbells", unit="reps") - - def test_user_has_coach(self): - all_exercises = Exercise.objects.all() - num_reps = Exercise.objects.filter(unit="reps") - - self.assertEqual(len(all_exercises), 3) - self.assertEqual(len(num_reps), 2) diff --git a/backend/tests/test_workouts.py b/backend/tests/test_workouts.py new file mode 100644 index 0000000..f4326dd --- /dev/null +++ b/backend/tests/test_workouts.py @@ -0,0 +1,93 @@ +from django.test import TestCase +from rest_framework.test import APIClient +from django.utils.timezone import now +from workouts.models import Workout +from django.contrib.auth import get_user_model + +class TestWorkoutAPI(TestCase): + def setUp(self): + self.client = APIClient() + self.user = get_user_model().objects.create_user(username="testuser", password="password") + self.client.force_authenticate(self.user) + + def test_create_workout_valid_date(self): + response = self.client.post("/api/workouts/", { + "name": "Morning Run", + "date": now().isoformat(), + "notes": "5km run", + "visibility": "PU", + "owner": self.user.id, + "exercise_instances": [] + }, format="json") + print(f"Response data: {response.data}") + print(f"Response status code: {response.status_code}") + assert response.status_code == 201 + assert Workout.objects.count() == 1 + + def test_create_workout_invalid_date(self): + response = self.client.post("/api/workouts/", { + "name": "Invalid Run", + "date": "invalid_date", + "notes": "Wrong format", + "visibility": "PU", + "owner": self.user.id, + "exercise_instances": [] + }, format="json") + print(f"Error response data: {response.data}") + print(f"Error response status code: {response.status_code}") + assert response.status_code == 400 + + def test_create_workout_with_other_owner(self): + """ + Test that when attempting to create a workout with an owner different from the authenticated user, + the workout is still created with the authenticated user as owner. + """ + other_user = get_user_model().objects.create_user(username="otheruser", password="password") + response = self.client.post("/api/workouts/", { + "name": "Unauthorized Owner Run", + "date": now().isoformat(), + "notes": "Attempting to assign a different owner", + "visibility": "PU", + "owner": other_user.id, # Attempt to override the owner field + "exercise_instances": [] + }, format="json") + print(f"Response data: {response.data}") + print(f"Response status code: {response.status_code}") + # The request should succeed (201) but the owner should be the authenticated user + assert response.status_code == 201 + workout = Workout.objects.first() + assert workout.owner.id == self.user.id + + def test_create_workout_leap_year_valid(self): + """ + Test that a workout can be created with a valid leap year date. + February 29, 2024 is a valid date. + """ + response = self.client.post("/api/workouts/", { + "name": "Leap Year Run", + "date": "2024-02-29T00:00:00", # Valid leap year date + "notes": "Leap year workout", + "visibility": "PU", + "owner": self.user.id, + "exercise_instances": [] + }, format="json") + print(f"Response data (leap year valid): {response.data}") + print(f"Response status code (leap year valid): {response.status_code}") + assert response.status_code == 201 + + def test_create_workout_leap_year_invalid(self): + """ + Test that a workout cannot be created with an invalid leap year date. + February 29, 2025 is invalid because 2025 is not a leap year. + """ + response = self.client.post("/api/workouts/", { + "name": "Invalid Leap Year Run", + "date": "2025-02-29T00:00:00", # Invalid leap year date + "notes": "Invalid leap year workout", + "visibility": "PU", + "owner": self.user.id, + "exercise_instances": [] + }, format="json") + print(f"Error response data (leap year invalid): {response.data}") + print(f"Error response status code (leap year invalid): {response.status_code}") + assert response.status_code == 400 \ No newline at end of file diff --git a/docker-compose.stag.yml b/docker-compose.stag.yml index 7655a4d..b11bb66 100644 --- a/docker-compose.stag.yml +++ b/docker-compose.stag.yml @@ -4,10 +4,10 @@ services: backend: container_name: secfit_stag_backend build: - context: backend/ + context: backend network: host - dockerfile: Dockerfile.test + dockerfile: tests/Dockerfile.test environment: - DEBUG=1 ports: - - ${STAG_PORT_PREFIX}4:8000 + - ${STAG_PORT_PREFIX}4:8000 \ No newline at end of file