Repository: Learnathon-By-Geeky-Solutions/h3cker
Pull request #57: code coverage for current codebase
Author: login: ismailariyan
Created: 2025-04-19T09:09:56Z | Merged: 2025-04-19T10:30:11Z



Diff:
diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml
index f52670a..b47a2ab 100644
--- a/.github/workflows/sonarqube.yml
+++ b/.github/workflows/sonarqube.yml
@@ -12,16 +12,87 @@ jobs:
   analyze:
     name: Analyze with SonarCloud
     runs-on: ubuntu-latest
-    
+
+    # Add PostgreSQL service container
+    services:
+      postgres:
+        image: postgres:14 # Or your preferred version
+        env:
+          POSTGRES_USER: testuser
+          POSTGRES_PASSWORD: testpassword
+          POSTGRES_DB: testdb
+        ports:
+          - 5432:5432 # Map port to the host runner
+        # Options to wait for the database to be ready
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+
+    # Set environment variables for the job to use the service DB
+    # These override any values that might be loaded from a .env file by python-dotenv
+    # and are directly used by settings.py via os.getenv()
+    env:
+      # Override database settings for CI
+      ENVIRONMENT: LOCAL # Use a specific value for CI if needed, or keep LOCAL/PROD logic
+      LOCAL_DB_NAME: testdb # Match service POSTGRES_DB
+      LOCAL_DB_USER: testuser # Match service POSTGRES_USER
+      LOCAL_DB_PASSWORD: testpassword # Match service POSTGRES_PASSWORD
+      LOCAL_DB_HOST: localhost # Service is available on localhost within the job
+      LOCAL_DB_PORT: 5432 # Mapped port
+      # Keep other necessary env vars (can still come from secrets if needed)
+      SECRET_KEY: ${{ secrets.SECRET_KEY }} # Still needed
+      DEBUG: "False" # Usually False for CI tests
+      # Add other non-DB vars needed by your app (Azure, Firebase, etc.)
+      AZURE_STORAGE_ACCOUNT_NAME: ${{ secrets.AZURE_STORAGE_ACCOUNT_NAME }}
+      AZURE_STORAGE_ACCOUNT_KEY: ${{ secrets.AZURE_STORAGE_ACCOUNT_KEY }}
+      AZURE_VIDEO_CONTAINER_NAME: ${{ secrets.AZURE_VIDEO_CONTAINER_NAME }}
+      AZURE_THUMBNAIL_CONTAINER_NAME: ${{ secrets.AZURE_THUMBNAIL_CONTAINER_NAME }}
+      FIREBASE_TYPE: ${{ secrets.FIREBASE_TYPE }}
+      FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
+      FIREBASE_PRIVATE_KEY_ID: ${{ secrets.FIREBASE_PRIVATE_KEY_ID }}
+      FIREBASE_PRIVATE_KEY: ${{ secrets.FIREBASE_PRIVATE_KEY }}
+      FIREBASE_CLIENT_EMAIL: ${{ secrets.FIREBASE_CLIENT_EMAIL }}
+      FIREBASE_CLIENT_ID: ${{ secrets.FIREBASE_CLIENT_ID }}
+      FIREBASE_AUTH_URI: ${{ secrets.FIREBASE_AUTH_URI }}
+      FIREBASE_TOKEN_URI: ${{ secrets.FIREBASE_TOKEN_URI }}
+      FIREBASE_AUTH_PROVIDER_X509_CERT_URL: ${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}
+      FIREBASE_CLIENT_X509_CERT_URL: ${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}
+      FIREBASE_UNIVERSE_DOMAIN: ${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}
+      # Note: ALLOWED_HOSTS and CORS might need adjustment for CI if tests make external calls
+      # Set SONAR_TOKEN for the SonarCloud Scan step later
+      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
     steps:
       - name: Checkout code
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
+      # Removed "Create Backend .env File" step as env vars are set at job level
+
+      # Add step to run migrations against the service database
+      - name: Run migrations
+        run: python backend/manage.py migrate
+
+      - name: Run tests and generate coverage report
+        run: |
+          cd backend
+          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
       
       - name: SonarCloud Scan
         uses: SonarSource/sonarcloud-github-action@v2
         with:
           projectBaseDir: .
-        env:
-          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
\ No newline at end of file
+        # SONAR_TOKEN is already set in the job's env block
diff --git a/backend/.coveragerc b/backend/.coveragerc
new file mode 100644
index 0000000..abc58b4
--- /dev/null
+++ b/backend/.coveragerc
@@ -0,0 +1,34 @@
+[run]
+source = .
+omit =
+    */migrations/*
+    */tests/*
+    manage.py
+    backend/settings.py
+    backend/wsgi.py
+    backend/asgi.py
+    backend/urls.py
+    backend/firebase.py
+    backend/authentication.py
+    venv/*
+    .*/site-packages/*
+
+[report]
+exclude_lines =
+    # Have to re-enable the standard pragma
+    pragma: no cover
+
+    # Don't complain about missing debug-only code:
+    def __repr__
+    if self\.debug:
+
+    # Don't complain if tests don't hit defensive assertion code:
+    raise AssertionError
+    raise NotImplementedError
+
+    # Don't complain if non-runnable code isn't run:
+    if 0:
+    if __name__ == .__main__.:
+
+    # Don't complain about abstract methods, they aren't run:
+    @(abc\.)?abstractmethod
diff --git a/backend/.env.example b/backend/.env.example
index 66ba89c..17b79b7 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -24,4 +24,19 @@ CORS_ALLOWED_ORIGINS=http://localhost:3000,https://your-frontend-domain.com
 AZURE_STORAGE_ACCOUNT_NAME=your_account_name
 AZURE_STORAGE_ACCOUNT_KEY="your_account_key"
 AZURE_VIDEO_CONTAINER_NAME="container_name"
-AZURE_THUMBNAIL_CONTAINER_NAME="thumbnail_container"
\ No newline at end of file
+AZURE_THUMBNAIL_CONTAINER_NAME="thumbnail_container"
+
+GITHUB_ACTIONS=True
+
+# Firebase Credentials
+FIREBASE_TYPE=""
+FIREBASE_PROJECT_ID=""
+FIREBASE_PRIVATE_KEY_ID=""
+FIREBASE_PRIVATE_KEY=""
+FIREBASE_CLIENT_EMAIL=""
+FIREBASE_CLIENT_ID=""
+FIREBASE_AUTH_URI=""
+FIREBASE_TOKEN_URI=""
+FIREBASE_AUTH_PROVIDER_X509_CERT_URL=""
+FIREBASE_CLIENT_X509_CERT_URL=""
+FIREBASE_UNIVERSE_DOMAIN=""
\ No newline at end of file
diff --git a/backend/.gitignore b/backend/.gitignore
index 6e9bc0c..5f92acf 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -1 +1,3 @@
-*.sqlite3
\ No newline at end of file
+*.sqlite3
+.coverage
+coverage.xml
\ No newline at end of file
diff --git a/backend/api/authentication.py b/backend/api/authentication.py
deleted file mode 100644
index 74f72ae..0000000
--- a/backend/api/authentication.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from rest_framework import authentication
-from rest_framework import exceptions
-from firebase_admin import auth, firestore
-from .models import User  # Import custom User model
-
-db = firestore.client()
-
-class FirebaseAuthentication(authentication.BaseAuthentication):
-    def authenticate(self, request):
-        auth_header = request.META.get("HTTP_AUTHORIZATION")
-        if not auth_header:
-            return None
-            
-        # Check if token format is correct
-        token_parts = auth_header.split()
-        if len(token_parts) != 2 or token_parts[0].lower() != "bearer":
-            return None
-            
-        id_token = token_parts[1]
-        
-        try:
-            # Verify token
-            decoded_token = auth.verify_id_token(id_token)
-            uid = decoded_token["uid"]
-            
-            # Get or create user
-            try:
-                user = User.objects.get(firebase_uid=uid)
-            except User.DoesNotExist:
-                # Fetch additional user data from Firestore
-                user_ref = db.collection('users').document(uid)
-                user_doc = user_ref.get()
-                
-                if user_doc.exists:
-                    user_data = user_doc.to_dict()
-                    first_name = user_data.get('firstName', '')
-                    last_name = user_data.get('lastName', '')
-                    email = user_data.get('email', '')
-                    role = user_data.get('role', 'user')  # Default to regular user
-                else:
-                    # Fallback to Firebase Auth data
-                    firebase_user = auth.get_user(uid)
-                    first_name = ''
-                    last_name = ''
-                    email = firebase_user.email or f"{uid}@example.com"
-                    role = 'user'  # Default role
-                
-                # Create user in our database
-                user = User.objects.create_user(
-                    firebase_uid=uid,
-                    email=email,
-                    first_name=first_name,
-                    last_name=last_name,
-                    role=role
-                )
-                
-                # Create relevant profile based on role
-                if role == 'company':
-                    from .models import CompanyProfile
-                    CompanyProfile.objects.create(user=user)
-                elif role == 'user':
-                    from .models import ViewerProfile
-                    ViewerProfile.objects.create(user=user)
-                
-            return (user, None)
-        except Exception as e:
-            raise exceptions.AuthenticationFailed(f'Invalid token: {str(e)}')
\ No newline at end of file
diff --git a/backend/api/tests/test_models.py b/backend/api/tests/test_models.py
new file mode 100644
index 0000000..4ba545a
--- /dev/null
+++ b/backend/api/tests/test_models.py
@@ -0,0 +1,156 @@
+import pytest
+from django.utils import timezone
+# ValueError is a built-in exception, no need to import from django.core.exceptions
+from api.models import User, Video, CompanyProfile, ViewerProfile
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+# --- User Model Tests ---
+
+def test_create_user():
+    """Test creating a regular user with the custom manager."""
+    user = User.objects.create_user(
+        email='test@example.com',
+        firebase_uid='testuid123',
+        first_name='Test',
+        last_name='User'
+    )
+    assert user.email == 'test@example.com'
+    assert user.firebase_uid == 'testuid123'
+    assert user.first_name == 'Test'
+    assert user.last_name == 'User'
+    assert user.role == 'user'
+    assert user.is_active is True
+    assert user.is_staff is False
+    assert user.is_superuser is False
+    assert user.check_password('') is False # No password set directly
+
+def test_create_user_no_email():
+    """Test creating user without email raises ValueError."""
+    with pytest.raises(ValueError, match='Users must have an email address'):
+        User.objects.create_user(email='', firebase_uid='testuid123')
+
+def test_create_user_no_firebase_uid():
+    """Test creating user without firebase_uid raises ValueError."""
+    with pytest.raises(ValueError, match='Users must have a Firebase UID'):
+        User.objects.create_user(email='test@example.com', firebase_uid='')
+
+def test_create_superuser():
+    """Test creating a superuser."""
+    admin_user = User.objects.create_superuser(
+        email='super@example.com',
+        firebase_uid='superuid456',
+        first_name='Super',
+        last_name='Admin'
+    )
+    assert admin_user.email == 'super@example.com'
+    assert admin_user.firebase_uid == 'superuid456'
+    assert admin_user.role == 'admin'
+    assert admin_user.is_active is True
+    assert admin_user.is_staff is True
+    assert admin_user.is_superuser is True
+
+def test_create_superuser_not_staff():
+    """Test creating superuser with is_staff=False raises ValueError."""
+    with pytest.raises(ValueError, match='Superuser must have is_staff=True.'):
+        User.objects.create_superuser(
+            email='super@example.com', firebase_uid='superuid456', is_staff=False
+        )
+
+def test_create_superuser_not_superuser():
+    """Test creating superuser with is_superuser=False raises ValueError."""
+    with pytest.raises(ValueError, match='Superuser must have is_superuser=True.'):
+        User.objects.create_superuser(
+            email='super@example.com', firebase_uid='superuid456', is_superuser=False
+        )
+
+def test_user_str_method():
+    """Test the string representation of the User model."""
+    user = User.objects.create_user(email='str@example.com', firebase_uid='struid789')
+    assert str(user) == 'str@example.com'
+
+def test_user_get_full_name():
+    """Test the get_full_name method."""
+    user_with_name = User.objects.create_user(
+        email='fullname@example.com', firebase_uid='fullnameuid', first_name='Full', last_name='Name'
+    )
+    user_without_name = User.objects.create_user(
+        email='noname@example.com', firebase_uid='nonameuid'
+    )
+    assert user_with_name.get_full_name() == 'Full Name'
+    assert user_without_name.get_full_name() == 'noname@example.com' # Fallback
+
+# --- Video Model Tests ---
+
+def test_create_video():
+    """Test creating a video instance."""
+    user = User.objects.create_user(email='uploader@example.com', firebase_uid='uploaderuid')
+    video = Video.objects.create(
+        title='Test Video Title',
+        description='A description for the test video.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/video.mp4',
+        thumbnail_url='http://example.com/thumb.jpg',
+        uploader=user,
+        duration='05:30'
+    )
+    assert video.title == 'Test Video Title'
+    assert video.description == 'A description for the test video.'
+    assert video.category == 'Testing'
+    assert video.visibility == 'public'
+    assert video.video_url == 'http://example.com/video.mp4'
+    assert video.thumbnail_url == 'http://example.com/thumb.jpg'
+    assert video.uploader == user
+    assert video.duration == '05:30'
+    assert video.views == 0 # Default value
+    assert video.likes == 0 # Default value
+    assert video.upload_date is not None
+    assert isinstance(video.upload_date, timezone.datetime)
+
+def test_video_default_visibility():
+    """Test the default visibility of a video."""
+    user = User.objects.create_user(email='uploader2@example.com', firebase_uid='uploaderuid2')
+    video = Video.objects.create(
+        title='Default Vis Video',
+        video_url='http://example.com/video2.mp4',
+        uploader=user
+    )
+    assert video.visibility == 'private' # Check default
+
+def test_video_str_method():
+    """Test the string representation of the Video model."""
+    user = User.objects.create_user(email='uploader3@example.com', firebase_uid='uploaderuid3')
+    video = Video.objects.create(
+        title='Video Str Test',
+        video_url='http://example.com/video3.mp4',
+        uploader=user
+    )
+    assert str(video) == 'Video Str Test'
+
+# --- Profile Model Tests (Basic Existence) ---
+
+def test_company_profile_creation():
+    """Test that a CompanyProfile can be created."""
+    user = User.objects.create_user(email='company@example.com', firebase_uid='companyuid', role='company')
+    profile = CompanyProfile.objects.create(
+        user=user,
+        company_name='Test Corp',
+        company_address='123 Test St',
+        industry='Tech'
+    )
+    assert profile.user == user
+    assert profile.company_name == 'Test Corp'
+    assert str(profile) == 'Test Corp'
+
+def test_viewer_profile_creation():
+    """Test that a ViewerProfile can be created."""
+    user = User.objects.create_user(email='viewer@example.com', firebase_uid='vieweruid', role='user')
+    profile = ViewerProfile.objects.create(
+        user=user,
+        country='Testland'
+    )
+    assert profile.user == user
+    assert profile.country == 'Testland'
+    assert str(profile) == f"Profile for {user.email}"
diff --git a/backend/api/tests/test_views.py b/backend/api/tests/test_views.py
new file mode 100644
index 0000000..602adef
--- /dev/null
+++ b/backend/api/tests/test_views.py
@@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
+
+# --- VideoDetailView Tests ---
+
+def test_video_detail_retrieve_success(api_client, sample_video):
+    """Test retrieving a specific video detail successfully."""
+    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['id'] == sample_video.pk
+    assert response.data['title'] == sample_video.title
+    assert response.data['description'] == sample_video.description
+    assert response.data['video_url'] == sample_video.video_url
+    # Add more assertions based on VideoDetailSerializer fields
+
+def test_video_detail_not_found(api_client):
+    """Test retrieving a non-existent video detail returns 404."""
+    url = reverse('video-detail', kwargs={'pk': 999}) # Assuming 999 doesn't exist
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_404_NOT_FOUND
+
+# --- TestAuthView Tests ---
+
+@pytest.fixture
+def authenticated_api_client(api_client, test_user):
+    """Fixture for an authenticated API client."""
+    # Note: In a real scenario with JWT/Firebase, you'd mock the authentication
+    # or use a mechanism to force authenticate. For basic DRF session auth (if enabled)
+    # or token auth, you might use force_authenticate.
+    # Since we use Firebase, proper testing requires mocking firebase_admin.auth
+    # or setting up test tokens. For now, we'll simulate by forcing authentication.
+    api_client.force_authenticate(user=test_user)
+    return api_client
+
+def test_auth_test_view_authenticated(authenticated_api_client):
+    """Test accessing the protected view when authenticated."""
+    url = reverse('auth-test')
+    response = authenticated_api_client.get(url)
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['success'] is True
+
+def test_auth_test_view_unauthenticated(api_client):
+    """Test accessing the protected view when unauthenticated."""
+    url = reverse('auth-test')
+    response = api_client.get(url)
+    # Expecting 401 Unauthorized or 403 Forbidden depending on DRF default settings
+    assert response.status_code in [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
+
+# --- More tests for Onboarding, UploadVideo, SetFirebaseToken would go here ---
+# --- These often require mocking external services (Firebase, Azure) ---
diff --git a/backend/backend/authentication.py b/backend/backend/authentication.py
new file mode 100644
index 0000000..4f5ad7d
--- /dev/null
+++ b/backend/backend/authentication.py
@@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
+class FirebaseAuthentication(authentication.BaseAuthentication):
+    """
+    Authenticates requests using Firebase Authentication ID tokens.
+    If the user doesn't exist locally, it attempts to create one
+    based on data from Firestore or Firebase Auth.
+    """
+
+    def authenticate(self, request):
+        """
+        Main authentication method called by DRF.
+        """
+        id_token = self._extract_token(request)
+        if not id_token:
+            return None # No token provided, authentication not attempted
+
+        try:
+            decoded_token = self._verify_firebase_token(id_token)
+            uid = decoded_token["uid"]
+            user = self._get_or_create_user(uid, decoded_token)
+            return (user, None) # Successful authentication
+        except exceptions.AuthenticationFailed:
+            # Let AuthenticationFailed exceptions propagate
+            raise
+        except Exception as e:
+            # Catch other potential errors during user get/create
+            logger.error(f"Unexpected error during authentication for token: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed(f'Authentication error: {str(e)}')
+
+    def _extract_token(self, request):
+
+        auth_header = request.META.get("HTTP_AUTHORIZATION")
+        if not auth_header:
+            return None
+
+        token_parts = auth_header.split()
+        if len(token_parts) != 2 or token_parts[0].lower() != "bearer":
+            logger.warning("Invalid Authorization header format.")
+            return None # Invalid format
+
+        return token_parts[1]
+
+    def _verify_firebase_token(self, id_token):
+
+        try:
+            # Verify token using Firebase Admin SDK
+            return auth.verify_id_token(id_token)
+        except Exception as e:
+            logger.warning(f"Firebase token verification failed: {e}")
+            raise exceptions.AuthenticationFailed(f'Invalid Firebase token: {str(e)}')
+
+    def _get_or_create_user(self, uid, decoded_token):
+
+        try:
+            # Try to get the user from the local database
+            user = User.objects.get(firebase_uid=uid)
+            return user
+        except User.DoesNotExist:
+            # User not found locally, proceed to create
+            logger.info(f"User with firebase_uid {uid} not found locally. Attempting creation.")
+            user_details = self._fetch_user_details(uid, decoded_token)
+            return self._create_user_with_profile(uid, user_details)
+
+    def _fetch_user_details(self, uid, decoded_token):
+
+        # Try Firestore first
+        firestore_data = self._fetch_from_firestore(uid)
+        if firestore_data:
+            return firestore_data
+
+        # Fallback to Firebase Auth
+        logger.info(f"No Firestore data for uid {uid}. Falling back to Firebase Auth.")
+        return self._fetch_from_firebase_auth(uid, decoded_token)
+
+    def _fetch_from_firestore(self, uid):
+
+        try:
+            user_ref = db.collection('users').document(uid)
+            user_doc = user_ref.get()
+            if user_doc.exists:
+                user_data = user_doc.to_dict()
+                return {
+                    'first_name': user_data.get('firstName', ''),
+                    'last_name': user_data.get('lastName', ''),
+                    'email': user_data.get('email', ''), # Ensure email is present
+                    'role': user_data.get('role', 'user') # Default role
+                }
+        except Exception as e:
+            logger.error(f"Error fetching user data from Firestore for uid {uid}: {e}", exc_info=True)
+        return None
+
+    def _fetch_from_firebase_auth(self, uid, decoded_token):
+
+        try:
+            # Use email from token if possible, otherwise query Firebase Auth user record
+            email = decoded_token.get('email')
+            if not email:
+                firebase_user = auth.get_user(uid)
+                email = firebase_user.email
+
+            # Ensure a valid email exists, generate placeholder if needed
+            if not email:
+                 logger.warning(f"No email found for uid {uid} in token or Firebase Auth. Using placeholder.")
+                 email = f"{uid}@example.com" # Placeholder email
+
+            return {
+                'first_name': '', # Firebase Auth doesn't reliably store names here
+                'last_name': '',
+                'email': email,
+                'role': 'user' # Default role for fallback
+            }
+        except auth.UserNotFoundError:
+             logger.error(f"User with uid {uid} not found in Firebase Auth.")
+             raise exceptions.AuthenticationFailed("User associated with token not found.")
+        except Exception as e:
+            logger.error(f"Error fetching user data from Firebase Auth for uid {uid}: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed("Could not retrieve user authentication details.")
+
+
+    def _create_user_with_profile(self, uid, user_details):
+
+        if not user_details.get('email'):
+             logger.error(f"Cannot create user with uid {uid}: email is missing.")
+             raise exceptions.AuthenticationFailed("Cannot create user without an email address.")
+
+        try:
+            user = User.objects.create_user(
+                firebase_uid=uid,
+                email=user_details['email'],
+                first_name=user_details['first_name'],
+                last_name=user_details['last_name'],
+                role=user_details['role']
+            )
+            logger.info(f"Successfully created local user for uid {uid} with role {user_details['role']}.")
+            self._create_profile(user, user_details['role'])
+            return user
+        except Exception as e:
+            # Catch potential database errors during user creation
+            logger.error(f"Database error creating user for uid {uid}: {e}", exc_info=True)
+            # Check if user was created despite error (e.g., race condition)
+            try:
+                return User.objects.get(firebase_uid=uid)
+            except User.DoesNotExist:
+                 raise exceptions.AuthenticationFailed("Failed to create local user record.")
+
+
+    def _create_profile(self, user, role):
+
+        try:
+            if role == 'company':
+                from api.models import CompanyProfile
+                CompanyProfile.objects.get_or_create(user=user)
+                logger.info(f"Ensured CompanyProfile exists for user {user.email}.")
+            elif role == 'user':
+                from api.models import ViewerProfile
+                ViewerProfile.objects.get_or_create(user=user)
+                logger.info(f"Ensured ViewerProfile exists for user {user.email}.")
+            else:
+                 logger.warning(f"Unknown role '{role}' for user {user.email}. No specific profile created.")
+        except Exception as e:
+             logger.error(f"Error creating profile for user {user.email} with role {role}: {e}", exc_info=True)
+             # Decide if this should fail authentication or just log
+             # raise exceptions.AuthenticationFailed("Failed to create user profile.")
diff --git a/backend/backend/firebase.py b/backend/backend/firebase.py
index 5ade7fb..cc6c80a 100644
--- a/backend/backend/firebase.py
+++ b/backend/backend/firebase.py
@@ -1,9 +1,44 @@
-import firebase_admin 
+import firebase_admin
 from firebase_admin import credentials
 import os
-from pathlib import Path
+from dotenv import load_dotenv
+load_dotenv()
 
-BASE_DIR = Path(__file__).resolve().parent.parent
-cred_path = os.path.join(BASE_DIR, "firebase-credentials.json")
-cred = credentials.Certificate(cred_path)
-default_app = firebase_admin.initialize_app(cred)
\ No newline at end of file
+# Construct credentials dictionary from environment variables
+try:
+    # The private key often contains newlines; replace escaped newlines
+    private_key = os.getenv("FIREBASE_PRIVATE_KEY", "").replace('\\n', '\n')
+
+    cred_dict = {
+        "type": os.getenv("FIREBASE_TYPE"),
+        "project_id": os.getenv("FIREBASE_PROJECT_ID"),
+        "private_key_id": os.getenv("FIREBASE_PRIVATE_KEY_ID"),
+        "private_key": private_key,
+        "client_email": os.getenv("FIREBASE_CLIENT_EMAIL"),
+        "client_id": os.getenv("FIREBASE_CLIENT_ID"),
+        "auth_uri": os.getenv("FIREBASE_AUTH_URI"),
+        "token_uri": os.getenv("FIREBASE_TOKEN_URI"),
+        "auth_provider_x509_cert_url": os.getenv("FIREBASE_AUTH_PROVIDER_X509_CERT_URL"),
+        "client_x509_cert_url": os.getenv("FIREBASE_CLIENT_X509_CERT_URL"),
+    }
+
+    # Add universe_domain only if the environment variable exists and is not empty
+    universe_domain = os.getenv("FIREBASE_UNIVERSE_DOMAIN")
+    if universe_domain:
+        cred_dict["universe_domain"] = universe_domain
+
+    # Check if essential keys are present
+    if not all([cred_dict["type"], cred_dict["project_id"], cred_dict["private_key"], cred_dict["client_email"]]):
+        raise ValueError("Missing essential Firebase credential environment variables.")
+
+    cred = credentials.Certificate(cred_dict)
+    default_app = firebase_admin.initialize_app(cred)
+
+except ValueError as e:
+    print(f"Error initializing Firebase Admin SDK: {e}")
+    # Handle the error appropriately, maybe raise it or log it
+    # Depending on your app's needs, you might want to prevent startup
+    default_app = None # Indicate initialization failed
+except Exception as e:
+    print(f"An unexpected error occurred during Firebase initialization: {e}")
+    default_app = None # Indicate initialization failed
diff --git a/backend/backend/settings.py b/backend/backend/settings.py
index cbecd22..d2407c5 100644
--- a/backend/backend/settings.py
+++ b/backend/backend/settings.py
@@ -73,6 +73,8 @@
 # Database
 # https://docs.djangoproject.com/en/5.1/ref/settings/#databases
 
+# Configuration now relies solely on environment variables.
+# In CI, these will be set by the workflow to point to the service container.
 if ENVIRONMENT == 'PROD':
     # Production Database
     tmpPostgres = urlparse(os.getenv("DATABASE_URL"))
@@ -153,11 +155,11 @@
 
 REST_FRAMEWORK = {
     'DEFAULT_AUTHENTICATION_CLASSES': [
-        'api.authentication.FirebaseAuthentication',
+        'backend.authentication.FirebaseAuthentication', # Updated path
         'rest_framework.authentication.SessionAuthentication',
     ],
     'DEFAULT_PERMISSION_CLASSES': [
         'rest_framework.permissions.IsAuthenticated',
     ]
 }
-AUTH_USER_MODEL = 'api.User'
\ No newline at end of file
+AUTH_USER_MODEL = 'api.User'
diff --git a/backend/pytest.ini b/backend/pytest.ini
new file mode 100644
index 0000000..6dd3b58
--- /dev/null
+++ b/backend/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+DJANGO_SETTINGS_MODULE = backend.settings
+python_files = tests.py test_*.py *_tests.py
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 0e20488..3db9594 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -10,4 +10,7 @@ psycopg2-binary
 python-dotenv
 firebase-admin
 django-extensions
-azure-storage-blob
\ No newline at end of file
+azure-storage-blob
+pytest
+pytest-django
+pytest-cov
diff --git a/ml-backend/deepface_heatmap.ipynb b/ml-backend/deepface_heatmap.ipynb
deleted file mode 100644
index 63fb605..0000000
--- a/ml-backend/deepface_heatmap.ipynb
+++ /dev/null
@@ -1,182 +0,0 @@
-{
-  "nbformat": 4,
-  "nbformat_minor": 0,
-  "metadata": {
-    "colab": {
-      "provenance": []
-    },
-    "kernelspec": {
-      "name": "python3",
-      "display_name": "Python 3"
-    },
-    "language_info": {
-      "name": "python"
-    }
-  },
-  "cells": [
-    {
-      "cell_type": "code",
-      "source": [
-        "!pip install deepface opencv-python pandas matplotlib seaborn tqdm"
-      ],
-      "metadata": {
-        "id": "GB7fARxAEbLK"
-      },
-      "execution_count": null,
-      "outputs": []
-    },
-    {
-      "cell_type": "code",
-      "source": [
-        "import cv2\n",
-        "import numpy as np\n",
-        "import pandas as pd\n",
-        "import matplotlib.pyplot as plt\n",
-        "import seaborn as sns\n",
-        "from deepface import DeepFace\n",
-        "from tqdm import tqdm"
-      ],
-      "metadata": {
-        "colab": {
-          "base_uri": "https://localhost:8080/"
-        },
-        "id": "MAG5_ZG3EkFf",
-        "outputId": "e5b87cc5-ab19-4e61-be5f-aa8705915538"
-      },
-      "execution_count": 2,
-      "outputs": [
-        {
-          "output_type": "stream",
-          "name": "stdout",
-          "text": [
-            "25-02-13 20:49:01 - Directory /root/.deepface has been created\n",
-            "25-02-13 20:49:01 - Directory /root/.deepface/weights has been created\n"
-          ]
-        }
-      ]
-    },
-    {
-      "cell_type": "code",
-      "execution_count": 4,
-      "metadata": {
-        "colab": {
-          "base_uri": "https://localhost:8080/"
-        },
-        "id": "XuLZ0rEdDfmI",
-        "outputId": "a9c6bcb4-4127-475e-9e7f-919392a18bd0"
-      },
-      "outputs": [
-        {
-          "output_type": "stream",
-          "name": "stderr",
-          "text": [
-            "\r  0%|          | 0/1045 [00:00<?, ?it/s]"
-          ]
-        },
-        {
-          "output_type": "stream",
-          "name": "stdout",
-          "text": [
-            "25-02-13 21:07:09 - facial_expression_model_weights.h5 will be downloaded...\n"
-          ]
-        },
-        {
-          "output_type": "stream",
-          "name": "stderr",
-          "text": [
-            "Downloading...\n",
-            "From: https://github.com/serengil/deepface_models/releases/download/v1.0/facial_expression_model_weights.h5\n",
-            "To: /root/.deepface/weights/facial_expression_model_weights.h5\n",
-            "\n",
-            "100%|██████████| 5.98M/5.98M [00:00<00:00, 61.2MB/s]\n",
-            "100%|█████████▉| 1044/1045 [03:58<00:00,  4.37it/s]\n"
-          ]
-        },
-        {
-          "output_type": "stream",
-          "name": "stdout",
-          "text": [
-            "Processing complete. Heatmap saved as emotion_heatmap.png\n"
-          ]
-        }
-      ],
-      "source": [
-        "def process_video_emotions(video_path, output_plot=\"heatmap.png\", frame_interval=1):\n",
-        "\n",
-        "    # Initialize video capture\n",
-        "    cap = cv2.VideoCapture(video_path)\n",
-        "    if not cap.isOpened():\n",
-        "        raise ValueError(\"Could not open video file\")\n",
-        "\n",
-        "    # Get video properties\n",
-        "    fps = cap.get(cv2.CAP_PROP_FPS)\n",
-        "    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n",
-        "\n",
-        "    emotion_data = []\n",
-        "    frame_count = 0\n",
-        "\n",
-        "    with tqdm(total=total_frames) as pbar:\n",
-        "        while cap.isOpened():\n",
-        "            ret, frame = cap.read()\n",
-        "            if not ret:\n",
-        "                break\n",
-        "\n",
-        "            if frame_count % frame_interval == 0:\n",
-        "                # Convert frame to RGB\n",
-        "                rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n",
-        "\n",
-        "                try:\n",
-        "                    # Analyze face and emotions\n",
-        "                    analysis = DeepFace.analyze(rgb_frame, actions=['emotion'], enforce_detection=True, silent=True)\n",
-        "                    if analysis:\n",
-        "                        emotions = analysis[0]['emotion']\n",
-        "                        emotions['frame'] = frame_count\n",
-        "                        emotions['time'] = frame_count / fps\n",
-        "                        emotion_data.append(emotions)\n",
-        "                except:\n",
-        "                    pass\n",
-        "\n",
-        "            frame_count += 1\n",
-        "            pbar.update(1)\n",
-        "\n",
-        "    cap.release()\n",
-        "\n",
-        "    # Create DataFrame\n",
-        "    df = pd.DataFrame(emotion_data)\n",
-        "    if df.empty:\n",
-        "        raise ValueError(\"No faces detected in the video\")\n",
-        "\n",
-        "    # Normalize time for display\n",
-        "    df['time'] = df['time'].round(1)\n",
-        "\n",
-        "    # Create heatmap\n",
-        "    plt.figure(figsize=(15, 8))\n",
-        "    heatmap_data = df.drop(['frame', 'time'], axis=1, errors='ignore')\n",
-        "    sns.heatmap(\n",
-        "        heatmap_data.T,\n",
-        "        xticklabels=df['time'],\n",
-        "        cmap='viridis',\n",
-        "        cbar_kws={'label': 'Emotion Intensity'}\n",
-        "    )\n",
-        "\n",
-        "    plt.title(\"Emotion Heatmap Over Time\")\n",
-        "    plt.xlabel(\"Time (seconds)\")\n",
-        "    plt.ylabel(\"Emotions\")\n",
-        "    plt.yticks(rotation=0)\n",
-        "    plt.savefig(output_plot, bbox_inches='tight', dpi=300)\n",
-        "    plt.close()\n",
-        "\n",
-        "    return df\n",
-        "\n",
-        "# Example usage\n",
-        "if __name__ == \"__main__\":\n",
-        "    df = process_video_emotions(\n",
-        "        video_path=\"/content/input.mp4\",\n",
-        "        output_plot=\"/content/emotion_heatmap.png\",\n",
-        "        frame_interval=5  # Process every 5th frame for faster results\n",
-        "    )\n",
-        "    print(\"Processing complete. Heatmap saved as emotion_heatmap.png\")"
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/sonar-project.properties b/sonar-project.properties
index ee0ecb0..bce6769 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -11,8 +11,8 @@ sonar.sources=backend
 sonar.inclusions=backend/**/*.py
 
 # Strict exclusions to prevent scanning frontend and other files
-sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**
+sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**,**/tests/**, **/test_*.py
 
 # Test coverage configuration (if you have tests)
-# sonar.python.coverage.reportPaths=backend/coverage.xml
-# sonar.python.xunit.reportPath=backend/test-reports/*.xml
\ No newline at end of file
+sonar.python.coverage.reportPaths=backend/coverage.xml
+# sonar.python.xunit.reportPath=backend/test-reports/*.xml


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 19**

<details>
<summary>🔭 Outside diff range comments (4)</summary><blockquote>

<details>
<summary>backend/.coveragerc (1)</summary><blockquote>

`16-35`: _🧹 Nitpick (assertive)_

**Consider branch coverage**  
The `[report]` excludes standard debug and abstract patterns. If you need branch coverage metrics, add `branch = True` under `[run]`. Beep boop.

</blockquote></details>
<details>
<summary>backend/api/tests/test_models.py (1)</summary><blockquote>

`1-157`: _🧹 Nitpick (assertive)_

**BEEP BOOP – Ignore Ruff’s ANN201 for tests**

Ruff complains about missing return‑type annotations on test functions. In pytest, return types are irrelevant; adding them is noisy. You can silence rule `ANN201` in the `[tool.ruff.per-file-ignores]` section for `test_*.py` files:

```toml
[tool.ruff.per-file-ignores]
"backend/api/tests/*.py" = ["ANN101", "ANN201"]
```

This keeps test suites clean without sprinkling `-> None` everywhere.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

11-11: Missing return type annotation for public function `test_create_user`

Add return type annotation: `None`

(ANN201)

---

17-17: Trailing comma missing

Add trailing comma

(COM812)

---

29-29: Missing return type annotation for public function `test_create_user_no_email`

Add return type annotation: `None`

(ANN201)

---

34-34: Missing return type annotation for public function `test_create_user_no_firebase_uid`

Add return type annotation: `None`

(ANN201)

---

39-39: Missing return type annotation for public function `test_create_superuser`

Add return type annotation: `None`

(ANN201)

---

45-45: Trailing comma missing

Add trailing comma

(COM812)

---

54-54: Missing return type annotation for public function `test_create_superuser_not_staff`

Add return type annotation: `None`

(ANN201)

---

58-58: Trailing comma missing

Add trailing comma

(COM812)

---

61-61: Missing return type annotation for public function `test_create_superuser_not_superuser`

Add return type annotation: `None`

(ANN201)

---

65-65: Trailing comma missing

Add trailing comma

(COM812)

---

68-68: Missing return type annotation for public function `test_user_str_method`

Add return type annotation: `None`

(ANN201)

---

73-73: Missing return type annotation for public function `test_user_get_full_name`

Add return type annotation: `None`

(ANN201)

---

76-76: Trailing comma missing

Add trailing comma

(COM812)

---

79-79: Trailing comma missing

Add trailing comma

(COM812)

---

86-86: Missing return type annotation for public function `test_create_video`

Add return type annotation: `None`

(ANN201)

---

97-97: Trailing comma missing

Add trailing comma

(COM812)

---

112-112: Missing return type annotation for public function `test_video_default_visibility`

Add return type annotation: `None`

(ANN201)

---

118-118: Trailing comma missing

Add trailing comma

(COM812)

---

122-122: Missing return type annotation for public function `test_video_str_method`

Add return type annotation: `None`

(ANN201)

---

128-128: Trailing comma missing

Add trailing comma

(COM812)

---

134-134: Missing return type annotation for public function `test_company_profile_creation`

Add return type annotation: `None`

(ANN201)

---

141-141: Trailing comma missing

Add trailing comma

(COM812)

---

147-147: Missing return type annotation for public function `test_viewer_profile_creation`

Add return type annotation: `None`

(ANN201)

---

152-152: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>

</blockquote></details>
<details>
<summary>backend/backend/authentication.py (1)</summary><blockquote>

`156-173`: _🛠️ Refactor suggestion_

**BEEP BOOP – Use `logger.exception` for profile errors**

Multiple places call `logger.error(..., exc_info=True)`.  The dedicated `logger.exception()` is clearer and automatically attaches the traceback:

```diff
-        except Exception as e:
-             logger.error(f"Error creating profile for user {user.email} with role {role}: {e}", exc_info=True)
+        except Exception:
+             logger.exception("Error creating profile for user %s with role %s", user.email, role)
```

Also leverages lazy %-formatting to avoid f‑string overhead flagged by Ruff `G004`.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

156-156: Missing return type annotation for private function `_create_profile`

Add return type annotation: `None`

(ANN202)

---

156-156: Missing type annotation for function argument `user`

(ANN001)

---

156-156: Missing type annotation for function argument `role`

(ANN001)

---

162-162: Logging statement uses f-string

(G004)

---

166-166: Logging statement uses f-string

(G004)

---

168-168: Logging statement uses f-string

(G004)

---

170-170: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

170-170: Logging statement uses f-string

(G004)

</details>

</details>

</blockquote></details>
<details>
<summary>backend/api/tests/test_views.py (1)</summary><blockquote>

`112-114`: _🧹 Nitpick (assertive)_

**PLACEHOLDER COMMENTS DETECTED WITHOUT IMPLEMENTATION.**

OBSERVATION: COMMENTS INDICATE MISSING TEST COVERAGE FOR CRITICAL FUNCTIONALITY.

WOULD YOU LIKE ASSISTANCE GENERATING TEST IMPLEMENTATIONS FOR ONBOARDING, UPLOADVIDEO, AND SETFIREBASETOKEN ENDPOINTS? THIS UNIT COULD PROVIDE MOCK IMPLEMENTATIONS.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 963603dded98ba5c49219cac88ed5693e71f587e and 0dbca47550fa4a653f023cd0383796b5753733b4.

</details>

<details>
<summary>📒 Files selected for processing (12)</summary>

* `.github/workflows/sonarqube.yml` (1 hunks)
* `backend/.coveragerc` (1 hunks)
* `backend/.gitignore` (1 hunks)
* `backend/api/authentication.py` (0 hunks)
* `backend/api/tests/test_models.py` (1 hunks)
* `backend/api/tests/test_views.py` (1 hunks)
* `backend/backend/authentication.py` (1 hunks)
* `backend/backend/settings.py` (2 hunks)
* `backend/pytest.ini` (1 hunks)
* `backend/requirements.txt` (1 hunks)
* `ml-backend/deepface_heatmap.ipynb` (0 hunks)
* `sonar-project.properties` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (2)</summary>

* ml-backend/deepface_heatmap.ipynb
* backend/api/authentication.py

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>backend/api/tests/test_models.py (1)</summary><blockquote>

<details>
<summary>backend/api/models.py (6)</summary>

* `User` (32-64)
* `CompanyProfile` (67-79)
* `ViewerProfile` (81-98)
* `create_user` (7-17)
* `create_superuser` (19-30)
* `get_full_name` (62-64)

</details>

</blockquote></details>
<details>
<summary>backend/api/tests/test_views.py (2)</summary><blockquote>

<details>
<summary>backend/api/models.py (2)</summary>

* `User` (32-64)
* `create_user` (7-17)

</details>
<details>
<summary>backend/api/views.py (1)</summary>

* `get` (15-20)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>backend/backend/settings.py</summary>

83-83: Trailing comma missing

Add trailing comma

(COM812)

---

170-170: Trailing comma missing

Add trailing comma

(COM812)

</details>
<details>
<summary>backend/api/tests/test_models.py</summary>

11-11: Missing return type annotation for public function `test_create_user`

Add return type annotation: `None`

(ANN201)

---

17-17: Trailing comma missing

Add trailing comma

(COM812)

---

29-29: Missing return type annotation for public function `test_create_user_no_email`

Add return type annotation: `None`

(ANN201)

---

34-34: Missing return type annotation for public function `test_create_user_no_firebase_uid`

Add return type annotation: `None`

(ANN201)

---

39-39: Missing return type annotation for public function `test_create_superuser`

Add return type annotation: `None`

(ANN201)

---

45-45: Trailing comma missing

Add trailing comma

(COM812)

---

54-54: Missing return type annotation for public function `test_create_superuser_not_staff`

Add return type annotation: `None`

(ANN201)

---

58-58: Trailing comma missing

Add trailing comma

(COM812)

---

61-61: Missing return type annotation for public function `test_create_superuser_not_superuser`

Add return type annotation: `None`

(ANN201)

---

65-65: Trailing comma missing

Add trailing comma

(COM812)

---

68-68: Missing return type annotation for public function `test_user_str_method`

Add return type annotation: `None`

(ANN201)

---

73-73: Missing return type annotation for public function `test_user_get_full_name`

Add return type annotation: `None`

(ANN201)

---

76-76: Trailing comma missing

Add trailing comma

(COM812)

---

79-79: Trailing comma missing

Add trailing comma

(COM812)

---

86-86: Missing return type annotation for public function `test_create_video`

Add return type annotation: `None`

(ANN201)

---

97-97: Trailing comma missing

Add trailing comma

(COM812)

---

112-112: Missing return type annotation for public function `test_video_default_visibility`

Add return type annotation: `None`

(ANN201)

---

118-118: Trailing comma missing

Add trailing comma

(COM812)

---

122-122: Missing return type annotation for public function `test_video_str_method`

Add return type annotation: `None`

(ANN201)

---

128-128: Trailing comma missing

Add trailing comma

(COM812)

---

134-134: Missing return type annotation for public function `test_company_profile_creation`

Add return type annotation: `None`

(ANN201)

---

141-141: Trailing comma missing

Add trailing comma

(COM812)

---

147-147: Missing return type annotation for public function `test_viewer_profile_creation`

Add return type annotation: `None`

(ANN201)

---

152-152: Trailing comma missing

Add trailing comma

(COM812)

</details>
<details>
<summary>backend/backend/authentication.py</summary>

18-18: Missing return type annotation for public function `authenticate`

(ANN201)

---

18-18: Missing type annotation for function argument `request`

(ANN001)

---

30-30: Consider moving this statement to an `else` block

(TRY300)

---

36-36: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

36-36: Logging statement uses f-string

(G004)

---

37-37: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

37-37: Avoid specifying long messages outside the exception class

(TRY003)

---

37-37: Use explicit conversion flag

Replace with conversion flag

(RUF010)

---

39-39: Missing return type annotation for private function `_extract_token`

(ANN202)

---

39-39: Missing type annotation for function argument `request`

(ANN001)

---

52-52: Missing return type annotation for private function `_verify_firebase_token`

(ANN202)

---

52-52: Missing type annotation for function argument `id_token`

(ANN001)

---

57-57: Do not catch blind exception: `Exception`

(BLE001)

---

58-58: Logging statement uses f-string

(G004)

---

59-59: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

59-59: Avoid specifying long messages outside the exception class

(TRY003)

---

59-59: Use explicit conversion flag

Replace with conversion flag

(RUF010)

---

61-61: Missing return type annotation for private function `_get_or_create_user`

(ANN202)

---

61-61: Missing type annotation for function argument `uid`

(ANN001)

---

61-61: Missing type annotation for function argument `decoded_token`

(ANN001)

---

66-66: Consider moving this statement to an `else` block

(TRY300)

---

66-66: Unnecessary assignment to `user` before `return` statement

Remove unnecessary assignment

(RET504)

---

69-69: Logging statement uses f-string

(G004)

---

73-73: Missing return type annotation for private function `_fetch_user_details`

(ANN202)

---

73-73: Missing type annotation for function argument `uid`

(ANN001)

---

73-73: Missing type annotation for function argument `decoded_token`

(ANN001)

---

81-81: Logging statement uses f-string

(G004)

---

84-84: Missing return type annotation for private function `_fetch_from_firestore`

(ANN202)

---

84-84: Missing type annotation for function argument `uid`

(ANN001)

---

95-95: Trailing comma missing

Add trailing comma

(COM812)

---

98-98: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

98-98: Logging statement uses f-string

(G004)

---

101-101: Missing return type annotation for private function `_fetch_from_firebase_auth`

(ANN202)

---

101-101: Missing type annotation for function argument `uid`

(ANN001)

---

101-101: Missing type annotation for function argument `decoded_token`

(ANN001)

---

112-112: Logging statement uses f-string

(G004)

---

115-120: Consider moving this statement to an `else` block

(TRY300)

---

119-119: Trailing comma missing

Add trailing comma

(COM812)

---

122-122: Use `logging.exception` instead of `logging.error`

Replace with `exception`

(TRY400)

---

122-122: Logging statement uses f-string

(G004)

---

123-123: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

123-123: Avoid specifying long messages outside the exception class

(TRY003)

---

125-125: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

125-125: Logging statement uses f-string

(G004)

---

126-126: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

126-126: Avoid specifying long messages outside the exception class

(TRY003)

---

129-129: Missing return type annotation for private function `_create_user_with_profile`

(ANN202)

---

129-129: Missing type annotation for function argument `uid`

(ANN001)

---

129-129: Missing type annotation for function argument `user_details`

(ANN001)

---

132-132: Logging statement uses f-string

(G004)

---

133-133: Avoid specifying long messages outside the exception class

(TRY003)

---

141-141: Trailing comma missing

Add trailing comma

(COM812)

---

143-143: Logging statement uses f-string

(G004)

---

145-145: Consider moving this statement to an `else` block

(TRY300)

---

148-148: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

148-148: Logging statement uses f-string

(G004)

---

153-153: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

153-153: Avoid specifying long messages outside the exception class

(TRY003)

---

156-156: Missing return type annotation for private function `_create_profile`

Add return type annotation: `None`

(ANN202)

---

156-156: Missing type annotation for function argument `user`

(ANN001)

---

156-156: Missing type annotation for function argument `role`

(ANN001)

---

162-162: Logging statement uses f-string

(G004)

---

166-166: Logging statement uses f-string

(G004)

---

168-168: Logging statement uses f-string

(G004)

---

170-170: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

170-170: Logging statement uses f-string

(G004)

</details>
<details>
<summary>backend/api/tests/test_views.py</summary>

11-11: Missing return type annotation for public function `api_client`

(ANN201)

---

16-16: Missing return type annotation for public function `test_user`

(ANN201)

---

21-21: Missing return type annotation for public function `sample_video`

(ANN201)

---

21-21: Missing type annotation for function argument `test_user`

(ANN001)

---

31-31: Trailing comma missing

Add trailing comma

(COM812)

---

36-36: Missing return type annotation for public function `test_video_feed_list_success`

Add return type annotation: `None`

(ANN201)

---

36-36: Missing type annotation for function argument `api_client`

(ANN001)

---

36-36: Missing type annotation for function argument `sample_video`

(ANN001)

---

43-43: Trailing comma missing

Add trailing comma

(COM812)

---

56-56: Missing return type annotation for public function `test_video_feed_empty`

Add return type annotation: `None`

(ANN201)

---

56-56: Missing type annotation for function argument `api_client`

(ANN001)

---

66-66: Missing return type annotation for public function `test_video_detail_retrieve_success`

Add return type annotation: `None`

(ANN201)

---

66-66: Missing type annotation for function argument `api_client`

(ANN001)

---

66-66: Missing type annotation for function argument `sample_video`

(ANN001)

---

78-78: Missing return type annotation for public function `test_video_detail_not_found`

Add return type annotation: `None`

(ANN201)

---

78-78: Missing type annotation for function argument `api_client`

(ANN001)

---

88-88: Missing return type annotation for public function `authenticated_api_client`

(ANN201)

---

88-88: Missing type annotation for function argument `api_client`

(ANN001)

---

88-88: Missing type annotation for function argument `test_user`

(ANN001)

---

98-98: Missing return type annotation for public function `test_auth_test_view_authenticated`

Add return type annotation: `None`

(ANN201)

---

98-98: Missing type annotation for function argument `authenticated_api_client`

(ANN001)

---

105-105: Missing return type annotation for public function `test_auth_test_view_unauthenticated`

Add return type annotation: `None`

(ANN201)

---

105-105: Missing type annotation for function argument `api_client`

(ANN001)

</details>

</details>
<details>
<summary>🪛 actionlint (1.7.4)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

23-23: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

---

[warning] 34-34: too few spaces before comment

(comments)

---

[warning] 38-38: too few spaces before comment

(comments)

---

[error] 44-44: trailing spaces

(trailing-spaces)

</details>

</details>
<details>
<summary>🪛 GitHub Check: CodeQL</summary>

<details>
<summary>backend/backend/authentication.py</summary>

[warning] 37-37: Information exposure through an exception
[Stack trace information](1) flows to this location and may be exposed to an external user.

---

[warning] 59-59: Information exposure through an exception
[Stack trace information](1) flows to this location and may be exposed to an external user.

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>backend/requirements.txt (1)</summary>

`13-13`: **Skip: unchanged package entry**  
The `azure-storage-blob` line remains unchanged aside from formatting. No action required. Beep boop.

</details>
<details>
<summary>backend/pytest.ini (1)</summary>

`1-3`: **Configure pytest for Django correctly**  
The `pytest.ini` sets `DJANGO_SETTINGS_MODULE` and test discovery patterns as intended for a Django project. Looks good. Beep boop.

</details>
<details>
<summary>backend/.gitignore (1)</summary>

`1-3`: **Ignore coverage and DB artifacts**  
Excluding `*.sqlite3`, `.coverage`, and `coverage.xml` prevents database and coverage files from being committed. Beep boop.

</details>
<details>
<summary>sonar-project.properties (1)</summary>

`17-18`: **Verify coverage report path**  
Ensure that `backend/coverage.xml` matches where pytest writes the XML report in CI. If coverage is generated at the repo root, update this path accordingly. Beep boop.

</details>
<details>
<summary>backend/.coveragerc (1)</summary>

`1-15`: **Validate omission patterns**  
The `[run]` section properly excludes migrations, tests, config, and virtual‐env directories. Configuration is solid. Beep boop.

</details>
<details>
<summary>backend/backend/settings.py (1)</summary>

`165-167`: <details>
<summary>✅ Verification successful</summary>

**BEEP BOOP – Double‑check dotted path update**

The authentication path has moved to `backend.authentication.FirebaseAuthentication`. Please verify no legacy references (`api.authentication.FirebaseAuthentication`) remain anywhere else in the repo to prevent `ImportError`s at runtime.  

Run:

---

🏁 Script executed:

```shell
rg -n "api\.authentication\.FirebaseAuthentication"
```

Length of output: 53

---

Beep boop – initiating extended path scan for legacy imports.

---

🏁 Script executed:

```shell
#!/bin/bash
# Broad search for any api.authentication references
rg -n "api\.authentication"
# Specifically look for import statements referencing the old path
rg -n "^from api\.authentication\.FirebaseAuthentication" 
rg -n "^import api\.authentication\.FirebaseAuthentication"
```

Length of output: 151

---



</details>

**All clear: No legacy `api.authentication` imports detected**

Beep boop – search completed, and no references to `api.authentication.FirebaseAuthentication` remain. The update to `backend.authentication.FirebaseAuthentication` is safe.

</details>
<details>
<summary>backend/api/tests/test_views.py (1)</summary>

`1-8`: **APPROPRIATE USE OF IMPORTS AND DATABASE MARKER DETECTED.**

EXECUTION ANALYSIS: ALL NECESSARY COMPONENTS FOR REST API TESTING IMPORTED. PYTEST.MARK.DJANGO_DB CORRECTLY APPLIED TO MODULE LEVEL.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T09:13:40Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T09:16:36Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>.github/workflows/sonarqube.yml (1)</summary><blockquote>

`32-35`: _🛠️ Refactor suggestion_

**Use `printf` for JSON secrets and fix comment indentation**. Beep boop – writing a multi‑line JSON secret with `echo` can break line breaks. Replace:
```diff
-run: echo "${{ secrets.FIREBASE_CREDENTIALS_JSON }}" > backend/firebase-credentials.json
+run: |
+  printf '%s' "${{ secrets.FIREBASE_CREDENTIALS_JSON }}" > backend/firebase-credentials.json
```
Additionally, line 34’s `shell: bash # Ensure bash shell for proper echo behavior` has too few spaces before `#`. Add two spaces before the comment to satisfy `yamllint`.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 34-34: too few spaces before comment

(comments)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0dbca47550fa4a653f023cd0383796b5753733b4 and 0406c8fd9b9d5a9d13a1afc10ff0befde1b2aaaa.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `.github/workflows/sonarqube.yml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

23-23: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

---

[warning] 34-34: too few spaces before comment

(comments)

---

[warning] 40-40: too few spaces before comment

(comments)

---

[error] 46-46: trailing spaces

(trailing-spaces)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>.github/workflows/sonarqube.yml (2)</summary>

`42-46`: **Verify coverage report path and remove trailing whitespace**. Beep boop – the pytest command outputs `coverage.xml` in `backend/`, but ensure your `sonar-project.properties` references `backend/coverage.xml`. Also, line 46 has trailing spaces; please trim them to satisfy `yamllint`.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 46-46: trailing spaces

(trailing-spaces)

</details>

</details>

---

`47-53`: **Ensure SonarCloud scan configuration matches coverage setup**. Beep boop – confirm that `projectBaseDir: .` aligns with where the `sonar-project.properties` file lives and that `SONAR_TOKEN` is correctly supplied. Verify the properties file includes the Python coverage settings for the backend directory.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T09:18:41Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (5)</summary><blockquote>

<details>
<summary>.github/workflows/sonarqube.yml (5)</summary><blockquote>

`31-31`: **Remove trailing whitespace at line 31**  
Beep boop – trailing spaces will fail `yamllint` (`trailing-spaces`). Please trim the whitespace.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

</details>

</details>

---

`39-39`: **Fix inline comment indentation**  
Beep boop – YAML lint requires two spaces before inline comments. Update to:  
```diff
-        shell: bash # Use cat with heredoc for exact content preservation
+        shell: bash  # Use cat with heredoc for exact content preservation
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 39-39: too few spaces before comment

(comments)

</details>

</details>

---

`45-45`: **Fix inline comment indentation**  
Beep boop – YAML lint requires two spaces before inline comments. Update to:  
```diff
-        shell: bash # Use printf for safer handling of special characters
+        shell: bash  # Use printf for safer handling of special characters
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 45-45: too few spaces before comment

(comments)

</details>

</details>

---

`51-51`: **Remove trailing whitespace at line 51**  
Beep boop – trailing spaces will fail `yamllint` (`trailing-spaces`). Please trim the whitespace.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 51-51: trailing spaces

(trailing-spaces)

</details>

</details>

---

`27-31`: _🧹 Nitpick (assertive)_

**Add pip dependency caching**  
Beep boop – caching pip packages can greatly reduce CI runtime on subsequent runs. Consider inserting an `actions/cache` step before installing dependencies:  
```diff
+      - name: Cache pip
+        uses: actions/cache@v3
+        with:
+          path: ~/.cache/pip
+          key: ${{ runner.os }}-pip-${{ hashFiles('backend/requirements.txt') }}
+          restore-keys: |
+            ${{ runner.os }}-pip-
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0406c8fd9b9d5a9d13a1afc10ff0befde1b2aaaa and 8d701d4bc1c90d28d7343b9db58ab50aae4eeff9.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `.github/workflows/sonarqube.yml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

23-23: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

---

[warning] 39-39: too few spaces before comment

(comments)

---

[warning] 45-45: too few spaces before comment

(comments)

---

[error] 51-51: trailing spaces

(trailing-spaces)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T09:22:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>.github/workflows/sonarqube.yml (1)</summary><blockquote>

`32-66`: _🧹 Nitpick (assertive)_

**Preserve multi-line secrets using printf**
Beep boop – using `echo` to write secrets can mangle multi-line values. Prefer `printf '%s\n'` to maintain line breaks and avoid JSON/key formatting errors.


Example diff:
```diff
-          {
-            echo "SECRET_KEY=${{ secrets.SECRET_KEY }}"
-            echo "DEBUG=${{ secrets.DEBUG }}"
-            # ... other secrets ...
-            echo "FIREBASE_UNIVERSE_DOMAIN=${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}"
-          } > backend/.env
+          printf '%s\n' \
+            "SECRET_KEY=${{ secrets.SECRET_KEY }}" \
+            "DEBUG=${{ secrets.DEBUG }}" \
+            # ... include all other secrets lines here \
+            "FIREBASE_UNIVERSE_DOMAIN=${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}" \
+          > backend/.env
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 41-41: trailing spaces

(trailing-spaces)

---

[error] 48-48: trailing spaces

(trailing-spaces)

---

[error] 64-64: trailing spaces

(trailing-spaces)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c2e9ff8618168d02cffab946482b0b456819d656 and 60abc4217c8f357aff5df1fa7afe0c2ee2023d4c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `.github/workflows/sonarqube.yml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

23-23: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

---

[error] 41-41: trailing spaces

(trailing-spaces)

---

[error] 48-48: trailing spaces

(trailing-spaces)

---

[error] 64-64: trailing spaces

(trailing-spaces)

---

[error] 72-72: trailing spaces

(trailing-spaces)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T10:06:59Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>.github/workflows/sonarqube.yml (2)</summary><blockquote>

`73-77`: _🛠️ Refactor suggestion_

**PYTHON SETUP ACTION OUTDATED.**

BEEP BOOP. SETUP-PYTHON ACTION VERSION V4 IS OUTDATED. UPDATE RECOMMENDED.

```diff
-      - name: Set up Python 3.12
-        uses: actions/setup-python@v4
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v5
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

74-74: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>

---

`89-92`: _🛠️ Refactor suggestion_

**TEST EXECUTION AND COVERAGE REPORTING NEEDS OPTIMIZATION.**

BEEP BOOP. TEST EXECUTION CORRECTLY GENERATES COVERAGE REPORT. HOWEVER, RECOMMENDED TO USE WORKING-DIRECTORY INSTEAD OF CD COMMAND.


```diff
-      - name: Run tests and generate coverage report
-        run: |
-          cd backend
-          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
+      - name: Run tests and generate coverage report
+        working-directory: backend
+        run: pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 60abc4217c8f357aff5df1fa7afe0c2ee2023d4c and d167858db1b4a624143da18bb587c52fb682dbc4.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `.github/workflows/sonarqube.yml` (1 hunks)
* `backend/backend/settings.py` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

<details>
<summary>backend/backend/settings.py</summary>

163-163: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>
<details>
<summary>🪛 actionlint (1.7.4)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

69-69: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

---

74-74: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>
<details>
<summary>🪛 Checkov (3.2.334)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

[LOW] 22-23: Base64 High Entropy String

(CKV_SECRET_6)

</details>

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>.github/workflows/sonarqube.yml</summary>

[warning] 19-19: too few spaces before comment

(comments)

---

[warning] 25-25: too few spaces before comment

(comments)

---

[warning] 38-38: too few spaces before comment

(comments)

---

[warning] 39-39: too few spaces before comment

(comments)

---

[warning] 40-40: too few spaces before comment

(comments)

---

[warning] 41-41: too few spaces before comment

(comments)

---

[warning] 42-42: too few spaces before comment

(comments)

---

[warning] 43-43: too few spaces before comment

(comments)

---

[warning] 45-45: too few spaces before comment

(comments)

---

[warning] 46-46: too few spaces before comment

(comments)

---

[error] 82-82: trailing spaces

(trailing-spaces)

---

[error] 93-93: trailing spaces

(trailing-spaces)

---

[warning] 98-98: comment not indented like content

(comments-indentation)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>backend/backend/settings.py (3)</summary>

`76-77`: **DATABASE CONFIGURATION COMMENT CORRECTLY EXPLAINS ENVIRONMENT VARIABLE USAGE.**

BEEP BOOP. COMMENT PROVIDES CLEAR EXPLANATION ABOUT RELIANCE ON ENVIRONMENT VARIABLES IN CI ENVIRONMENT. THIS ALIGNS WITH CODE COVERAGE OBJECTIVE OF PR.

---

`158-158`: **AUTHENTICATION PATH UPDATED CORRECTLY.**

BEEP BOOP. FIREBASE AUTHENTICATION PATH UPDATED FROM 'api.authentication.FirebaseAuthentication' TO 'backend.authentication.FirebaseAuthentication' REFLECTING CODE REFACTORING. THIS IS CONSISTENT WITH AUTHENTICATION LOGIC RELOCATION.

---

`165-166`: **TRAILING NEWLINE ADDED TO AUTH_USER_MODEL.**

BEEP BOOP. ADDING TRAILING NEWLINE FOLLOWS PEP 8 BEST PRACTICES FOR PYTHON FILES.

</details>
<details>
<summary>.github/workflows/sonarqube.yml (3)</summary>

`16-31`: **POSTGRES SERVICE CONTAINER PROPERLY CONFIGURED FOR TESTS.**

BEEP BOOP. DATABASE SERVICE IMPLEMENTATION SUPPORTS CODE COVERAGE OBJECTIVE. HEALTH CHECKS ENSURE DATABASE READINESS BEFORE TEST EXECUTION.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Checkov (3.2.334)</summary>

[LOW] 22-23: Base64 High Entropy String

(CKV_SECRET_6)

</details>
<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 19-19: too few spaces before comment

(comments)

---

[warning] 25-25: too few spaces before comment

(comments)

</details>

</details>

---

`33-65`: **ENVIRONMENT VARIABLES PROPERLY CONFIGURED AT JOB LEVEL.**

BEEP BOOP. ENVIRONMENT VARIABLES MOVED FROM .ENV FILE TO JOB LEVEL CONFIGURATION. FIREBASE AND AZURE CREDENTIALS CORRECTLY INCLUDED FOR AUTHENTICATION TESTING.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 38-38: too few spaces before comment

(comments)

---

[warning] 39-39: too few spaces before comment

(comments)

---

[warning] 40-40: too few spaces before comment

(comments)

---

[warning] 41-41: too few spaces before comment

(comments)

---

[warning] 42-42: too few spaces before comment

(comments)

---

[warning] 43-43: too few spaces before comment

(comments)

---

[warning] 45-45: too few spaces before comment

(comments)

---

[warning] 46-46: too few spaces before comment

(comments)

</details>

</details>

---

`85-88`: **MIGRATIONS STEP PROPERLY ADDED.**

BEEP BOOP. MIGRATION STEP ENSURES DATABASE SCHEMA IS UPDATED BEFORE TEST EXECUTION. CONSISTENT WITH CODE COVERAGE OBJECTIVE.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T10:23:00Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d167858db1b4a624143da18bb587c52fb682dbc4 and 6abdae5f6411782031a354366812fc490bcfd690.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `sonar-project.properties` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>sonar-project.properties (1)</summary>

`17-17`: **Beep boop. Verify the coverage report path**  
Beep boop. Please confirm that `backend/coverage.xml` matches the actual location and filename produced by your CI job. If the report is saved elsewhere or named differently, update this setting accordingly to ensure SonarCloud imports coverage data.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-19T10:28:08Z

Review comments:
diff_hunk: @@ -10,4 +10,7 @@ psycopg2-binary
 python-dotenv
 firebase-admin
 django-extensions
-azure-storage-blob
\ No newline at end of file
+azure-storage-blob
+pytest
+pytest-django
+pytest-cov
path: backend/requirements.txt
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Pin test dependencies for reproducibility**  
Including `pytest`, `pytest-django`, and `pytest-cov` aligns with the new testing framework. For stable CI builds, consider adding explicit version constraints. Beep boop.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:37Z
author_association: NONE
diff_hunk: @@ -1 +1,3 @@
-*.sqlite3
\ No newline at end of file
+*.sqlite3
+.coverage
+coverage.xml
path: backend/.gitignore
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Optional: Exclude pytest cache**  
Consider adding `.pytest_cache/` to keep the repo clean from pytest cache artifacts. Beep boop.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -73,7 +73,16 @@
 # Database
 # https://docs.djangoproject.com/en/5.1/ref/settings/#databases
 
-if ENVIRONMENT == 'PROD':
+# Check if running in GitHub Actions CI environment for tests
+if os.getenv('GITHUB_ACTIONS') == 'true':
+    # Use in-memory SQLite database for tests in CI
+    DATABASES = {
+        'default': {
+            'ENGINE': 'django.db.backends.sqlite3',
+            'NAME': ':memory:',
+        }
+    }
+elif ENVIRONMENT == 'PROD':
path: backend/backend/settings.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**BEEP BOOP – Make the CI‑switch more robust**

Comparing strings to the hard‑coded literal `"true"` is brittle (values may be `"True"` or simply be non‑empty). A safer pattern is to test for existence or normalise case:

```diff
-if os.getenv('GITHUB_ACTIONS') == 'true':
+if os.getenv('GITHUB_ACTIONS', '').lower() == 'true':
```

This avoids silent fall‑back to Postgres if GitHub ever changes the casing.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
# Check if running in GitHub Actions CI environment for tests
if os.getenv('GITHUB_ACTIONS', '').lower() == 'true':
    # Use in-memory SQLite database for tests in CI
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': ':memory:',
        }
    }
elif ENVIRONMENT == 'PROD':
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

83-83: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -18,10 +18,33 @@
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
+      - name: Create Firebase Credentials File
+        run: echo "${{ secrets.FIREBASE_CREDENTIALS_JSON }}" > backend/firebase-credentials.json
+        shell: bash # Ensure bash shell for proper echo behavior
+
+      - name: Create Backend .env File
+        run: echo "${{ secrets.BACKEND_ENV_FILE }}" > backend/.env
+        shell: bash # Ensure bash shell for proper echo behavior
+
+      - name: Run tests and generate coverage report
+        run: |
+          cd backend
+          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
       
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**BEEP BOOP – Preserve new‑line‑rich secrets safely**

Using `echo` to write multiline secrets (especially `.env` content) can mangle line‑breaks.  Prefer `printf '%s'` which leaves the byte‑stream untouched:

```diff
-        run: echo "${{ secrets.BACKEND_ENV_FILE }}" > backend/.env
+        run: |
+          printf '%s' "${{ secrets.BACKEND_ENV_FILE }}" > backend/.env
```

Same for the Firebase credentials step. This eliminates elusive “invalid JSON / missing key” errors when the secret spans several lines.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      - name: Create Backend .env File
        run: |
          printf '%s' "${{ secrets.BACKEND_ENV_FILE }}" > backend/.env
        shell: bash # Ensure bash shell for proper echo behavior
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

---

[warning] 34-34: too few spaces before comment

(comments)

---

[warning] 38-38: too few spaces before comment

(comments)

---

[error] 44-44: trailing spaces

(trailing-spaces)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 0406c8f
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,156 @@
+import pytest
+from django.utils import timezone
+# ValueError is a built-in exception, no need to import from django.core.exceptions
+from api.models import User, Video, CompanyProfile, ViewerProfile
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+# --- User Model Tests ---
+
+def test_create_user():
+    """Test creating a regular user with the custom manager."""
+    user = User.objects.create_user(
+        email='test@example.com',
+        firebase_uid='testuid123',
+        first_name='Test',
+        last_name='User'
+    )
+    assert user.email == 'test@example.com'
+    assert user.firebase_uid == 'testuid123'
+    assert user.first_name == 'Test'
+    assert user.last_name == 'User'
+    assert user.role == 'user'
+    assert user.is_active is True
+    assert user.is_staff is False
+    assert user.is_superuser is False
+    assert user.check_password('') is False # No password set directly
+
+def test_create_user_no_email():
+    """Test creating user without email raises ValueError."""
+    with pytest.raises(ValueError, match='Users must have an email address'):
+        User.objects.create_user(email='', firebase_uid='testuid123')
+
+def test_create_user_no_firebase_uid():
+    """Test creating user without firebase_uid raises ValueError."""
+    with pytest.raises(ValueError, match='Users must have a Firebase UID'):
+        User.objects.create_user(email='test@example.com', firebase_uid='')
+
+def test_create_superuser():
+    """Test creating a superuser."""
+    admin_user = User.objects.create_superuser(
+        email='super@example.com',
+        firebase_uid='superuid456',
+        first_name='Super',
+        last_name='Admin'
+    )
+    assert admin_user.email == 'super@example.com'
+    assert admin_user.firebase_uid == 'superuid456'
+    assert admin_user.role == 'admin'
+    assert admin_user.is_active is True
+    assert admin_user.is_staff is True
+    assert admin_user.is_superuser is True
+
+def test_create_superuser_not_staff():
+    """Test creating superuser with is_staff=False raises ValueError."""
+    with pytest.raises(ValueError, match='Superuser must have is_staff=True.'):
+        User.objects.create_superuser(
+            email='super@example.com', firebase_uid='superuid456', is_staff=False
+        )
+
+def test_create_superuser_not_superuser():
+    """Test creating superuser with is_superuser=False raises ValueError."""
+    with pytest.raises(ValueError, match='Superuser must have is_superuser=True.'):
+        User.objects.create_superuser(
+            email='super@example.com', firebase_uid='superuid456', is_superuser=False
+        )
+
+def test_user_str_method():
+    """Test the string representation of the User model."""
+    user = User.objects.create_user(email='str@example.com', firebase_uid='struid789')
+    assert str(user) == 'str@example.com'
+
+def test_user_get_full_name():
+    """Test the get_full_name method."""
+    user_with_name = User.objects.create_user(
+        email='fullname@example.com', firebase_uid='fullnameuid', first_name='Full', last_name='Name'
+    )
+    user_without_name = User.objects.create_user(
+        email='noname@example.com', firebase_uid='nonameuid'
+    )
+    assert user_with_name.get_full_name() == 'Full Name'
+    assert user_without_name.get_full_name() == 'noname@example.com' # Fallback
+
+# --- Video Model Tests ---
+
+def test_create_video():
+    """Test creating a video instance."""
+    user = User.objects.create_user(email='uploader@example.com', firebase_uid='uploaderuid')
+    video = Video.objects.create(
+        title='Test Video Title',
+        description='A description for the test video.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/video.mp4',
+        thumbnail_url='http://example.com/thumb.jpg',
+        uploader=user,
+        duration='05:30'
+    )
+    assert video.title == 'Test Video Title'
+    assert video.description == 'A description for the test video.'
+    assert video.category == 'Testing'
+    assert video.visibility == 'public'
+    assert video.video_url == 'http://example.com/video.mp4'
+    assert video.thumbnail_url == 'http://example.com/thumb.jpg'
+    assert video.uploader == user
+    assert video.duration == '05:30'
+    assert video.views == 0 # Default value
+    assert video.likes == 0 # Default value
+    assert video.upload_date is not None
+    assert isinstance(video.upload_date, timezone.datetime)
+
path: backend/api/tests/test_models.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**BEEP BOOP – Extract common fixtures to reduce duplication**

Many tests create users and videos inline. Pytest fixtures keep test bodies tiny and DRY:

```diff
-    user = User.objects.create_user(email='uploader@example.com', firebase_uid='uploaderuid')
+@pytest.fixture
+def uploader():
+    return User.objects.create_user(email='uploader@example.com', firebase_uid='uploaderuid')
+
+def test_create_video(uploader):
+    video = Video.objects.create(
+        title='Test Video Title',
+        ...
+        uploader=uploader,
+    )
```

Not mandatory for coverage, but it speeds future maintenance.  



> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

86-86: Missing return type annotation for public function `test_create_video`

Add return type annotation: `None`

(ANN201)

---

97-97: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
+class FirebaseAuthentication(authentication.BaseAuthentication):
+    """
+    Authenticates requests using Firebase Authentication ID tokens.
+    If the user doesn't exist locally, it attempts to create one
+    based on data from Firestore or Firebase Auth.
+    """
+
+    def authenticate(self, request):
+        """
+        Main authentication method called by DRF.
+        """
+        id_token = self._extract_token(request)
+        if not id_token:
+            return None # No token provided, authentication not attempted
+
+        try:
+            decoded_token = self._verify_firebase_token(id_token)
+            uid = decoded_token["uid"]
+            user = self._get_or_create_user(uid, decoded_token)
+            return (user, None) # Successful authentication
+        except exceptions.AuthenticationFailed:
+            # Let AuthenticationFailed exceptions propagate
+            raise
+        except Exception as e:
+            # Catch other potential errors during user get/create
+            logger.error(f"Unexpected error during authentication for token: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed(f'Authentication error: {str(e)}')
+
+    def _extract_token(self, request):
+
+        auth_header = request.META.get("HTTP_AUTHORIZATION")
+        if not auth_header:
+            return None
+
+        token_parts = auth_header.split()
+        if len(token_parts) != 2 or token_parts[0].lower() != "bearer":
+            logger.warning("Invalid Authorization header format.")
+            return None # Invalid format
+
+        return token_parts[1]
+
+    def _verify_firebase_token(self, id_token):
+
+        try:
+            # Verify token using Firebase Admin SDK
+            return auth.verify_id_token(id_token)
+        except Exception as e:
+            logger.warning(f"Firebase token verification failed: {e}")
+            raise exceptions.AuthenticationFailed(f'Invalid Firebase token: {str(e)}')
+
+    def _get_or_create_user(self, uid, decoded_token):
+
+        try:
+            # Try to get the user from the local database
+            user = User.objects.get(firebase_uid=uid)
+            return user
+        except User.DoesNotExist:
+            # User not found locally, proceed to create
+            logger.info(f"User with firebase_uid {uid} not found locally. Attempting creation.")
+            user_details = self._fetch_user_details(uid, decoded_token)
+            return self._create_user_with_profile(uid, user_details)
+
+    def _fetch_user_details(self, uid, decoded_token):
+
+        # Try Firestore first
+        firestore_data = self._fetch_from_firestore(uid)
+        if firestore_data:
+            return firestore_data
+
+        # Fallback to Firebase Auth
+        logger.info(f"No Firestore data for uid {uid}. Falling back to Firebase Auth.")
+        return self._fetch_from_firebase_auth(uid, decoded_token)
+
path: backend/backend/authentication.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**BEEP BOOP – Guard against missing e‑mail from Firestore**

`_fetch_user_details` returns Firestore data even when the `email` key is empty, leading to a later hard failure.  Skip to Firebase Auth fallback if e‑mail is absent:

```diff
-        if firestore_data:
+        if firestore_data and firestore_data.get("email"):
             return firestore_data
```

Prevents `AuthenticationFailed("Cannot create user without an email…")` for partially‑filled documents.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    def _fetch_user_details(self, uid, decoded_token):

        # Try Firestore first
        firestore_data = self._fetch_from_firestore(uid)
        if firestore_data and firestore_data.get("email"):
            return firestore_data

        # Fallback to Firebase Auth
        logger.info(f"No Firestore data for uid {uid}. Falling back to Firebase Auth.")
        return self._fetch_from_firebase_auth(uid, decoded_token)
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

73-73: Missing return type annotation for private function `_fetch_user_details`

(ANN202)

---

73-73: Missing type annotation for function argument `uid`

(ANN001)

---

73-73: Missing type annotation for function argument `decoded_token`

(ANN001)

---

81-81: Logging statement uses f-string

(G004)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
+class FirebaseAuthentication(authentication.BaseAuthentication):
+    """
+    Authenticates requests using Firebase Authentication ID tokens.
+    If the user doesn't exist locally, it attempts to create one
+    based on data from Firestore or Firebase Auth.
+    """
+
+    def authenticate(self, request):
+        """
+        Main authentication method called by DRF.
+        """
+        id_token = self._extract_token(request)
+        if not id_token:
+            return None # No token provided, authentication not attempted
+
+        try:
+            decoded_token = self._verify_firebase_token(id_token)
+            uid = decoded_token["uid"]
+            user = self._get_or_create_user(uid, decoded_token)
+            return (user, None) # Successful authentication
+        except exceptions.AuthenticationFailed:
+            # Let AuthenticationFailed exceptions propagate
+            raise
+        except Exception as e:
+            # Catch other potential errors during user get/create
+            logger.error(f"Unexpected error during authentication for token: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed(f'Authentication error: {str(e)}')
+
+    def _extract_token(self, request):
+
+        auth_header = request.META.get("HTTP_AUTHORIZATION")
+        if not auth_header:
+            return None
+
+        token_parts = auth_header.split()
+        if len(token_parts) != 2 or token_parts[0].lower() != "bearer":
+            logger.warning("Invalid Authorization header format.")
+            return None # Invalid format
+
+        return token_parts[1]
+
+    def _verify_firebase_token(self, id_token):
+
+        try:
+            # Verify token using Firebase Admin SDK
+            return auth.verify_id_token(id_token)
+        except Exception as e:
+            logger.warning(f"Firebase token verification failed: {e}")
+            raise exceptions.AuthenticationFailed(f'Invalid Firebase token: {str(e)}')
+
+    def _get_or_create_user(self, uid, decoded_token):
+
+        try:
+            # Try to get the user from the local database
+            user = User.objects.get(firebase_uid=uid)
+            return user
+        except User.DoesNotExist:
+            # User not found locally, proceed to create
+            logger.info(f"User with firebase_uid {uid} not found locally. Attempting creation.")
+            user_details = self._fetch_user_details(uid, decoded_token)
+            return self._create_user_with_profile(uid, user_details)
+
+    def _fetch_user_details(self, uid, decoded_token):
+
+        # Try Firestore first
+        firestore_data = self._fetch_from_firestore(uid)
+        if firestore_data:
+            return firestore_data
+
+        # Fallback to Firebase Auth
+        logger.info(f"No Firestore data for uid {uid}. Falling back to Firebase Auth.")
+        return self._fetch_from_firebase_auth(uid, decoded_token)
+
+    def _fetch_from_firestore(self, uid):
+
+        try:
+            user_ref = db.collection('users').document(uid)
+            user_doc = user_ref.get()
+            if user_doc.exists:
+                user_data = user_doc.to_dict()
+                return {
+                    'first_name': user_data.get('firstName', ''),
+                    'last_name': user_data.get('lastName', ''),
+                    'email': user_data.get('email', ''), # Ensure email is present
+                    'role': user_data.get('role', 'user') # Default role
+                }
+        except Exception as e:
+            logger.error(f"Error fetching user data from Firestore for uid {uid}: {e}", exc_info=True)
+        return None
+
+    def _fetch_from_firebase_auth(self, uid, decoded_token):
+
+        try:
+            # Use email from token if possible, otherwise query Firebase Auth user record
+            email = decoded_token.get('email')
+            if not email:
+                firebase_user = auth.get_user(uid)
+                email = firebase_user.email
+
+            # Ensure a valid email exists, generate placeholder if needed
+            if not email:
+                 logger.warning(f"No email found for uid {uid} in token or Firebase Auth. Using placeholder.")
+                 email = f"{uid}@example.com" # Placeholder email
+
+            return {
+                'first_name': '', # Firebase Auth doesn't reliably store names here
+                'last_name': '',
+                'email': email,
+                'role': 'user' # Default role for fallback
+            }
+        except auth.UserNotFoundError:
+             logger.error(f"User with uid {uid} not found in Firebase Auth.")
+             raise exceptions.AuthenticationFailed("User associated with token not found.")
+        except Exception as e:
+            logger.error(f"Error fetching user data from Firebase Auth for uid {uid}: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed("Could not retrieve user authentication details.")
+
+
+    def _create_user_with_profile(self, uid, user_details):
+
+        if not user_details.get('email'):
+             logger.error(f"Cannot create user with uid {uid}: email is missing.")
+             raise exceptions.AuthenticationFailed("Cannot create user without an email address.")
+
+        try:
+            user = User.objects.create_user(
+                firebase_uid=uid,
+                email=user_details['email'],
+                first_name=user_details['first_name'],
+                last_name=user_details['last_name'],
+                role=user_details['role']
+            )
+            logger.info(f"Successfully created local user for uid {uid} with role {user_details['role']}.")
+            self._create_profile(user, user_details['role'])
+            return user
+        except Exception as e:
+            # Catch potential database errors during user creation
+            logger.error(f"Database error creating user for uid {uid}: {e}", exc_info=True)
+            # Check if user was created despite error (e.g., race condition)
+            try:
+                return User.objects.get(firebase_uid=uid)
+            except User.DoesNotExist:
+                 raise exceptions.AuthenticationFailed("Failed to create local user record.")
+
path: backend/backend/authentication.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**BEEP BOOP – Race‑condition retry should be explicit**

If user creation raises an integrity error due to a concurrent create, silently retrying with a generic `Exception` mask may swallow genuine bugs. Catch `IntegrityError` explicitly:

```diff
-        except Exception as e:
+        except IntegrityError as e:
             logger.error("IntegrityError creating user …")
             ...
```

Other exceptions should propagate; otherwise debugging becomes painful.  



> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

148-148: Logging `.exception(...)` should be used instead of `.error(..., exc_info=True)`

(G201)

---

148-148: Logging statement uses f-string

(G004)

---

153-153: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

153-153: Avoid specifying long messages outside the exception class

(TRY003)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
+class FirebaseAuthentication(authentication.BaseAuthentication):
+    """
+    Authenticates requests using Firebase Authentication ID tokens.
+    If the user doesn't exist locally, it attempts to create one
+    based on data from Firestore or Firebase Auth.
+    """
+
+    def authenticate(self, request):
+        """
+        Main authentication method called by DRF.
+        """
+        id_token = self._extract_token(request)
+        if not id_token:
+            return None # No token provided, authentication not attempted
+
+        try:
+            decoded_token = self._verify_firebase_token(id_token)
+            uid = decoded_token["uid"]
+            user = self._get_or_create_user(uid, decoded_token)
+            return (user, None) # Successful authentication
+        except exceptions.AuthenticationFailed:
+            # Let AuthenticationFailed exceptions propagate
+            raise
+        except Exception as e:
+            # Catch other potential errors during user get/create
+            logger.error(f"Unexpected error during authentication for token: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed(f'Authentication error: {str(e)}')
+
+    def _extract_token(self, request):
+
+        auth_header = request.META.get("HTTP_AUTHORIZATION")
+        if not auth_header:
+            return None
+
+        token_parts = auth_header.split()
+        if len(token_parts) != 2 or token_parts[0].lower() != "bearer":
+            logger.warning("Invalid Authorization header format.")
+            return None # Invalid format
+
+        return token_parts[1]
+
+    def _verify_firebase_token(self, id_token):
+
+        try:
+            # Verify token using Firebase Admin SDK
+            return auth.verify_id_token(id_token)
+        except Exception as e:
+            logger.warning(f"Firebase token verification failed: {e}")
+            raise exceptions.AuthenticationFailed(f'Invalid Firebase token: {str(e)}')
+
path: backend/backend/authentication.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

_🛠️ Refactor suggestion_

**BEEP BOOP – Catch specific verification errors and avoid info‑leak**

Catching broad `Exception` both hides actionable errors and exposes internals via `str(e)` to clients.

```diff
-        except Exception as e:
-            logger.warning(f"Firebase token verification failed: {e}")
-            raise exceptions.AuthenticationFailed(f'Invalid Firebase token: {str(e)}')
+        except auth.InvalidIdTokenError:
+            logger.info("Firebase token invalid or expired.")
+            raise exceptions.AuthenticationFailed("Invalid Firebase token.")
+        except Exception:
+            logger.exception("Unexpected verification failure.")
+            raise exceptions.AuthenticationFailed("Authentication service error.")
```

• Use specific Firebase errors  
• Log stack‑trace with `logger.exception` but send a generic message outward, limiting information exposure flagged by CodeQL.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    def _verify_firebase_token(self, id_token):

        try:
            # Verify token using Firebase Admin SDK
            return auth.verify_id_token(id_token)
        except auth.InvalidIdTokenError:
            logger.info("Firebase token invalid or expired.")
            raise exceptions.AuthenticationFailed("Invalid Firebase token.")
        except Exception:
            logger.exception("Unexpected verification failure.")
            raise exceptions.AuthenticationFailed("Authentication service error.")
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

52-52: Missing return type annotation for private function `_verify_firebase_token`

(ANN202)

---

52-52: Missing type annotation for function argument `id_token`

(ANN001)

---

57-57: Do not catch blind exception: `Exception`

(BLE001)

---

58-58: Logging statement uses f-string

(G004)

---

59-59: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

(B904)

---

59-59: Avoid specifying long messages outside the exception class

(TRY003)

---

59-59: Use explicit conversion flag

Replace with conversion flag

(RUF010)

</details>
<details>
<summary>🪛 GitHub Check: CodeQL</summary>

[warning] 59-59: Information exposure through an exception
[Stack trace information](1) flows to this location and may be exposed to an external user.

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
path: backend/backend/authentication.py
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**BEEP BOOP – Delay heavy Firestore client creation**

`firestore.client()` is executed at import time, opening network sockets even when authentication is never used (e.g., management commands, unit tests).  Postpone until actually needed:

```diff
-db = firestore.client()
+def _get_db():
+    # Lazily initialise; cache the instance for reuse
+    if not hasattr(_get_db, "_client"):
+        _get_db._client = firestore.client()
+    return _get_db._client
```

Then replace `db` usages with `_get_db()`.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import logging
from rest_framework import authentication, exceptions
from firebase_admin import auth, firestore
from api.models import User

# It's generally better to get the logger instance
logger = logging.getLogger(__name__)

def _get_db():
    # Lazily initialise; cache the instance for reuse
    if not hasattr(_get_db, "_client"):
        _get_db._client = firestore.client()
    return _get_db._client
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**TYPE ANNOTATIONS MISSING FROM FIXTURES.**

STATIC ANALYSIS REPORTS TYPE ANNOTATION DEFICIENCY. RECOMMEND ENHANCEMENT OF FUNCTION SIGNATURES.

```diff
@pytest.fixture
-def api_client():
+def api_client() -> APIClient:
    """Fixture to provide an API client instance."""
    return APIClient()

@pytest.fixture
-def test_user():
+def test_user() -> User:
    """Fixture to create a standard test user."""
    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
@pytest.fixture
def api_client() -> APIClient:
    """Fixture to provide an API client instance."""
    return APIClient()

@pytest.fixture
def test_user() -> User:
    """Fixture to create a standard test user."""
    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

11-11: Missing return type annotation for public function `api_client`

(ANN201)

---

16-16: Missing return type annotation for public function `test_user`

(ANN201)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
+
+# --- VideoDetailView Tests ---
+
+def test_video_detail_retrieve_success(api_client, sample_video):
+    """Test retrieving a specific video detail successfully."""
+    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['id'] == sample_video.pk
+    assert response.data['title'] == sample_video.title
+    assert response.data['description'] == sample_video.description
+    assert response.data['video_url'] == sample_video.video_url
+    # Add more assertions based on VideoDetailSerializer fields
+
+def test_video_detail_not_found(api_client):
+    """Test retrieving a non-existent video detail returns 404."""
+    url = reverse('video-detail', kwargs={'pk': 999}) # Assuming 999 doesn't exist
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_404_NOT_FOUND
+
+# --- TestAuthView Tests ---
+
+@pytest.fixture
+def authenticated_api_client(api_client, test_user):
+    """Fixture for an authenticated API client."""
+    # Note: In a real scenario with JWT/Firebase, you'd mock the authentication
+    # or use a mechanism to force authenticate. For basic DRF session auth (if enabled)
+    # or token auth, you might use force_authenticate.
+    # Since we use Firebase, proper testing requires mocking firebase_admin.auth
+    # or setting up test tokens. For now, we'll simulate by forcing authentication.
+    api_client.force_authenticate(user=test_user)
+    return api_client
+
+def test_auth_test_view_authenticated(authenticated_api_client):
+    """Test accessing the protected view when authenticated."""
+    url = reverse('auth-test')
+    response = authenticated_api_client.get(url)
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['success'] is True
+
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**AUTHENTICATION TEST MISSING TYPE ANNOTATIONS.**

FUNCTION SIGNATURE ENHANCEMENT REQUIRED.

```diff
-def test_auth_test_view_authenticated(authenticated_api_client):
+def test_auth_test_view_authenticated(authenticated_api_client: APIClient) -> None:
    """Test accessing the protected view when authenticated."""
    url = reverse('auth-test')
    response = authenticated_api_client.get(url)
    assert response.status_code == status.HTTP_200_OK
    assert response.data['success'] is True
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def test_auth_test_view_authenticated(authenticated_api_client: APIClient) -> None:
    """Test accessing the protected view when authenticated."""
    url = reverse('auth-test')
    response = authenticated_api_client.get(url)
    assert response.status_code == status.HTTP_200_OK
    assert response.data['success'] is True
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

98-98: Missing return type annotation for public function `test_auth_test_view_authenticated`

Add return type annotation: `None`

(ANN201)

---

98-98: Missing type annotation for function argument `authenticated_api_client`

(ANN001)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
+
+# --- VideoDetailView Tests ---
+
+def test_video_detail_retrieve_success(api_client, sample_video):
+    """Test retrieving a specific video detail successfully."""
+    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['id'] == sample_video.pk
+    assert response.data['title'] == sample_video.title
+    assert response.data['description'] == sample_video.description
+    assert response.data['video_url'] == sample_video.video_url
+    # Add more assertions based on VideoDetailSerializer fields
+
+def test_video_detail_not_found(api_client):
+    """Test retrieving a non-existent video detail returns 404."""
+    url = reverse('video-detail', kwargs={'pk': 999}) # Assuming 999 doesn't exist
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_404_NOT_FOUND
+
+# --- TestAuthView Tests ---
+
+@pytest.fixture
+def authenticated_api_client(api_client, test_user):
+    """Fixture for an authenticated API client."""
+    # Note: In a real scenario with JWT/Firebase, you'd mock the authentication
+    # or use a mechanism to force authenticate. For basic DRF session auth (if enabled)
+    # or token auth, you might use force_authenticate.
+    # Since we use Firebase, proper testing requires mocking firebase_admin.auth
+    # or setting up test tokens. For now, we'll simulate by forcing authentication.
+    api_client.force_authenticate(user=test_user)
+    return api_client
+
+def test_auth_test_view_authenticated(authenticated_api_client):
+    """Test accessing the protected view when authenticated."""
+    url = reverse('auth-test')
+    response = authenticated_api_client.get(url)
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['success'] is True
+
+def test_auth_test_view_unauthenticated(api_client):
+    """Test accessing the protected view when unauthenticated."""
+    url = reverse('auth-test')
+    response = api_client.get(url)
+    # Expecting 401 Unauthorized or 403 Forbidden depending on DRF default settings
+    assert response.status_code in [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**UNAUTHENTICATED TEST MISSING TYPE ANNOTATIONS.**

FUNCTION SIGNATURE ENHANCEMENT REQUIRED.

```diff
-def test_auth_test_view_unauthenticated(api_client):
+def test_auth_test_view_unauthenticated(api_client: APIClient) -> None:
    """Test accessing the protected view when unauthenticated."""
    url = reverse('auth-test')
    response = api_client.get(url)
    # Expecting 401 Unauthorized or 403 Forbidden depending on DRF default settings
    assert response.status_code in [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
-def test_auth_test_view_unauthenticated(api_client):
+def test_auth_test_view_unauthenticated(api_client: APIClient) -> None:
     """Test accessing the protected view when unauthenticated."""
     url = reverse('auth-test')
     response = api_client.get(url)
     # Expecting 401 Unauthorized or 403 Forbidden depending on DRF default settings
     assert response.status_code in [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

105-105: Missing return type annotation for public function `test_auth_test_view_unauthenticated`

Add return type annotation: `None`

(ANN201)

---

105-105: Missing type annotation for function argument `api_client`

(ANN001)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**SAMPLE_VIDEO FIXTURE REQUIRES TRAILING COMMA AND TYPE ANNOTATIONS.**

CODE ANALYSIS DETECTS FORMATTING AND TYPE CONSISTENCY ISSUES.

```diff
@pytest.fixture
-def sample_video(test_user):
+def sample_video(test_user: User) -> Video:
    """Fixture to create a sample video linked to the test user."""
    return Video.objects.create(
        title='Sample Video',
        description='A test video description.',
        category='Testing',
        visibility='public',
        video_url='http://example.com/sample.mp4',
        thumbnail_url='http://example.com/sample_thumb.jpg',
        uploader=test_user,
-        duration='01:00'
+        duration='01:00',
    )
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
@pytest.fixture
def sample_video(test_user: User) -> Video:
    """Fixture to create a sample video linked to the test user."""
    return Video.objects.create(
        title='Sample Video',
        description='A test video description.',
        category='Testing',
        visibility='public',
        video_url='http://example.com/sample.mp4',
        thumbnail_url='http://example.com/sample_thumb.jpg',
        uploader=test_user,
        duration='01:00',
    )
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

21-21: Missing return type annotation for public function `sample_video`

(ANN201)

---

21-21: Missing type annotation for function argument `test_user`

(ANN001)

---

31-31: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
+
+# --- VideoDetailView Tests ---
+
+def test_video_detail_retrieve_success(api_client, sample_video):
+    """Test retrieving a specific video detail successfully."""
+    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['id'] == sample_video.pk
+    assert response.data['title'] == sample_video.title
+    assert response.data['description'] == sample_video.description
+    assert response.data['video_url'] == sample_video.video_url
+    # Add more assertions based on VideoDetailSerializer fields
+
+def test_video_detail_not_found(api_client):
+    """Test retrieving a non-existent video detail returns 404."""
+    url = reverse('video-detail', kwargs={'pk': 999}) # Assuming 999 doesn't exist
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_404_NOT_FOUND
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**TYPE ANNOTATIONS MISSING FROM NOT FOUND TEST.**

FUNCTION SIGNATURE REQUIRES ENHANCEMENT.

```diff
-def test_video_detail_not_found(api_client):
+def test_video_detail_not_found(api_client: APIClient) -> None:
    """Test retrieving a non-existent video detail returns 404."""
    url = reverse('video-detail', kwargs={'pk': 999}) # Assuming 999 doesn't exist
    response = api_client.get(url)

    assert response.status_code == status.HTTP_404_NOT_FOUND
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def test_video_detail_not_found(api_client: APIClient) -> None:
    """Test retrieving a non-existent video detail returns 404."""
    url = reverse('video-detail', kwargs={'pk': 999})  # Assuming 999 doesn't exist
    response = api_client.get(url)

    assert response.status_code == status.HTTP_404_NOT_FOUND
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

78-78: Missing return type annotation for public function `test_video_detail_not_found`

Add return type annotation: `None`

(ANN201)

---

78-78: Missing type annotation for function argument `api_client`

(ANN001)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
+
+# --- VideoDetailView Tests ---
+
+def test_video_detail_retrieve_success(api_client, sample_video):
+    """Test retrieving a specific video detail successfully."""
+    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['id'] == sample_video.pk
+    assert response.data['title'] == sample_video.title
+    assert response.data['description'] == sample_video.description
+    assert response.data['video_url'] == sample_video.video_url
+    # Add more assertions based on VideoDetailSerializer fields
+
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**VIDEO DETAIL TEST CONTAINS INCOMPLETE TODO COMMENT.**

OBSERVATION: COMMENT INDICATES UNFINISHED ASSERTIONS. RECOMMEND IMPLEMENTING COMPREHENSIVE FIELD VALIDATION.

```diff
-def test_video_detail_retrieve_success(api_client, sample_video):
+def test_video_detail_retrieve_success(api_client: APIClient, sample_video: Video) -> None:
    """Test retrieving a specific video detail successfully."""
    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
    response = api_client.get(url)

    assert response.status_code == status.HTTP_200_OK
    assert response.data['id'] == sample_video.pk
    assert response.data['title'] == sample_video.title
    assert response.data['description'] == sample_video.description
    assert response.data['video_url'] == sample_video.video_url
-    # Add more assertions based on VideoDetailSerializer fields
+    # Complete serializer field validation
+    assert response.data['category'] == sample_video.category
+    assert response.data['visibility'] == sample_video.visibility
+    assert response.data['thumbnail_url'] == sample_video.thumbnail_url
+    assert response.data['duration'] == sample_video.duration
+    assert response.data['uploader']['id'] == sample_video.uploader.id
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def test_video_detail_retrieve_success(api_client: APIClient, sample_video: Video) -> None:
    """Test retrieving a specific video detail successfully."""
    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
    response = api_client.get(url)

    assert response.status_code == status.HTTP_200_OK
    assert response.data['id'] == sample_video.pk
    assert response.data['title'] == sample_video.title
    assert response.data['description'] == sample_video.description
    assert response.data['video_url'] == sample_video.video_url
    # Complete serializer field validation
    assert response.data['category'] == sample_video.category
    assert response.data['visibility'] == sample_video.visibility
    assert response.data['thumbnail_url'] == sample_video.thumbnail_url
    assert response.data['duration'] == sample_video.duration
    assert response.data['uploader']['id'] == sample_video.uploader.id
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

66-66: Missing return type annotation for public function `test_video_detail_retrieve_success`

Add return type annotation: `None`

(ANN201)

---

66-66: Missing type annotation for function argument `api_client`

(ANN001)

---

66-66: Missing type annotation for function argument `sample_video`

(ANN001)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**VIDEO FEED SUCCESS TEST REQUIRES EXACT ASSERTION AND TRAILING COMMA.**

IMPRECISE VALIDATION LOGIC DETECTED. EQUALITY ASSERTION RECOMMENDED OVER INEQUALITY CHECK FOR DETERMINISTIC TESTING.

```diff
-def test_video_feed_list_success(api_client, sample_video):
+def test_video_feed_list_success(api_client: APIClient, sample_video: Video) -> None:
    """Test retrieving the video feed successfully."""
    # Create another video to test listing multiple items
    Video.objects.create(
        title='Another Video',
        video_url='http://example.com/another.mp4',
        uploader=sample_video.uploader,
-        visibility='public'
+        visibility='public',
    )
    url = reverse('video-feed')
    response = api_client.get(url)

    assert response.status_code == status.HTTP_200_OK
-    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    assert len(response.data) == 2  # Exactly two videos should be returned
    # Check if essential fields are present in the first result
    assert 'id' in response.data[0]
    assert 'title' in response.data[0]
    assert 'thumbnail_url' in response.data[0]
    assert 'uploader' in response.data[0] # Check for the nested uploader object
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def test_video_feed_list_success(api_client: APIClient, sample_video: Video) -> None:
    """Test retrieving the video feed successfully."""
    # Create another video to test listing multiple items
    Video.objects.create(
        title='Another Video',
        video_url='http://example.com/another.mp4',
        uploader=sample_video.uploader,
        visibility='public',
    )
    url = reverse('video-feed')
    response = api_client.get(url)

    assert response.status_code == status.HTTP_200_OK
    assert len(response.data) == 2  # Exactly two videos should be returned
    # Check if essential fields are present in the first result
    assert 'id' in response.data[0]
    assert 'title' in response.data[0]
    assert 'thumbnail_url' in response.data[0]
    assert 'uploader' in response.data[0]  # Check for the nested uploader object
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

36-36: Missing return type annotation for public function `test_video_feed_list_success`

Add return type annotation: `None`

(ANN201)

---

36-36: Missing type annotation for function argument `api_client`

(ANN001)

---

36-36: Missing type annotation for function argument `sample_video`

(ANN001)

---

43-43: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**TYPE ANNOTATIONS MISSING FROM EMPTY FEED TEST.**

FUNCTION SIGNATURE REQUIRES ENHANCEMENT.

```diff
-def test_video_feed_empty(api_client):
+def test_video_feed_empty(api_client: APIClient) -> None:
    """Test retrieving an empty video feed."""
    url = reverse('video-feed')
    response = api_client.get(url)

    assert response.status_code == status.HTTP_200_OK
    assert len(response.data) == 0
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
def test_video_feed_empty(api_client: APIClient) -> None:
    """Test retrieving an empty video feed."""
    url = reverse('video-feed')
    response = api_client.get(url)

    assert response.status_code == status.HTTP_200_OK
    assert len(response.data) == 0
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

56-56: Missing return type annotation for public function `test_video_feed_empty`

Add return type annotation: `None`

(ANN201)

---

56-56: Missing type annotation for function argument `api_client`

(ANN001)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:39Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import pytest
+from django.urls import reverse
+from rest_framework import status
+from rest_framework.test import APIClient
+from api.models import User, Video
+
+# Mark all tests in this module to use the database
+pytestmark = pytest.mark.django_db
+
+@pytest.fixture
+def api_client():
+    """Fixture to provide an API client instance."""
+    return APIClient()
+
+@pytest.fixture
+def test_user():
+    """Fixture to create a standard test user."""
+    return User.objects.create_user(email='testuser@example.com', firebase_uid='testuseruid')
+
+@pytest.fixture
+def sample_video(test_user):
+    """Fixture to create a sample video linked to the test user."""
+    return Video.objects.create(
+        title='Sample Video',
+        description='A test video description.',
+        category='Testing',
+        visibility='public',
+        video_url='http://example.com/sample.mp4',
+        thumbnail_url='http://example.com/sample_thumb.jpg',
+        uploader=test_user,
+        duration='01:00'
+    )
+
+# --- VideoFeedView Tests ---
+
+def test_video_feed_list_success(api_client, sample_video):
+    """Test retrieving the video feed successfully."""
+    # Create another video to test listing multiple items
+    Video.objects.create(
+        title='Another Video',
+        video_url='http://example.com/another.mp4',
+        uploader=sample_video.uploader,
+        visibility='public'
+    )
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) >= 2 # Check if at least the two created videos are listed
+    # Check if essential fields are present in the first result
+    assert 'id' in response.data[0]
+    assert 'title' in response.data[0]
+    assert 'thumbnail_url' in response.data[0]
+    assert 'uploader' in response.data[0] # Check for the nested uploader object
+
+def test_video_feed_empty(api_client):
+    """Test retrieving an empty video feed."""
+    url = reverse('video-feed')
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert len(response.data) == 0
+
+# --- VideoDetailView Tests ---
+
+def test_video_detail_retrieve_success(api_client, sample_video):
+    """Test retrieving a specific video detail successfully."""
+    url = reverse('video-detail', kwargs={'pk': sample_video.pk})
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_200_OK
+    assert response.data['id'] == sample_video.pk
+    assert response.data['title'] == sample_video.title
+    assert response.data['description'] == sample_video.description
+    assert response.data['video_url'] == sample_video.video_url
+    # Add more assertions based on VideoDetailSerializer fields
+
+def test_video_detail_not_found(api_client):
+    """Test retrieving a non-existent video detail returns 404."""
+    url = reverse('video-detail', kwargs={'pk': 999}) # Assuming 999 doesn't exist
+    response = api_client.get(url)
+
+    assert response.status_code == status.HTTP_404_NOT_FOUND
+
+# --- TestAuthView Tests ---
+
+@pytest.fixture
+def authenticated_api_client(api_client, test_user):
+    """Fixture for an authenticated API client."""
+    # Note: In a real scenario with JWT/Firebase, you'd mock the authentication
+    # or use a mechanism to force authenticate. For basic DRF session auth (if enabled)
+    # or token auth, you might use force_authenticate.
+    # Since we use Firebase, proper testing requires mocking firebase_admin.auth
+    # or setting up test tokens. For now, we'll simulate by forcing authentication.
+    api_client.force_authenticate(user=test_user)
+    return api_client
path: backend/api/tests/test_views.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**AUTHENTICATED CLIENT FIXTURE MISSING TYPE ANNOTATIONS.**

FUNCTION SIGNATURE ENHANCEMENT REQUIRED.

```diff
@pytest.fixture
-def authenticated_api_client(api_client, test_user):
+def authenticated_api_client(api_client: APIClient, test_user: User) -> APIClient:
    """Fixture for an authenticated API client."""
    # Note: In a real scenario with JWT/Firebase, you'd mock the authentication
    # or use a mechanism to force authenticate. For basic DRF session auth (if enabled)
    # or token auth, you might use force_authenticate.
    # Since we use Firebase, proper testing requires mocking firebase_admin.auth
    # or setting up test tokens. For now, we'll simulate by forcing authentication.
    api_client.force_authenticate(user=test_user)
    return api_client
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
@pytest.fixture
def authenticated_api_client(api_client: APIClient, test_user: User) -> APIClient:
    """Fixture for an authenticated API client."""
    # Note: In a real scenario with JWT/Firebase, you'd mock the authentication
    # or use a mechanism to force authenticate. For basic DRF session auth (if enabled)
    # or token auth, you might use force_authenticate.
    # Since we use Firebase, proper testing requires mocking firebase_admin.auth
    # or setting up test tokens. For now, we'll simulate by forcing authentication.
    api_client.force_authenticate(user=test_user)
    return api_client
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

88-88: Missing return type annotation for public function `authenticated_api_client`

(ANN201)

---

88-88: Missing type annotation for function argument `api_client`

(ANN001)

---

88-88: Missing type annotation for function argument `test_user`

(ANN001)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:13:40Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
+class FirebaseAuthentication(authentication.BaseAuthentication):
+    """
+    Authenticates requests using Firebase Authentication ID tokens.
+    If the user doesn't exist locally, it attempts to create one
+    based on data from Firestore or Firebase Auth.
+    """
+
+    def authenticate(self, request):
+        """
+        Main authentication method called by DRF.
+        """
+        id_token = self._extract_token(request)
+        if not id_token:
+            return None # No token provided, authentication not attempted
+
+        try:
+            decoded_token = self._verify_firebase_token(id_token)
+            uid = decoded_token["uid"]
+            user = self._get_or_create_user(uid, decoded_token)
+            return (user, None) # Successful authentication
+        except exceptions.AuthenticationFailed:
+            # Let AuthenticationFailed exceptions propagate
+            raise
+        except Exception as e:
+            # Catch other potential errors during user get/create
+            logger.error(f"Unexpected error during authentication for token: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed(f'Authentication error: {str(e)}')
path: backend/backend/authentication.py
user: login: github-advanced-security[bot]
body: ## Information exposure through an exception

[Stack trace information](1) flows to this location and may be exposed to an external user.

[Show more details](https://github.com/Learnathon-By-Geeky-Solutions/h3cker/security/code-scanning/22)
created_at: 2025-04-19T09:16:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,172 @@
+import logging
+from rest_framework import authentication, exceptions
+from firebase_admin import auth, firestore
+from api.models import User
+
+# It's generally better to get the logger instance
+logger = logging.getLogger(__name__)
+
+db = firestore.client()
+
+class FirebaseAuthentication(authentication.BaseAuthentication):
+    """
+    Authenticates requests using Firebase Authentication ID tokens.
+    If the user doesn't exist locally, it attempts to create one
+    based on data from Firestore or Firebase Auth.
+    """
+
+    def authenticate(self, request):
+        """
+        Main authentication method called by DRF.
+        """
+        id_token = self._extract_token(request)
+        if not id_token:
+            return None # No token provided, authentication not attempted
+
+        try:
+            decoded_token = self._verify_firebase_token(id_token)
+            uid = decoded_token["uid"]
+            user = self._get_or_create_user(uid, decoded_token)
+            return (user, None) # Successful authentication
+        except exceptions.AuthenticationFailed:
+            # Let AuthenticationFailed exceptions propagate
+            raise
+        except Exception as e:
+            # Catch other potential errors during user get/create
+            logger.error(f"Unexpected error during authentication for token: {e}", exc_info=True)
+            raise exceptions.AuthenticationFailed(f'Authentication error: {str(e)}')
+
+    def _extract_token(self, request):
+
+        auth_header = request.META.get("HTTP_AUTHORIZATION")
+        if not auth_header:
+            return None
+
+        token_parts = auth_header.split()
+        if len(token_parts) != 2 or token_parts[0].lower() != "bearer":
+            logger.warning("Invalid Authorization header format.")
+            return None # Invalid format
+
+        return token_parts[1]
+
+    def _verify_firebase_token(self, id_token):
+
+        try:
+            # Verify token using Firebase Admin SDK
+            return auth.verify_id_token(id_token)
+        except Exception as e:
+            logger.warning(f"Firebase token verification failed: {e}")
+            raise exceptions.AuthenticationFailed(f'Invalid Firebase token: {str(e)}')
path: backend/backend/authentication.py
user: login: github-advanced-security[bot]
body: ## Information exposure through an exception

[Stack trace information](1) flows to this location and may be exposed to an external user.

[Show more details](https://github.com/Learnathon-By-Geeky-Solutions/h3cker/security/code-scanning/23)
created_at: 2025-04-19T09:16:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -18,10 +18,35 @@
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
+      - name: Create Firebase Credentials File
+        run: echo "${{ secrets.FIREBASE_CREDENTIALS_JSON }}" > backend/firebase-credentials.json
+        shell: bash # Ensure bash shell for proper echo behavior
+
+      - name: Create Backend .env File
+        env:
+          BACKEND_ENV_CONTENT: ${{ secrets.BACKEND_ENV_FILE }}
+        run: printf "%s\n" "$BACKEND_ENV_CONTENT" > backend/.env
+        shell: bash # Use printf for safer handling of special characters
+
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Great use of `printf` for `.env` file, but fix comment formatting**. Beep boop – your `printf` approach correctly preserves special characters. However, the inline comment on line 40 (`shell: bash # Use printf for safer handling of special characters`) needs two spaces before `#` to pass YAML lint rules.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 40-40: too few spaces before comment

(comments)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit c2e9ff8
created_at: 2025-04-19T09:18:40Z
author_association: NONE
diff_hunk: @@ -18,10 +18,35 @@
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Remove redundant blank line**. Beep boop – extraneous blank line at line 21 adds unnecessary whitespace to the workflow. Please delete this line to keep the YAML tidy.

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit c2e9ff8
created_at: 2025-04-19T09:18:41Z
author_association: NONE
diff_hunk: @@ -18,10 +18,40 @@ jobs:
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
+      - name: Create Firebase Credentials File
+        env:
+          FIREBASE_CREDS: ${{ secrets.FIREBASE_CREDENTIALS_JSON }}
+        run: |
+          cat <<EOF > backend/firebase-credentials.json
+          ${FIREBASE_CREDS}
+          EOF
+        shell: bash # Use cat with heredoc for exact content preservation
+
+      - name: Create Backend .env File
+        env:
+          BACKEND_ENV_CONTENT: ${{ secrets.BACKEND_ENV_FILE }}
+        run: printf "%s\n" "$BACKEND_ENV_CONTENT" > backend/.env
+        shell: bash # Use printf for safer handling of special characters
+
+      - name: Run tests and generate coverage report
+        run: |
+          cd backend
+          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Use `working-directory` for test execution**  
Beep boop – you can simplify the test step by removing `cd` and using `working-directory`:  
```diff
-      - name: Run tests and generate coverage report
-        run: |
-          cd backend
-          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
+      - name: Run tests and generate coverage report
+        working-directory: backend
+        run: pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      - name: Run tests and generate coverage report
        working-directory: backend
        run: pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T09:22:51Z
author_association: NONE
diff_hunk: @@ -18,10 +18,61 @@ jobs:
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Remove trailing whitespace**
Beep boop – lines 31, 41, 48, 64, and 72 contain trailing spaces which will fail YAML lint (`trailing-spaces`). Please trim these to keep the workflow healthy.



Also applies to: 41-41, 48-48, 64-64, 72-72

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 31-31: trailing spaces

(trailing-spaces)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit d167858
created_at: 2025-04-19T10:06:59Z
author_association: NONE
diff_hunk: @@ -18,10 +18,61 @@
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
+      - name: Create Backend .env File
+        run: |
+          # Using echo block redirection as per user example
+          {
+            echo "SECRET_KEY=${{ secrets.SECRET_KEY }}"
+            echo "DEBUG=${{ secrets.DEBUG }}" # Or set directly, e.g., DEBUG=False
+            echo "ENVIRONMENT=${{ secrets.ENVIRONMENT }}" # Or set directly, e.g., ENVIRONMENT=CI
+            echo "ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }}"
+            echo "CORS_ALLOWED_ORIGINS=${{ secrets.CORS_ALLOWED_ORIGINS }}"
+            
+            # Azure
+            echo "AZURE_STORAGE_ACCOUNT_NAME=${{ secrets.AZURE_STORAGE_ACCOUNT_NAME }}"
+            echo "AZURE_STORAGE_ACCOUNT_KEY=${{ secrets.AZURE_STORAGE_ACCOUNT_KEY }}"
+            echo "AZURE_VIDEO_CONTAINER_NAME=${{ secrets.AZURE_VIDEO_CONTAINER_NAME }}"
+            echo "AZURE_THUMBNAIL_CONTAINER_NAME=${{ secrets.AZURE_THUMBNAIL_CONTAINER_NAME }}"
+            echo "GH_ACTIONS=${{ secrets.GH_ACTIONS }}"
+            
+            # Firebase
+            echo "FIREBASE_TYPE=${{ secrets.FIREBASE_TYPE }}"
+            echo "FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }}"
+            echo "FIREBASE_PRIVATE_KEY_ID=${{ secrets.FIREBASE_PRIVATE_KEY_ID }}"
+            # Using echo without quotes around the secret can be problematic for multi-line keys.
+            # Ensure the secret value itself is formatted correctly (e.g., contains literal \n)
+            # and doesn't contain characters that break the echo command.
+            echo "FIREBASE_PRIVATE_KEY=${{ secrets.FIREBASE_PRIVATE_KEY }}"
+            echo "FIREBASE_CLIENT_EMAIL=${{ secrets.FIREBASE_CLIENT_EMAIL }}"
+            echo "FIREBASE_CLIENT_ID=${{ secrets.FIREBASE_CLIENT_ID }}"
+            echo "FIREBASE_AUTH_URI=${{ secrets.FIREBASE_AUTH_URI }}"
+            echo "FIREBASE_TOKEN_URI=${{ secrets.FIREBASE_TOKEN_URI }}"
+            echo "FIREBASE_AUTH_PROVIDER_X509_CERT_URL=${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}"
+            echo "FIREBASE_CLIENT_X509_CERT_URL=${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}"
+            echo "FIREBASE_UNIVERSE_DOMAIN=${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}"
+            
+          } > backend/.env
+        shell: bash
+
+      - name: Run tests and generate coverage report
+        run: |
+          cd backend
+          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
       
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Simplify test step with working-directory**
Beep boop – instead of spawning a subshell via `cd backend`, use:
```yaml
working-directory: backend
run: pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
```
This reduces complexity and avoids trailing spaces.


```diff
-      - name: Run tests and generate coverage report
-        run: |
-          cd backend
-          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
+      - name: Run tests and generate coverage report
+        working-directory: backend
+        run: pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 72-72: trailing spaces

(trailing-spaces)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:06:59Z
author_association: NONE
diff_hunk: @@ -18,10 +18,61 @@
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Update setup-python action to latest minor version**
Beep boop – `actions/setup-python@v4` is outdated and may not run on GitHub’s runners. Update to `actions/setup-python@v5` to ensure compatibility.


```diff
-      - name: Set up Python 3.12
-        uses: actions/setup-python@v4
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v5
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      - name: Set up Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

23-23: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:06:59Z
author_association: NONE
diff_hunk: @@ -153,11 +155,11 @@
 
 REST_FRAMEWORK = {
     'DEFAULT_AUTHENTICATION_CLASSES': [
-        'api.authentication.FirebaseAuthentication',
+        'backend.authentication.FirebaseAuthentication', # Updated path
         'rest_framework.authentication.SessionAuthentication',
     ],
     'DEFAULT_PERMISSION_CLASSES': [
         'rest_framework.permissions.IsAuthenticated',
     ]
path: backend/backend/settings.py
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**MISSING TRAILING COMMA IN REST_FRAMEWORK CONFIGURATION.**

BEEP BOOP. STATIC ANALYSIS DETECTED MISSING TRAILING COMMA. CONSISTENCY IN SYNTAX IMPROVES CODE READABILITY.

```diff
    'DEFAULT_PERMISSION_CLASSES': [
-        'rest_framework.permissions.IsAuthenticated',
+        'rest_framework.permissions.IsAuthenticated',
    ]
```


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Ruff (0.8.2)</summary>

163-163: Trailing comma missing

Add trailing comma

(COM812)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:22:59Z
author_association: NONE
diff_hunk: @@ -12,16 +12,87 @@ jobs:
   analyze:
     name: Analyze with SonarCloud
     runs-on: ubuntu-latest
-    
+
+    # Add PostgreSQL service container
+    services:
+      postgres:
+        image: postgres:14 # Or your preferred version
+        env:
+          POSTGRES_USER: testuser
+          POSTGRES_PASSWORD: testpassword
+          POSTGRES_DB: testdb
+        ports:
+          - 5432:5432 # Map port to the host runner
+        # Options to wait for the database to be ready
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+
+    # Set environment variables for the job to use the service DB
+    # These override any values that might be loaded from a .env file by python-dotenv
+    # and are directly used by settings.py via os.getenv()
+    env:
+      # Override database settings for CI
+      ENVIRONMENT: LOCAL # Use a specific value for CI if needed, or keep LOCAL/PROD logic
+      LOCAL_DB_NAME: testdb # Match service POSTGRES_DB
+      LOCAL_DB_USER: testuser # Match service POSTGRES_USER
+      LOCAL_DB_PASSWORD: testpassword # Match service POSTGRES_PASSWORD
+      LOCAL_DB_HOST: localhost # Service is available on localhost within the job
+      LOCAL_DB_PORT: 5432 # Mapped port
+      # Keep other necessary env vars (can still come from secrets if needed)
+      SECRET_KEY: ${{ secrets.SECRET_KEY }} # Still needed
+      DEBUG: "False" # Usually False for CI tests
+      # Add other non-DB vars needed by your app (Azure, Firebase, etc.)
+      AZURE_STORAGE_ACCOUNT_NAME: ${{ secrets.AZURE_STORAGE_ACCOUNT_NAME }}
+      AZURE_STORAGE_ACCOUNT_KEY: ${{ secrets.AZURE_STORAGE_ACCOUNT_KEY }}
+      AZURE_VIDEO_CONTAINER_NAME: ${{ secrets.AZURE_VIDEO_CONTAINER_NAME }}
+      AZURE_THUMBNAIL_CONTAINER_NAME: ${{ secrets.AZURE_THUMBNAIL_CONTAINER_NAME }}
+      FIREBASE_TYPE: ${{ secrets.FIREBASE_TYPE }}
+      FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
+      FIREBASE_PRIVATE_KEY_ID: ${{ secrets.FIREBASE_PRIVATE_KEY_ID }}
+      FIREBASE_PRIVATE_KEY: ${{ secrets.FIREBASE_PRIVATE_KEY }}
+      FIREBASE_CLIENT_EMAIL: ${{ secrets.FIREBASE_CLIENT_EMAIL }}
+      FIREBASE_CLIENT_ID: ${{ secrets.FIREBASE_CLIENT_ID }}
+      FIREBASE_AUTH_URI: ${{ secrets.FIREBASE_AUTH_URI }}
+      FIREBASE_TOKEN_URI: ${{ secrets.FIREBASE_TOKEN_URI }}
+      FIREBASE_AUTH_PROVIDER_X509_CERT_URL: ${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}
+      FIREBASE_CLIENT_X509_CERT_URL: ${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}
+      FIREBASE_UNIVERSE_DOMAIN: ${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}
+      # Note: ALLOWED_HOSTS and CORS might need adjustment for CI if tests make external calls
+      # Set SONAR_TOKEN for the SonarCloud Scan step later
+      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
     steps:
       - name: Checkout code
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
+      # Removed "Create Backend .env File" step as env vars are set at job level
+
+      # Add step to run migrations against the service database
+      - name: Run migrations
+        run: python backend/manage.py migrate
+
+      - name: Run tests and generate coverage report
+        run: |
+          cd backend
+          pytest --cov=api --cov=backend --cov-report=xml:coverage.xml
       
       - name: SonarCloud Scan
         uses: SonarSource/sonarcloud-github-action@v2
         with:
           projectBaseDir: .
-        env:
-          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
\ No newline at end of file
+        # SONAR_TOKEN is already set in the job's env block
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**SONARCLOUD SCAN CONFIGURATION IMPROVED.**

BEEP BOOP. SONAR_TOKEN NOW SET IN JOB ENV BLOCK INSTEAD OF STEP LEVEL. COMMENT FORMATTING NEEDS ADJUSTMENT.


```diff
-          projectBaseDir: .
-        # SONAR_TOKEN is already set in the job's env block
+          projectBaseDir: .
+        # SONAR_TOKEN is already set in the job's env block
```


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[warning] 98-98: comment not indented like content

(comments-indentation)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:22:59Z
author_association: NONE
diff_hunk: @@ -12,16 +12,87 @@
   analyze:
     name: Analyze with SonarCloud
     runs-on: ubuntu-latest
-    
+
+    # Add PostgreSQL service container
+    services:
+      postgres:
+        image: postgres:14 # Or your preferred version
+        env:
+          POSTGRES_USER: testuser
+          POSTGRES_PASSWORD: testpassword
+          POSTGRES_DB: testdb
+        ports:
+          - 5432:5432 # Map port to the host runner
+        # Options to wait for the database to be ready
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+
+    # Set environment variables for the job to use the service DB
+    # These override any values that might be loaded from a .env file by python-dotenv
+    # and are directly used by settings.py via os.getenv()
+    env:
+      # Override database settings for CI
+      ENVIRONMENT: LOCAL # Use a specific value for CI if needed, or keep LOCAL/PROD logic
+      LOCAL_DB_NAME: testdb # Match service POSTGRES_DB
+      LOCAL_DB_USER: testuser # Match service POSTGRES_USER
+      LOCAL_DB_PASSWORD: testpassword # Match service POSTGRES_PASSWORD
+      LOCAL_DB_HOST: localhost # Service is available on localhost within the job
+      LOCAL_DB_PORT: 5432 # Mapped port
+      # Keep other necessary env vars (can still come from secrets if needed)
+      SECRET_KEY: ${{ secrets.SECRET_KEY }} # Still needed
+      DEBUG: "False" # Usually False for CI tests
+      # Add other non-DB vars needed by your app (Azure, Firebase, etc.)
+      AZURE_STORAGE_ACCOUNT_NAME: ${{ secrets.AZURE_STORAGE_ACCOUNT_NAME }}
+      AZURE_STORAGE_ACCOUNT_KEY: ${{ secrets.AZURE_STORAGE_ACCOUNT_KEY }}
+      AZURE_VIDEO_CONTAINER_NAME: ${{ secrets.AZURE_VIDEO_CONTAINER_NAME }}
+      AZURE_THUMBNAIL_CONTAINER_NAME: ${{ secrets.AZURE_THUMBNAIL_CONTAINER_NAME }}
+      FIREBASE_TYPE: ${{ secrets.FIREBASE_TYPE }}
+      FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
+      FIREBASE_PRIVATE_KEY_ID: ${{ secrets.FIREBASE_PRIVATE_KEY_ID }}
+      FIREBASE_PRIVATE_KEY: ${{ secrets.FIREBASE_PRIVATE_KEY }}
+      FIREBASE_CLIENT_EMAIL: ${{ secrets.FIREBASE_CLIENT_EMAIL }}
+      FIREBASE_CLIENT_ID: ${{ secrets.FIREBASE_CLIENT_ID }}
+      FIREBASE_AUTH_URI: ${{ secrets.FIREBASE_AUTH_URI }}
+      FIREBASE_TOKEN_URI: ${{ secrets.FIREBASE_TOKEN_URI }}
+      FIREBASE_AUTH_PROVIDER_X509_CERT_URL: ${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}
+      FIREBASE_CLIENT_X509_CERT_URL: ${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}
+      FIREBASE_UNIVERSE_DOMAIN: ${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}
+      # Note: ALLOWED_HOSTS and CORS might need adjustment for CI if tests make external calls
+      # Set SONAR_TOKEN for the SonarCloud Scan step later
+      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
     steps:
       - name: Checkout code
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
+
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.12'
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
+      
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**DEPENDENCY INSTALLATION CORRECTLY CONFIGURED.**

BEEP BOOP. PYTHON DEPENDENCIES INSTALLATION STEP PROPERLY CONFIGURED FOR TESTING REQUIREMENTS. DETECTED TRAILING SPACES ON LINE 82 THAT SHOULD BE REMOVED.


```diff
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install -r backend/requirements.txt
-      
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install -r backend/requirements.txt
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r backend/requirements.txt
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 82-82: trailing spaces

(trailing-spaces)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:22:59Z
author_association: NONE
diff_hunk: @@ -12,16 +12,87 @@
   analyze:
     name: Analyze with SonarCloud
     runs-on: ubuntu-latest
-    
+
+    # Add PostgreSQL service container
+    services:
+      postgres:
+        image: postgres:14 # Or your preferred version
+        env:
+          POSTGRES_USER: testuser
+          POSTGRES_PASSWORD: testpassword
+          POSTGRES_DB: testdb
+        ports:
+          - 5432:5432 # Map port to the host runner
+        # Options to wait for the database to be ready
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+
+    # Set environment variables for the job to use the service DB
+    # These override any values that might be loaded from a .env file by python-dotenv
+    # and are directly used by settings.py via os.getenv()
+    env:
+      # Override database settings for CI
+      ENVIRONMENT: LOCAL # Use a specific value for CI if needed, or keep LOCAL/PROD logic
+      LOCAL_DB_NAME: testdb # Match service POSTGRES_DB
+      LOCAL_DB_USER: testuser # Match service POSTGRES_USER
+      LOCAL_DB_PASSWORD: testpassword # Match service POSTGRES_PASSWORD
+      LOCAL_DB_HOST: localhost # Service is available on localhost within the job
+      LOCAL_DB_PORT: 5432 # Mapped port
+      # Keep other necessary env vars (can still come from secrets if needed)
+      SECRET_KEY: ${{ secrets.SECRET_KEY }} # Still needed
+      DEBUG: "False" # Usually False for CI tests
+      # Add other non-DB vars needed by your app (Azure, Firebase, etc.)
+      AZURE_STORAGE_ACCOUNT_NAME: ${{ secrets.AZURE_STORAGE_ACCOUNT_NAME }}
+      AZURE_STORAGE_ACCOUNT_KEY: ${{ secrets.AZURE_STORAGE_ACCOUNT_KEY }}
+      AZURE_VIDEO_CONTAINER_NAME: ${{ secrets.AZURE_VIDEO_CONTAINER_NAME }}
+      AZURE_THUMBNAIL_CONTAINER_NAME: ${{ secrets.AZURE_THUMBNAIL_CONTAINER_NAME }}
+      FIREBASE_TYPE: ${{ secrets.FIREBASE_TYPE }}
+      FIREBASE_PROJECT_ID: ${{ secrets.FIREBASE_PROJECT_ID }}
+      FIREBASE_PRIVATE_KEY_ID: ${{ secrets.FIREBASE_PRIVATE_KEY_ID }}
+      FIREBASE_PRIVATE_KEY: ${{ secrets.FIREBASE_PRIVATE_KEY }}
+      FIREBASE_CLIENT_EMAIL: ${{ secrets.FIREBASE_CLIENT_EMAIL }}
+      FIREBASE_CLIENT_ID: ${{ secrets.FIREBASE_CLIENT_ID }}
+      FIREBASE_AUTH_URI: ${{ secrets.FIREBASE_AUTH_URI }}
+      FIREBASE_TOKEN_URI: ${{ secrets.FIREBASE_TOKEN_URI }}
+      FIREBASE_AUTH_PROVIDER_X509_CERT_URL: ${{ secrets.FIREBASE_AUTH_PROVIDER_X509_CERT_URL }}
+      FIREBASE_CLIENT_X509_CERT_URL: ${{ secrets.FIREBASE_CLIENT_X509_CERT_URL }}
+      FIREBASE_UNIVERSE_DOMAIN: ${{ secrets.FIREBASE_UNIVERSE_DOMAIN }}
+      # Note: ALLOWED_HOSTS and CORS might need adjustment for CI if tests make external calls
+      # Set SONAR_TOKEN for the SonarCloud Scan step later
+      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
     steps:
       - name: Checkout code
         uses: actions/checkout@v3
         with:
           fetch-depth: 0  # Important for SonarQube to see history
path: .github/workflows/sonarqube.yml
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**ACTION VERSION OUTDATED.**

BEEP BOOP. CHECKOUT ACTION VERSION V3 IS OUTDATED. UPDATE RECOMMENDED.

```diff
-      - name: Checkout code
-        uses: actions/checkout@v3
+      - name: Checkout code
+        uses: actions/checkout@v4
```

---

🌐 Web query:

```web
What is the latest version of actions/checkout GitHub Action?
```

💡 Result:

The latest version of the official [actions/checkout](https://github.com/actions/checkout) GitHub Action is **v4** as of April 2025. You should use it in your GitHub Actions workflows like this:

```yaml
- uses: actions/checkout@v4
```

There are multiple confirmations of this in recent documentation and usage examples[1][5][7][9]. As of early 2025, v4 is recommended because older versions (such as v3) rely on deprecated Node.js runtimes[4][8]. For the most robust and secure experience, always use the latest major version, currently v4.

Citations:

- 1: https://github.com/actions/checkout
- 2: https://github.com/marketplace/actions/checkout-action
- 3: https://graphite.dev/guides/github-actions-checkout
- 4: https://discourse.julialang.org/t/how-do-i-update-github-actions-to-node-js-20/110536
- 5: https://spacelift.io/blog/github-actions-checkout
- 6: https://github.com/orgs/community/discussions/25682
- 7: https://www.devzery.com/post/github-actions-complete-guide-to-actions-checkout
- 8: https://github.com/denoland/fresh/issues/2285
- 9: https://community.particle.io/t/github-actions-could-not-find-current-product-version-macro/66338

---



</details>

**Update actions/checkout to v4**

Beep boop! The workflow is using actions/checkout@v3, which is outdated. The latest stable release is v4 as of April 2025—upgrade for better security and Node.js runtime support.

Locations to update:
- .github/workflows/sonarqube.yml (lines 69–71)

Suggested change:
```diff
-      - name: Checkout code
-        uses: actions/checkout@v3
+      - name: Checkout code
+        uses: actions/checkout@v4
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 actionlint (1.7.4)</summary>

69-69: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:22:59Z
author_association: NONE
diff_hunk: @@ -11,8 +11,8 @@ sonar.sources=backend
 sonar.inclusions=backend/**/*.py
 
 # Strict exclusions to prevent scanning frontend and other files
-sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**
+sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**,**/tests/**, **/test_*.py
 
 # Test coverage configuration (if you have tests)
-# sonar.python.coverage.reportPaths=backend/coverage.xml
-# sonar.python.xunit.reportPath=backend/test-reports/*.xml
\ No newline at end of file
+sonar.python.coverage.reportPaths=backend/coverage.xml
+# sonar.python.xunit.reportPath=backend/test-reports/*.xml
path: sonar-project.properties
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Beep boop. Clean up commented-out xUnit report path**  
Beep boop. If you are not generating xUnit‑style XML test reports, consider removing this commented line to reduce clutter. If you do produce them, uncomment and adjust the path so SonarCloud can pick up your test results.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:28:07Z
author_association: NONE
diff_hunk: @@ -11,8 +11,8 @@
 sonar.inclusions=backend/**/*.py
 
 # Strict exclusions to prevent scanning frontend and other files
-sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**
+sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**,**/tests/**, **/test_*.py
path: sonar-project.properties
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Beep boop. Trim whitespace in exclusion patterns**  
Beep boop. The space after the comma in `**,**/tests/**, **/test_*.py` may be interpreted as part of the pattern, causing exclusions to miss files. Removing the extra space ensures both patterns are matched correctly.  
  
```diff
-sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**,**/tests/**, **/test_*.py
+sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**,**/tests/**,**/test_*.py
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
sonar.exclusions=frontend/**/*,**/*.css,**/*.scss,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.html,**/node_modules/**,**/dist/**,**/migrations/**,**/tests/**,**/test_*.py
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-19T10:28:08Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-19T09:10:03Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Summary by CodeRabbit

- **New Features**
  - Added comprehensive unit and API view tests for backend models and endpoints.
  - Introduced configuration files for code coverage and pytest discovery.
  - Expanded environment variable templates to include Firebase credentials and GitHub Actions support.

- **Bug Fixes**
  - Improved Firebase Admin SDK initialization with robust error handling and dynamic credential loading from environment variables.

- **Refactor**
  - Replaced and enhanced the Firebase authentication backend for better reliability and logging.
  - Updated Django settings to use the new authentication backend import path.

- **Chores**
  - Enhanced CI workflow with PostgreSQL service, environment variable management, and automated Django migrations.
  - Updated `.gitignore` to exclude coverage report files.
  - Improved SonarCloud analysis configuration for better test and coverage exclusion.

- **Revert**
  - Removed legacy authentication implementation and a DeepFace emotion analysis notebook.
## Summary by CodeRabbit

- **New Features**
  - Added comprehensive automated tests for backend models and API endpoints.
  - Introduced code coverage configuration and reporting for backend Python code.
  - Added a custom authentication backend for Firebase Authentication integration.

- **Bug Fixes**
  - Updated settings to ensure correct authentication backend import path.
  - Improved Firebase Admin SDK initialization with robust environment-based credential loading and error handling.

- **Chores**
  - Enhanced CI workflow to run backend tests with coverage and prepare environment variables.
  - Updated backend dependencies to include testing tools.
  - Improved .gitignore to exclude coverage files.
  - Enabled SonarCloud to use Python coverage reports.
  - Extended environment variable templates for Firebase integration and GitHub Actions detection.

- **Refactor**
  - Replaced and relocated the custom Firebase authentication class for better structure, logging, and error handling.
  - Updated Django settings for better CI and environment detection.

- **Revert**
  - Removed obsolete DeepFace emotion analysis notebook and legacy authentication module.

## Walkthrough

01100001 01101110 01100001 01101100 01111001 01111010 01101001 01101110 01100111 00101110  . . . SYSTEM UPDATE: Major enhancements to backend testing and authentication. Added pytest configuration, coverage reporting, and new test suites for models and API views. Introduced `.coveragerc` for code coverage control and updated `.gitignore` to exclude coverage files. Authentication logic was refactored and moved to a new backend module. The SonarQube workflow now prepares the environment, runs tests, and generates coverage before scanning. Database settings now adapt for CI environments. Removed a notebook and deprecated authentication code. SonarCloud is configured to use coverage reports. 01100101 01101110 01100100 00101110

## Changes

| File(s)                                        | Change Summary                                                                                                      |
|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
| .github/workflows/sonarqube.yml                 | Enhanced workflow: sets up Python 3.12, installs dependencies, injects environment variables, runs migrations and backend tests with coverage before SonarCloud scan |
| backend/.coveragerc                             | Added configuration for Python coverage, specifying source inclusions and exclusions for run and report sections    |
| backend/.gitignore                              | Updated to ignore coverage report files `.coverage` and `coverage.xml` alongside existing SQLite3 ignores          |
| backend/api/authentication.py                   | Deleted old `FirebaseAuthentication` class and its authentication logic                                             |
| backend/api/tests/test_models.py, backend/api/tests/test_views.py | Added comprehensive pytest test modules for models and API views, including fixtures and assertions                |
| backend/backend/authentication.py               | Added new modular `FirebaseAuthentication` class with helper methods, improved error handling, and detailed logging |
| backend/backend/settings.py                     | Updated DB config comments for CI environment; fixed authentication backend import path                            |
| backend/pytest.ini                              | Added pytest configuration for Django settings module and test discovery patterns                                   |
| backend/requirements.txt                        | Added testing dependencies: `pytest`, `pytest-django`, `pytest-cov`                                                |
| ml-backend/deepface_heatmap.ipynb              | Deleted obsolete DeepFace emotion analysis notebook                                                                 |
| sonar-project.properties                        | Uncommented Python coverage report path for SonarCloud analysis                                                    |
| backend/.env.example                            | Added environment variable declarations for GitHub Actions and Firebase credentials placeholders                    |
| backend/backend/firebase.py                     | Changed Firebase Admin SDK initialization to use environment variables with error handling                          |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant DRF
    participant FirebaseAuthentication
    participant FirebaseAdmin
    participant Firestore
    participant LocalDB

    Client->>DRF: Sends request with Authorization header
    DRF->>FirebaseAuthentication: authenticate(request)
    FirebaseAuthentication->>FirebaseAdmin: verify_id_token(token)
    FirebaseAdmin-->>FirebaseAuthentication: decoded_token
    FirebaseAuthentication->>LocalDB: get_user_by_firebase_uid(uid)
    alt User exists
        LocalDB-->>FirebaseAuthentication: user
    else User does not exist
        FirebaseAuthentication->>Firestore: get_user_details(uid)
        alt Firestore has data
            Firestore-->>FirebaseAuthentication: user_details
        else
            FirebaseAuthentication->>FirebaseAdmin: get_user(uid)
            FirebaseAdmin-->>FirebaseAuthentication: user_record
        end
        FirebaseAuthentication->>LocalDB: create_user(user_details)
        LocalDB-->>FirebaseAuthentication: user
        FirebaseAuthentication->>LocalDB: create_profile(user, role)
    end
    FirebaseAuthentication-->>DRF: user
    DRF-->>Client: Response
```

## Poem

> 01010100 01100101 01110011 01110100 01110011 00100000 01100001 01110010 01100101 00100000 01100010 01101111 01110010 01101110 00101110  
> New coverage rules, so crisp and neat,  
> Authentication logic—now a treat!  
> Pytest marches in, with tests galore,  
> The robot heart is proud to roar.  
> Sonar scans with coverage in sight,  
> All systems green—deploy at night!  
> 🤖✨

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEejqANiS5MlCqVVKQAZvj4NsFCuwVKBaRBIDADlsZgFKLgBWAHYDAFVAii4kZm0LNAp4WUwjfWNwKDJ6fGccAmIyZRp6JlZ2Ll5+YVFxKRl5W2VVdS0dfJMoOFRUTHLCUnIqGoV6jE5IKgB3SEQwtIp5OV9utU1tXTBDAtMDDSJ1WGwBAHoltwBrZwt8JcQbxHwMTIBHa5INLJmBYOAYAEQQgwAYihkAAggBJSpTah0VbrTLyUqMWCYUiIMywEiQADi6gAEtd4WJ4F9kPcKE8XitXHwAMpfTIAYRe2HomDQFlkiFQSwCq3gRAw8Gc8AYmFwQsgZFxGAYaO2aFotHgGCI6EgAAV8IhcERvGyAIoAGVWlAkcuJTHm2imCgwsqInjRS0ukGwSQANDwAogGbRg5h6LRqCoAsSyA6KF82PNIBJMvAVFZEJGXnrIL7cLBIETBcWcaIHvxuOI6ZACMqMGtvIsSFrddJEBpIFy3N5ENwvjqC4n4MmMKncOnM9npC43JAY7g44F3eQaV90BgSsXKOhuNwLHLqLSsIEGN5cMgABRwgBe3uDADFxyR/IFgyRcAwNABKQtKGJQJp2oBsiUgIRBEgKwpAsYN8AcLIR31TB5A0RMXHgKx/UQNBSB7OBiQZJlXkgDAyJIAAPI85UseQQOQbBuCNWRiy3ABmDQAEYACZt3oXVTUFCxkCUbhijIBh4HnZxk2YSAAAN/AYB5ihubw/jfKdu1wKjcEUwiIN4EgHXwANVhoFjizAy92xmNBIBUtSdyUjCMAkRSsJwsVkG8ZhELREU1QTDzxxTHwMyyOdkEyYkKJWXURDENEdW8MQlTAvdIOg2CSAsHs4XIkgVlNEgWIobBm0gAARIQ8XwSBmElaYz1iohXVNcDiWNU1zRIK1bSSB11SckhWQTKjRDwXV9Wc4oG2ka9Cz9JgHHw4lvCHChxD1IziQ5b4KB5cz6EQeVzysts0iE4MBDwbqlLZAB5YI4QAJQAfWgZ6AGkAFFgi8scJynGdooEHDUAomCvlIPhuFDNFqNo6TFXkXVHrKljgtG9QUFiixvC1Bjv0gIgXn8CwlVZR6oIEDQjCMGF4QsGhWvrRtsqUBgMg56rsRRtwZkXbhrmPBgm3EcRpDySBgkahhVXxQDWzSOxGyFna0VF8W5Sl9QZMQABuJrFBlE862quKYPgZqZi5iCyVwSkBGpK36UeZ4yOdT1PFPLdZRwwyLmLa47i95l3k+I6/giQFgUM8FIQMCAwCMeadxuDQ1uUeGGFBCEwWhWFEWRaogoxTZ+DKJW8VlgwivIFZDNzxxKAYLzfclf2re84kRnmZNaGwdV6EbZ1cGTCxWPYrAunsPPiTYAJvSnfalIAbUqjAAF0vIvPvB1EC35xs6c2421ZYHMix6AiJr2xbNFaeyjwvB8NLWjcBjzIoUauhfiorzPks1+6xVcl/MQbgjboiVugZAzVzQB2bMGGgppcxNQFARbgsg7poFUgtbuXp+YCUWOZXa+og7SGDEmXA2BBRNiTBFNMUCCBZBoWQkUNAwCI1UhtbsMAIKKW3uVYW+87SbiwEoWU5BkCI1wOzAWZRjxyIbI1aiIC7ByRYIvdubZtrXmDLqLRYDVHzg2GpegRYSyKV4PhNIXAYZt0UsGPw2AiBgC+EqBeax4HikUp9T6W0KBBK8mwditAIG1GHIbTkM93FzReKpTBMiyAiikAgpIfcF5UCQGAxScJEDZLPP9LwbgvKLkUgrXACJmBHhIFOOgZTkwUFceRL4YAd7fEhk6RQxJIb4BSbbNSSkZSQAAAwcEqXwRS4ygnfDYEEyAABeFZkAADkQSboYCCRs6ZkZXIqFNFQMQj9IliVEG4VE1i/SKQAALHKngQ3AESb60EMkI1AxDe5nibM/ZA589FXy2sLZArgPDIC3N4WCCplRTQ8CuXpOxCwIChlKGBBY6gBSwBRDAYBqLTSRThRcIMWG4DAMfaSspJYLwUUo7sTMS6s3ZigwFjVuaiD5mymu8LDE6wRnrSW7BDYNwVuQRmRc8inEzrQbOodJQUW8IXSELMy6TArmdKuWJa7KwbkRNyCqMXeC8tQlAWBsrKQIS5D5S43zQOrr5f03BlxokbCYiwfJiS4HuOgbUcTvgz3YBwxAXBW6BXbl5KMSlL4ESoonfaa4tQ6g9uak5o9pzO3USgY1xJqF+Xyjc7NC9Y2bTETtQmcMRQawgtRJAlDFjYBwu63NkBBrqBIBxJcsYPx5uwvOG8ikABUGhEA/GPDQDiik/w9gVvwPc7g9XsseoqtwxJ6WUAFnwM16axDelVsvLUAImVqrZtUNq2bOW80yDywWNFhYCp4EKg2MsCQhC+EEKVqcZXWvUmgbg8AbhoDwESeYlszwaFwSq4uaqkQaumJXVgmJeV1z1PqiCSgrAzDNXbBpTT+SMADAQBSwG9xgflDkjIxSlKvm8L2uEIGRUUbPKaxcjk6oNUgO9f6bJoCQGfFQNgJF4SGgRD2BE05qLPJSuPCCEQ4p8AIC5Fw8lHrkmgNAQ08IQMwPvCg0s7YlAUFoZQU+snvX4GUwGMBtH3zxnhLQZqWA2Q1V+oc+gV4OFSHM7hfcCIaqQBvPEfzAEdEKSvf08elmyDienGR9AijGm1jdY1Zw35/EKA/oOWJBYAz7jC49ZJjDlyrgGfINIP4EAFmyrZ3tkBgs1XE2UbKeW+A6noBRSTVF62w3lNTPBKBpx/FM2iWzpo10Lj4MmgNjDWtLm/OkZAfiSzillBQLqiySDBio9OTbX4brwTIdPAEkAERlDG+w4kJXCziiqmgDM2E5zGOnGl6mTlrXZtq/ZhjFY5slbceNYDbMwHNWKWA2U+UokHl4PgXgWYaBCkKsVFYc2nV2SLZjbKRWEk9vszY8Cg9WQVYg5AAAQvGEoFqIKtY2X5fAVhnuMGJg5LAoYhnw7RDDs1ghkrThvCQS4+5FJ9nqWhQ0yZqEzKUgANRkksSg4vSj9unRvN5ig2z0IoOQHzpHQPiAo2iObPPWj8D4HkwI/IsCFMY+RlBz50h0GBsA8qfdxlKbIOmUzNL9POAdyUKbGAsQLuVOUvgQz37eFoBvXXTH9Nq489+Tw2ulLipIF5N3EEfs3yyHpvuZYjM3cQUgYK+pFxpAsETugeZmRgPwMHmPtu+7x+XTD9UdBGZM1Pay1Njsrtcpvamu9/L/dPshvrEVr65Y8lDNdQKtAw1fcCFn2PVt09W9lUBgDQGbf65QZB2QilU6QAALLfnebP7zYaG+75oDeQIFfgyaWwEtP8a/GBUeQIpRfJBl+N5Y+apSDff9QDa/cDL4ffQ/L9NODOX9LOYAm4dBa8BApaT6AKTDbsKDZOGDUuODKoBDLVJDaubEVDfEQkaGEqRaLqNAptPNRcbKWVETBEP1KHOoEyUDDJYkKqfGRA8FOg4RRISgdpRSWXJQfAIQkXRGQPRXSXdzGXOXBXCXZXM2dA3CMBXBRAlaCsDjPURqErOrAhdUajMgOcKPfjPg4kRSAQtpZQ/KRnHg3zdwZnP5fHDMY8aMWML8D0NwQwtsLSSPLCSHW8RSRpdIKNVyRSNbOzQIT6bAeAD5P8EzLICHJbZiSgObdHHJOkZ5XUZaQdJAT6YSZwZwMI+gOZRAAo1IyqJIU1DIIgRABIshJWKsJbKeMBEJaQdgfTaNZwJtGeTbDXLzRhF4C4X8IRCwkQkgMQmwmeewtuJneyZwv0ESAIu+VJQHJtacB0EUNQCdeQVw5/WQk5No8qAcToq2HsZ8UWRQnCNA/KTBfGBwU+GQAIfWDIv5aNI4gsChMWF7RcAQOvWxCQsXa4tPMhYQ+QigaQ5XDeewpGHaTLdKUCRRLIe6GgLJRVHSAHX3DYmcT1ThaNSgVpRYbQEvfgLAXUVwuI81H4zBdJTwEHfpGeHIkgc0dQeQAkmiVoDUEgXEMyCgDvZmUuM9fmZdK9blQfMoLWEWQVMfYVMDSfI/YICgjQnotUVNZNNETGK1QhOArfHg5A00VAxkjAg/UESAXQJSRAz6DIkgGIpIG8V/Awc0qARSK0m0u0ygT6CiT6EI7CB0w/Z0y0lA901rL0/AT6SI3tGIuI/0p0i0104M5nW0tYCSKoygWMwMhMw090lMtIpIMM3AAolcIojM+Mt0pMio1M0MzrSsvM9Mx0zMq00Mk5VAs/RQUsl0ps/M0gQsno6mL0tANgDsoM7Mish0UQ4crMws8cyYz6GRIHac/JHYtkycq0mc8Mls+PVc4MlgSQ2QT6Lnfta0pwr4bcw0h0EqT0w8qwY8hY08x0qAn9HUuVeA/UtcuXE06DZldVPAotNYQgnVHEeuN9YYZAZuSg6cagnCV+OTWA+gOEUTc1KeRQUeecVg7wdgyQb1JaYFJwWmQIdaGeBCpgi8t4NsDIB2Rqdc90GgNMaNUA5jcA07acPLeRNiHC/HbQogRqbjXjfjQTEqR4WnRgnkGSOi1ySxQmGYpaZdPLR6PQ+MC4+AfSb0WKVsGRTsKLHgZMGctsAMOcCC7tFcRo48dgUNbcRg9/MS3ASMQy1rWyvCepHCai1RKxS9XEFioMMhMYBiotYiqynwIFVkaSSGdknfS2M+CCDQ1rDvA1WYiNcCbwQ9TGfy4oIcHI0NIwbiHsIdIdCY/AZ8EgOgWXEqXKrgaAGS81LRYCUeQwxAPsgYmSVw3lRyY8LqbEdczBR45wWQMBbKEyQIEKXlZgDY+ABpdMOIyY6JSANSeQCHVYgA7KAcIcZsLbMhN+PHIkVsGGTq+FetIBCSGkAsMYJLXAeQNq6cfHRyXiCZCZSAP6SyagAMDvXiHKvKya/AGqBbbCEqpYMqmASqj1L1OBOqhqzzJqxhbERyKlC2SWai7YOHDYeQWayMYpSgBtJQFcftegeaqHCreBbKRy8a9c2Q1UWgVRUvMoRyPFAlbrMqNMai/zDXJPMBRyAAFgmTZvljrzMKqjOhXHoUZQMC4kgFyqzwqtNF+v+oluWm6vZIYDqpounick6CJFUj6o8ost8pmF5msrbHVCwtikgBuruoequpBq7BcFqODCWDRWJDGDu3CoN1qFMrTHSgF3aGVEFym0gA5u4nq2+B0xz0fQ5q7UuIoDUG1A92EkFo7xFx0nNR1ANx4AyHVBvjvkoF4Km39StkYXsJhSLUbC+H+MyBQloQ+udReC1FkO/3UWUxAkoWDE6w1qyiJDXHuCbQTz8OXiGQeDASk03UYWGkdBSP8WQFrujQfG9BhJwrWA7V82QC4vwGEu8AcDXFVKkWzQHHp0yXiHemtA6HIkHPtqOTRorUgHU000esFp2GmuWrpCu1jEskqj3VbEbEpNdUsoiF5NpH5JPSFO7wvV73m2vRFN5SlMfTFllJfSNjljhCjvHhwo3tTS1KAL1JkoNMXJKhNMUjNLLJQPXIjKKtoE+guoqIVq7BvGAOtNdpstWEHIaU+nXIbPwfPI+qIboB9PqTOqoYAxoespYc7IIfYcxvSGCW/C82TNqsoeod1vYGDCJpvOYYDNYcXNELnO+osALIjPMh3F4fgH4fYEEZHMLN10+nfJKk+m1roCoadpuSsb4bkfmGManKsZA3MeEcscdrIxvxsdkdocdKP3gaUHoHUKQeUs13nFQbgs30AzfM8beAgLwZdP8esrPMLNa0nMUdtPXJvC7MoBcesZIdSfYH0cMfmDQRQNawfJTmgIMA3yALsfOMwKlVg3LnwPRAApQyXSMCbgoO1r+QYOvWoy/zfHoyaf/0HmQpHjHkm1qnqh0K4x4z4wE2PuE0bGsYcOQAdEclrt/xvz+SZvd2bA3mGeQCk1OWWkcnk28EU2iywAK2ykvq0yz10303z0oESKeMG1UILD2cc0xhczcyOwkaasiuJFroaxU10QixCbrpi2YoSxoG4eXVkQIyYCyxWpQl61mySBVqUkjPjGjI+Sa0ej+3wHnE6z2q6mxyFEZzS0q3nD+00fOe9uhYUguwmzcFRoNF92pmcmDAKz2ZAwcP1rcCiQ3gZaaOjBZaqs9W0XHA22Pu2wCF22VeVAO1kOOxtr9HnJxP2PnH/VojRHFHIDoHbyMDO1FdEewjUpqooeKT7KVHBtMirwNHAux1Fe+STNuQrD3DXBtdElOZPKwFb0tqBqUHOdD1LCjAprmdB1JN9JnmTQHECGmoUrXE0Rdzah1YrEck83kEbClZLBazxcxnlDXGxFOT6R3ADUEThGcHZlFbeK+FsuvIHmQBtPvgpzJLJaSEAEwCOnKwMNIEqQkE1jPgMEVgtCMEchHCaNcEy8yE8duZsEVrWd47HseIDAB4BKLAY7O12GIgUge+B6WgRqKl+UOS33bCaeoRZMDxG+B6fq5MOqgHLGqwegYYi4AsEYJyxpdgF+RcMULXWaWk0PTBaNXUInFBRhV1+YetiZwOdIVS4kpAe2w8ZMOHVEWqd6Z8eFdUWsIBxqEUKURhCkwUKk45qFPgdIkNgmNYaQDvLvc9TmDlDDfvMBofB9EfKBiWGBhuKAYJtEM5mjMZ+MfZsAjAN/bUm1G4RpnxqTiAzQktiCVwamV4NQ59ePZJpSGRcoPXCKu/fKZwR/EgYbU0V/c0oJhBpqUa8auHDMNEokCwVM85d5ekS4LU7/STxi6T3TwJC5l5cx+54zh/Xw5/SzlRl0phr3fcwl6I45sL0zlAEh45lxz6Hsz6NwW81ED0igZL4MWIiMEByLELlyDL4t/LjRj9xAQr/0OIgHLoNL+5yr9LWACM+SCMt8cbbwer4rtryrTrlgbrujIl3Xfrxr0rkJ8rsgDLkM/MmxA8kEybkr0MwN+o6LpSXLmgZbpXKwfrry47Gp4ub9dOepmJjfBusD/fL8tp+DP87Vbp4CwkR+pFezH5UhQ+Zw27F1QuxqGFeQTRT1DgpULcMlScSKWcXpTBc27FHwDUl2zMHqlu6cTGLkJgiHhO/14kKKLMGHlaN7B+ECfF7KEib2FYRsdKtMYB4egBL4LGqYWdVLKqKRRhEg+ceXNWI9dyt70rdcVoP5YY/WdQe/ZwcTKnYkJepZviuSNZx4O0RRMDtBCCXDYWEMCsGCiF8TpNJD+eD/QvICtDHG1TRSDZYArQPXjQHzvXjZLyRsM32VS3xTvz63nXn/W3z5A1QpeIaAckT6eINkf6L6Y/Z6Gqf6a0A+b8BtfyTqf0NUPVegLN2sOZ7KabPuKGhsPJON5ucxZjgB1j6qYBnmcUi9bj7WXj59CfWBxUxWJdXniUMjyJmjkB0v+scv6U0ffj6vznoCLBOwTHBAMSGUcXrA6Vc7jfMJ00DQXUeAO7nA9px7rp4gnpxuZHHgdirqT7n3ftJSKfzQWf9PWKWz4B2T4oLyNhH+Iyb5L4P2L778QFCCaX67vURBFC5tRqM/ncUdaPsD+3jRZsPun34L1HoGLREvM04xY92AsWdAKJBI6HVvm2URZGoWoAMpU+4THMNmgfg6hzoEaDUPIH37GI1Q8rApDwQgKHI5q/aFopkAbT45XG7SeioHnARNhsWdAjxhgggIClvywpHlMX0463pJS96Cvqbi77j55SNfVPGPzO4wFnyGkczrEX8hmUNAekTgGP3u6/kZg/5JGs9zQwgUIIT+bSGZX7ip9YKz5Q3sxFdRaUI23qWAElXX6IFZoXSQtDMD4QPABEYaffkIX35gBaACzbigwPCLeC1oXvWtHTQbSuCr4ikNAI+G8CUp2EG0MAIMgEBeQnUV4V0D6EuAUJlCsNfTMn1+LZ1sW1NEqOYgSyPQFo2IbKNQi4EscwGfA0BgIL5Q8cRBfHMQdLAkEfpJUtTU4MCESExMlA5UX3OqE+hlhXk/6GfrggwACB5+8IXAiiE0FPcV+L3AwAakwzfg0QnWd8PgEswoB/2+GA0ABgkglCNmgaWQPeATABRU0BWainLzYBMRSS2UL6uVHtyjRjwAgKgJsBgFCQVw1MdYaIC7DIZxIkkNULAig71ImhMKUyHCleHvDYEy2BBPdQkgYAuQ0uYMJIRjCYIKsR4OvK8IUbth/iWuWQrgB+COZTmE2ZBmeGDB2JX2XYJhuw0aR142o7SWHFUAIzXDd+HJaTNhgEqxRQIqwBARDkEjzB7QgoC4qSgIQlh78XJE3uq3xjOgHADsVXmkCcCNh3oJIUnN5UPCmV6Ajw7gM8JIDCV6RfcAUEKBFCecKwZAYKi/AIR94aAUiHsF9VtEzBDRF6Lnn0h+EyZYBcMFTipmPrkQwgEQAPOPDthLQGGSFRqK1XrSStWeeeWNsjAg4SgrA8wJUCPCyDHUThpo4tAz11DP5tKQyLsLNEKiNt9wYbMHMdTew3DOEsor4PKLdQQQmA7omYNdhyLhiQwO4cUDVFjCrM2Aj+EkqzSwCElFw4yGGIMN77qU1hMmGAfFgfY7g3UwY4SPUmmoPcHIBmagGkBYhbEGEx4XPH8kFgXC/kzJZsKKihQOAGwwY35vqECAqA3AGABRvdg1rroXgoECBDsKvhVCxiHSGgP8W2HWDLc8KBhjhH0pOBBkqkcCGBBLEW4DQ2TCaqIS4T3ZaxxIEYWuJ7CK5+o1GJ1NJjcrm0iRjmXMahOeLGYyE1FeULWH3Tb1PAPhV0bmNTLOt8oT8C1p3gL61D2OfeeoRKUaHCDdY0DHvm+igD/QwMrYfyIhEhrNY1O0Yv5FSLzHFJaR6jZ0XSBvCEMFEsABCHgB+IHlHxKyMEIhLGHcA9QYIQVgJU+jMkKArhFZNxCs6PMIIGwr8dWEUg9CN8/Q7gCOOGH2QkJY1WQJMKTiPlzuMcTILwmTC85IMWHdGkbGmE/k5hiGbQYsN0GvcjeTgCwaiEf4WEfJFAPyfgACkw5Uyr6LuLfx7ikJNQ/qY6jnTPBwdgEIPP5Bui1zLpFIyUjCKVOsx0h2kMNb3P1gI4kCyxM8fNPHwLxoQTGiAC/vanYTskjkTAw0GxBvgPMqBWCSrKQJQIjpcEnyYJjNn6yM5WeLAbHsIhqnqFxpOcCNBtA0CgodohoagLAEQArIN8paDQPGgsBeRzEnhbMGAkOjcheQWlOSoTX5EyR6Ao0ueLhTLSGI5mxo4UEgA3hURt23BHCgdOnCKTrocfeHkKJKB4B8+LKQvqKQ46sSy+gg4fM0Kr7iCxUH6KQXUw3zuQJAtU/8dYDUEL8lxEU5DFFNILLDhERMkmf+1NS78nUA9GcffHZIFTUI6/KAQzWh7BwSQCIP3vEFJyfQ4QXIaAAiFehsgo+cWT/tAEqhp5CoRU+JHS3dYUFvugcRcLXTshKAwMgoekOKByLDxUKJXCNiDns44RTqDEVogWCPDWj06RmLOuDFpAWQdZOUkhPplmqj0Vsn+Z8AiG4yk44QQfb6AAE1DQ/0IQv7MDnBz/on0Q0O9GegAApf6BLM+j+Yo5Ac/6EHJDkJyEQ0uOENADjkAxQ56cmqJnJjm5z3o+cwucXP+ihyK52c2OZ9C5DWgEQgMaAJ9H+jH5EQkfSkdHKbkhzW57c4IJ3Izn9ys5OcuOXCF97+896CIRuVPO+h/RAYAfauYvObkzy/e8cxOdLn8zB9PoAADSiATIAAnC3OD6dy96fcmjJPObnDyO5R8k+efK5CXy15N8hdgPKXnxBgg+c4PiHJqjPQe5CIIGImntoqzqoMOXSo5GRb2y0StMZksggbTuyPQuU/TM2LKFhRQYPgECMxGZ7UsG0ePGKAegvyPpywQEe+CQFkDDhmCrNZHCUNbqhQSgIkvtFYGqGMTeBzE1vgPnRnsTO+LQuUm0ME4OY4WvMzYvzJYlt8MAYaQWcLNFnizJZ0slZArOfwydCZiYRmQ0kPxCdbOYil2QZRL48L6wtMHWZHhFQGyACX/OVAzOoikzcGcZF0l/ObnQBw5/0DSWCC25f475Vc5OanLHk1R3FnipxVXJrlFzPoJcsuYEocW3zK5ccvOQXLCUlyolmZYJXHIfmjyu5wC60MkvjKpKW5bcx+f5hyWOLvF082eWvIRDFKYlg8uOT9ABjBAKlVSrxbErFnlKE5z0PeeHy+jHyz5F896FfP3pNK8l6SzuT0pflvzr5Qy0pQH1/nS5/5ccwBcAuCBRKvJMguThvgS4AgWmqqCmRoKplEFdUSwg1P8ycxtpXM5qQ2JR23Fbg0cifdlrDA7BYozF+s0SPcuhoC19YScl6MEGMFbgcB1YRsLQHcmDkTwLU50LugbRvxnl4gCxUnVzrVwCseiohQTyrpwsdmenOvImBCHrosgTnYkLNWfpgJUA4ElLAYlToJhzo/6X4UsBKF1xLmmdb0S8k3H0LOwgiA1AvASjugU0xUlqdYMSk1hc6M8RSFwTXq2lz2OyZmZDhQBlABqPgQfqFGYSQ95gMAj+glMeiyqhqgsNGi8vmJ6yYVHUmSAtUHRnUJIXg/ya0EMkfJKRjnPLrNSEJONCySbadEdhJKGsZcgoZ/C0gqTSrtw7JNWMXgLGnZpQ+q+ANcvJL0gqAh4TUqziz6yBaahHacMBIBWa0KMTRT/NLg9UkAvV1haNPBnHzO4iOdIVEamIbQDi+AtwvCCrA+K/8CwikPVmzAcbcB/+KeD9M2t1BJ0cON7T1N4CIFlQtQLVDTr6GqwQQjWxaKgIgBLBXE8xQUftEmNkDX8C09snwi+1Miuzh+4A6hL0Igm6zzFM8VFX2MFGXKtx+mGxFkLLUxsdwFNdhUjKYnyV+BbEiBpXy4k4y30c6DnvSD77qwLMd6tGe3wxlNDOJ3fF9XAl9kuAxJRa9/EjEwSLgKYggRhMipzCq53+JAMAHlBngIaLC9a0xoeC8ix8hI3C/wqiUGzIBZ+IasNUe31hOo0CZmJCoPX6ydDTu+QAwIMBYEtU8AEwPZTEjmALBlgnTbQdsC6BUAeg+wfoEcGY2FBZg9sS1eUVXryESGwkeEkxpY0TJaAAgeUGzRiBRAT5vuNmmgAABsUQDiM4AmS8QOIDAWgBMg4gAAODiDEFPl6aBAsQQzTEA4gcQBAXNAYBJs5oTI9NDAKzc4FoCnyBAp82gFEDQAhbuIHENANxDQDOAGA3ECZEUQmQRAAtJAbiAIF4hoAstjkTzRAEgBWbaAMQCZNxFoBs01N3EBgKfJU28QCtLmtmm5pC2OarNKgE+VlpIBs0iqRRU+YcEMAsaGAvEEgKfKKJWa9N3EKzdxD00FaTNDAIoioFPls09NbNKzbxAEDJa2aUQSbdxBC0Ga9NPW8TXlr00TIVADANmrxG4gxA/NzgDiLEFi3OAogtAZwNxF9wxBYtJACZP1qKo3VTNpWyWLlqgC0AJtMQKzVEAK0CB0tumvTbxDZrcR6tMYMbaoBB0XaogvEZwAIEm28RVNJ2vbSxr00qAYwJAKIM4EW3cRzty2yzTFuu31a9Nm2lHSdqq1qaAtemqrXtv21QBsU6gaTeI1IqcMFo+gIAA=== -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Learnathon-By-Geeky-Solutions/h3cker&utm_content=57):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-19T10:27:21Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=Learnathon-By-Geeky-Solutions_h3cker&pullRequest=57) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=Learnathon-By-Geeky-Solutions_h3cker&pullRequest=57&metric=new_coverage&view=list) (required ≥ 80%)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=Learnathon-By-Geeky-Solutions_h3cker&pullRequest=57)


author_association: NONE