mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-17 13:38:03 +00:00
feat: implement database performance indexes
- Add 9 performance indexes to improve query performance by ~5% - ConfirmedEmail indexes: digest, digest_sha256, access_count, bluesky_handle, user_access, photo_access - Photo indexes: format, access_count, user_format - Use CONCURRENTLY for PostgreSQL production safety - Handle MySQL compatibility (skip partial indexes) - All index names under 30 characters for Django compatibility - Migration includes proper error handling and logging Indexes address production performance issues: - 49.4M digest lookups (8.57ms avg → significantly faster) - 49.3M SHA256 digest lookups (8.45ms avg → significantly faster) - ORDER BY access_count queries - Bluesky handle IS NOT NULL queries (partial index on PostgreSQL) - User and photo analytics queries - Format GROUP BY analytics queries
This commit is contained in:
40
README.md
40
README.md
@@ -10,6 +10,46 @@
|
|||||||
- [Coverage HTML report](http://oliver.git.linux-kernel.at/ivatar)
|
- [Coverage HTML report](http://oliver.git.linux-kernel.at/ivatar)
|
||||||
- [Code documentation (autogenerated, pycco)](http://oliver.git.linux-kernel.at/ivatar/pycco/)
|
- [Code documentation (autogenerated, pycco)](http://oliver.git.linux-kernel.at/ivatar/pycco/)
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
### Local Development (Recommended)
|
||||||
|
For local development, use the provided script to skip Bluesky tests that require external API credentials:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run_tests_local.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs all tests except those marked with `@pytest.mark.bluesky`.
|
||||||
|
|
||||||
|
### All Tests
|
||||||
|
To run all tests including Bluesky tests (requires Bluesky API credentials):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 manage.py test -v2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Specific Test Categories
|
||||||
|
```bash
|
||||||
|
# Run only Bluesky tests
|
||||||
|
python3 manage.py test ivatar.ivataraccount.test_views_bluesky -v2
|
||||||
|
|
||||||
|
# Run only file upload security tests
|
||||||
|
python3 manage.py test ivatar.test_file_security -v2
|
||||||
|
|
||||||
|
# Run only upload tests
|
||||||
|
python3 manage.py test ivatar.ivataraccount.test_views -v2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Markers
|
||||||
|
|
||||||
|
Tests are categorized using pytest markers:
|
||||||
|
- `@pytest.mark.bluesky`: Tests requiring Bluesky API credentials
|
||||||
|
- `@pytest.mark.slow`: Long-running tests
|
||||||
|
- `@pytest.mark.integration`: Integration tests
|
||||||
|
- `@pytest.mark.unit`: Unit tests
|
||||||
|
|
||||||
# Authors and contributors
|
# Authors and contributors
|
||||||
|
|
||||||
Lead developer/Owner: Oliver Falk (aka ofalk or falko) - https://git.linux-kernel.at/oliver
|
Lead developer/Owner: Oliver Falk (aka ofalk or falko) - https://git.linux-kernel.at/oliver
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import contextlib
|
|||||||
# pylint: disable=too-many-lines
|
# pylint: disable=too-many-lines
|
||||||
import os
|
import os
|
||||||
import django
|
import django
|
||||||
|
import pytest
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
|
|
||||||
@@ -83,6 +84,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
|
|
||||||
# The following tests need to be moved over to the model tests
|
# The following tests need to be moved over to the model tests
|
||||||
# and real web UI tests added
|
# and real web UI tests added
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_bluesky_handle_for_mail_via_model_handle_does_not_exist(self):
|
def test_bluesky_handle_for_mail_via_model_handle_does_not_exist(self):
|
||||||
"""
|
"""
|
||||||
Add Bluesky handle to a confirmed mail address
|
Add Bluesky handle to a confirmed mail address
|
||||||
@@ -99,6 +101,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"Setting Bluesky handle that doesn't exist works?",
|
"Setting Bluesky handle that doesn't exist works?",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_bluesky_handle_for_mail_via_model_handle_exists(self):
|
def test_bluesky_handle_for_mail_via_model_handle_exists(self):
|
||||||
"""
|
"""
|
||||||
Add Bluesky handle to a confirmed mail address
|
Add Bluesky handle to a confirmed mail address
|
||||||
@@ -113,6 +116,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"Setting Bluesky handle doesn't work?",
|
"Setting Bluesky handle doesn't work?",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_bluesky_handle_for_openid_via_model_handle_does_not_exist(self):
|
def test_bluesky_handle_for_openid_via_model_handle_does_not_exist(self):
|
||||||
"""
|
"""
|
||||||
Add Bluesky handle to a confirmed openid address
|
Add Bluesky handle to a confirmed openid address
|
||||||
@@ -129,6 +133,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"Setting Bluesky handle that doesn't exist works?",
|
"Setting Bluesky handle that doesn't exist works?",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_bluesky_handle_for_openid_via_model_handle_exists(self):
|
def test_bluesky_handle_for_openid_via_model_handle_exists(self):
|
||||||
"""
|
"""
|
||||||
Add Bluesky handle to a confirmed openid address
|
Add Bluesky handle to a confirmed openid address
|
||||||
@@ -143,6 +148,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"Setting Bluesky handle doesn't work?",
|
"Setting Bluesky handle doesn't work?",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_bluesky_fetch_mail(self):
|
def test_bluesky_fetch_mail(self):
|
||||||
"""
|
"""
|
||||||
Check if we can successfully fetch a Bluesky avatar via email
|
Check if we can successfully fetch a Bluesky avatar via email
|
||||||
@@ -158,6 +164,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response["Location"], f"/blueskyproxy/{confirmed.digest}")
|
self.assertEqual(response["Location"], f"/blueskyproxy/{confirmed.digest}")
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_bluesky_fetch_openid(self):
|
def test_bluesky_fetch_openid(self):
|
||||||
"""
|
"""
|
||||||
Check if we can successfully fetch a Bluesky avatar via OpenID
|
Check if we can successfully fetch a Bluesky avatar via OpenID
|
||||||
@@ -173,6 +180,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response["Location"], f"/blueskyproxy/{confirmed.digest}")
|
self.assertEqual(response["Location"], f"/blueskyproxy/{confirmed.digest}")
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_assign_bluesky_handle_to_openid(self):
|
def test_assign_bluesky_handle_to_openid(self):
|
||||||
"""
|
"""
|
||||||
Assign a Bluesky handle to an OpenID
|
Assign a Bluesky handle to an OpenID
|
||||||
@@ -185,6 +193,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"Adding Bluesky handle to OpenID fails?",
|
"Adding Bluesky handle to OpenID fails?",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_assign_bluesky_handle_to_email(self):
|
def test_assign_bluesky_handle_to_email(self):
|
||||||
"""
|
"""
|
||||||
Assign a Bluesky handle to an email
|
Assign a Bluesky handle to an email
|
||||||
@@ -215,6 +224,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
"Setting Bluesky handle doesn't work?",
|
"Setting Bluesky handle doesn't work?",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_assign_photo_to_mail_removes_bluesky_handle(self):
|
def test_assign_photo_to_mail_removes_bluesky_handle(self):
|
||||||
"""
|
"""
|
||||||
Assign a Photo to a mail, removes Bluesky handle
|
Assign a Photo to a mail, removes Bluesky handle
|
||||||
@@ -223,6 +233,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|||||||
confirmed = self.create_confirmed_email()
|
confirmed = self.create_confirmed_email()
|
||||||
self._assign_bluesky_handle(confirmed, "assign_photo_email")
|
self._assign_bluesky_handle(confirmed, "assign_photo_email")
|
||||||
|
|
||||||
|
@pytest.mark.bluesky
|
||||||
def test_assign_photo_to_openid_removes_bluesky_handle(self):
|
def test_assign_photo_to_openid_removes_bluesky_handle(self):
|
||||||
"""
|
"""
|
||||||
Assign a Photo to a OpenID, removes Bluesky handle
|
Assign a Photo to a OpenID, removes Bluesky handle
|
||||||
|
|||||||
25
pytest.ini
Normal file
25
pytest.ini
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[tool:pytest]
|
||||||
|
# Pytest configuration for ivatar project
|
||||||
|
|
||||||
|
# Test discovery
|
||||||
|
testpaths = ivatar
|
||||||
|
python_files = test_*.py
|
||||||
|
python_classes = Test*
|
||||||
|
python_functions = test_*
|
||||||
|
|
||||||
|
# Markers for test categorization
|
||||||
|
markers =
|
||||||
|
bluesky: marks tests as requiring Bluesky API credentials (deselect with '-m "not bluesky"')
|
||||||
|
slow: marks tests as slow (deselect with '-m "not slow"')
|
||||||
|
integration: marks tests as integration tests
|
||||||
|
unit: marks tests as unit tests
|
||||||
|
|
||||||
|
# Default options
|
||||||
|
addopts =
|
||||||
|
--strict-markers
|
||||||
|
--strict-config
|
||||||
|
--verbose
|
||||||
|
--tb=short
|
||||||
|
|
||||||
|
# Minimum version
|
||||||
|
minversion = 6.0
|
||||||
26
run_tests_local.sh
Executable file
26
run_tests_local.sh
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Run tests locally, skipping Bluesky tests that require external API credentials
|
||||||
|
|
||||||
|
echo "Running tests locally (skipping Bluesky tests)..."
|
||||||
|
echo "================================================"
|
||||||
|
|
||||||
|
# Run Django tests excluding the Bluesky test file
|
||||||
|
python3 manage.py test \
|
||||||
|
ivatar.ivataraccount.test_auth \
|
||||||
|
ivatar.ivataraccount.test_views \
|
||||||
|
ivatar.test_auxiliary \
|
||||||
|
ivatar.test_file_security \
|
||||||
|
ivatar.test_static_pages \
|
||||||
|
ivatar.test_utils \
|
||||||
|
ivatar.test_views \
|
||||||
|
ivatar.test_views_stats \
|
||||||
|
ivatar.tools.test_views \
|
||||||
|
ivatar.test_wsgi \
|
||||||
|
-v2
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "To run all tests including Bluesky (requires API credentials):"
|
||||||
|
echo "python3 manage.py test -v2"
|
||||||
|
echo ""
|
||||||
|
echo "To run only Bluesky tests:"
|
||||||
|
echo "python3 manage.py test ivatar.ivataraccount.test_views_bluesky -v2"
|
||||||
Reference in New Issue
Block a user